зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to fx-team a=merge
This commit is contained in:
Коммит
160fc3eb2e
|
@ -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="bc3bbf42d2a606f6b7038881cff5ec3795fdf953"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="90bb898e8401f5fb98edcf38293073c5c26ab7bd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
|
|
@ -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="bc3bbf42d2a606f6b7038881cff5ec3795fdf953"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="90bb898e8401f5fb98edcf38293073c5c26ab7bd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="bc3bbf42d2a606f6b7038881cff5ec3795fdf953"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="90bb898e8401f5fb98edcf38293073c5c26ab7bd"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||
<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="bc3bbf42d2a606f6b7038881cff5ec3795fdf953"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="90bb898e8401f5fb98edcf38293073c5c26ab7bd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
|
|
@ -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="bc3bbf42d2a606f6b7038881cff5ec3795fdf953"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="90bb898e8401f5fb98edcf38293073c5c26ab7bd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "2c2f2fa0a101f07cbb206cca2e69ef3a7b18244c",
|
||||
"revision": "944559e6e60f9c97bc3b9daf00d73549e0eb80a6",
|
||||
"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="bc3bbf42d2a606f6b7038881cff5ec3795fdf953"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="90bb898e8401f5fb98edcf38293073c5c26ab7bd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -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="bc3bbf42d2a606f6b7038881cff5ec3795fdf953"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="90bb898e8401f5fb98edcf38293073c5c26ab7bd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||
<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="bc3bbf42d2a606f6b7038881cff5ec3795fdf953"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="90bb898e8401f5fb98edcf38293073c5c26ab7bd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
|
||||
|
|
|
@ -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="bc3bbf42d2a606f6b7038881cff5ec3795fdf953"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="90bb898e8401f5fb98edcf38293073c5c26ab7bd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -24,15 +24,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame", "resource:///modules/Panel
|
|||
iframe.addEventListener("DOMContentLoaded", function documentDOMLoaded() {
|
||||
iframe.removeEventListener("DOMContentLoaded", documentDOMLoaded, true);
|
||||
injectLoopAPI(iframe.contentWindow);
|
||||
|
||||
// We use loopPanelInitialized so that we know we've finished localising before
|
||||
// sizing the panel.
|
||||
iframe.contentWindow.addEventListener("loopPanelInitialized",
|
||||
function documentLoaded() {
|
||||
iframe.contentWindow.removeEventListener("loopPanelInitialized",
|
||||
documentLoaded, true);
|
||||
}, true);
|
||||
|
||||
}, true);
|
||||
};
|
||||
|
||||
|
|
|
@ -212,7 +212,6 @@
|
|||
let panel = this.panel;
|
||||
let frameId = this.getAttribute("notificationFrameId");
|
||||
|
||||
let wasAlive = SharedFrame.isGroupAlive(frameId);
|
||||
SharedFrame.setOwner(frameId, this.content);
|
||||
|
||||
// Clear dimensions on all browsers so the panel size will
|
||||
|
|
|
@ -29,7 +29,10 @@ function handleRequest(request, response) {
|
|||
cachedCount++;
|
||||
}
|
||||
|
||||
Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer).initWithCallback(() => {
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback(() => {
|
||||
// to avoid garbage collection
|
||||
timer = null;
|
||||
switch (format) {
|
||||
case "txt": {
|
||||
response.setStatusLine(request.httpVersion, status, "DA DA DA");
|
||||
|
|
|
@ -9,7 +9,10 @@ function handleRequest(request, response) {
|
|||
let params = request.queryString.split("&");
|
||||
let index = params.filter((s) => s.contains("index="))[0].split("=")[1];
|
||||
|
||||
Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer).initWithCallback(() => {
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback(() => {
|
||||
// to avoid garbage collection
|
||||
timer = null;
|
||||
response.setStatusLine(request.httpVersion, index == 1 ? 101 : index * 100, "Meh");
|
||||
|
||||
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
|
|
|
@ -9,7 +9,10 @@ function handleRequest(request, response) {
|
|||
let params = request.queryString.split("&");
|
||||
let status = params.filter((s) => s.contains("sts="))[0].split("=")[1];
|
||||
|
||||
Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer).initWithCallback(() => {
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback(() => {
|
||||
// to avoid garbage collection
|
||||
timer = null;
|
||||
switch (status) {
|
||||
case "100":
|
||||
response.setStatusLine(request.httpVersion, 101, "Switching Protocols");
|
||||
|
|
|
@ -135,7 +135,6 @@ let PanelFrame = {
|
|||
let notificationFrameId = aToolbarButton.getAttribute("notificationFrameId");
|
||||
let notificationFrame = aWindow.document.getElementById(notificationFrameId);
|
||||
|
||||
let wasAlive = SharedFrame.isGroupAlive(notificationFrameId);
|
||||
SharedFrame.setOwner(notificationFrameId, notificationFrame);
|
||||
|
||||
// Clear dimensions on all browsers so the panel size will
|
||||
|
@ -184,7 +183,7 @@ let PanelFrame = {
|
|||
if (!inMenuPanel)
|
||||
anchorBtn.setAttribute("open", "true");
|
||||
if (notificationFrame.contentDocument &&
|
||||
notificationFrame.contentDocument.readyState == "complete" && wasAlive) {
|
||||
notificationFrame.contentDocument.readyState == "complete") {
|
||||
initFrameShow();
|
||||
} else {
|
||||
// first time load, wait for load and dispatch after load
|
||||
|
|
|
@ -2474,17 +2474,14 @@ nsDocShell::GetFullscreenAllowed(bool* aFullscreenAllowed)
|
|||
// If we have no parent then we're the root docshell; no ancestor of the
|
||||
// original docshell doesn't have a allowfullscreen attribute, so
|
||||
// report fullscreen as allowed.
|
||||
nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
|
||||
GetParent(getter_AddRefs(parentTreeItem));
|
||||
if (!parentTreeItem) {
|
||||
nsRefPtr<nsDocShell> parent = GetParentDocshell();
|
||||
if (!parent) {
|
||||
*aFullscreenAllowed = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Otherwise, we have a parent, continue the checking for
|
||||
// mozFullscreenAllowed in the parent docshell's ancestors.
|
||||
nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentTreeItem);
|
||||
NS_ENSURE_TRUE(parent, NS_OK);
|
||||
|
||||
return parent->GetFullscreenAllowed(aFullscreenAllowed);
|
||||
}
|
||||
|
||||
|
@ -3132,16 +3129,15 @@ NS_IMETHODIMP
|
|||
nsDocShell::GetRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aRootTreeItem);
|
||||
*aRootTreeItem = static_cast<nsIDocShellTreeItem *>(this);
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> parent;
|
||||
NS_ENSURE_SUCCESS(GetParent(getter_AddRefs(parent)), NS_ERROR_FAILURE);
|
||||
nsRefPtr<nsDocShell> root = this;
|
||||
nsRefPtr<nsDocShell> parent = root->GetParentDocshell();
|
||||
while (parent) {
|
||||
*aRootTreeItem = parent;
|
||||
NS_ENSURE_SUCCESS((*aRootTreeItem)->GetParent(getter_AddRefs(parent)),
|
||||
NS_ERROR_FAILURE);
|
||||
root = parent;
|
||||
parent = root->GetParentDocshell();
|
||||
}
|
||||
NS_ADDREF(*aRootTreeItem);
|
||||
|
||||
root.forget(aRootTreeItem);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -5556,15 +5552,12 @@ nsDocShell::GetVisibility(bool * aVisibility)
|
|||
// for a hidden view, unless we're an off screen browser, which
|
||||
// would make this test meaningless.
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
|
||||
nsCOMPtr<nsIDocShellTreeItem> parentItem;
|
||||
treeItem->GetParent(getter_AddRefs(parentItem));
|
||||
nsRefPtr<nsDocShell> docShell = this;
|
||||
nsRefPtr<nsDocShell> parentItem = docShell->GetParentDocshell();
|
||||
while (parentItem) {
|
||||
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(treeItem));
|
||||
presShell = docShell->GetPresShell();
|
||||
|
||||
nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parentItem);
|
||||
nsCOMPtr<nsIPresShell> pPresShell = parentDS->GetPresShell();
|
||||
nsCOMPtr<nsIPresShell> pPresShell = parentItem->GetPresShell();
|
||||
|
||||
// Null-check for crash in bug 267804
|
||||
if (!pPresShell) {
|
||||
|
@ -5593,8 +5586,8 @@ nsDocShell::GetVisibility(bool * aVisibility)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
treeItem = parentItem;
|
||||
treeItem->GetParent(getter_AddRefs(parentItem));
|
||||
docShell = parentItem;
|
||||
parentItem = docShell->GetParentDocshell();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
|
||||
|
@ -8019,8 +8012,7 @@ nsDocShell::RestoreFromHistory()
|
|||
nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
|
||||
uint32_t parentSuspendCount = 0;
|
||||
if (document) {
|
||||
nsCOMPtr<nsIDocShellTreeItem> parent;
|
||||
GetParent(getter_AddRefs(parent));
|
||||
nsRefPtr<nsDocShell> parent = GetParentDocshell();
|
||||
if (parent) {
|
||||
nsCOMPtr<nsIDocument> d = parent->GetDocument();
|
||||
if (d) {
|
||||
|
@ -9225,8 +9217,7 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
|||
|
||||
// If this docshell is owned by a frameloader, make sure to cancel
|
||||
// possible frameloader initialization before loading a new page.
|
||||
nsCOMPtr<nsIDocShellTreeItem> parent;
|
||||
GetParent(getter_AddRefs(parent));
|
||||
nsCOMPtr<nsIDocShellTreeItem> parent = GetParentDocshell();
|
||||
if (parent) {
|
||||
nsCOMPtr<nsIDocument> doc = do_GetInterface(parent);
|
||||
if (doc) {
|
||||
|
@ -12302,7 +12293,7 @@ nsDocShell::GetNestedFrameId(uint64_t* aId)
|
|||
NS_IMETHODIMP
|
||||
nsDocShell::IsAppOfType(uint32_t aAppType, bool *aIsOfType)
|
||||
{
|
||||
nsCOMPtr<nsIDocShell> shell = this;
|
||||
nsRefPtr<nsDocShell> shell = this;
|
||||
while (shell) {
|
||||
uint32_t type;
|
||||
shell->GetAppType(&type);
|
||||
|
@ -12310,10 +12301,7 @@ nsDocShell::IsAppOfType(uint32_t aAppType, bool *aIsOfType)
|
|||
*aIsOfType = true;
|
||||
return NS_OK;
|
||||
}
|
||||
nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(shell);
|
||||
nsCOMPtr<nsIDocShellTreeItem> parent;
|
||||
item->GetParent(getter_AddRefs(parent));
|
||||
shell = do_QueryInterface(parent);
|
||||
shell = shell->GetParentDocshell();
|
||||
}
|
||||
|
||||
*aIsOfType = false;
|
||||
|
@ -13076,19 +13064,14 @@ nsDocShell::GetAsyncPanZoomEnabled(bool* aOut)
|
|||
bool
|
||||
nsDocShell::HasUnloadedParent()
|
||||
{
|
||||
nsCOMPtr<nsIDocShellTreeItem> currentTreeItem = this;
|
||||
while (currentTreeItem) {
|
||||
nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
|
||||
currentTreeItem->GetParent(getter_AddRefs(parentTreeItem));
|
||||
nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentTreeItem);
|
||||
if (parent) {
|
||||
bool inUnload = false;
|
||||
parent->GetIsInUnload(&inUnload);
|
||||
if (inUnload) {
|
||||
return true;
|
||||
}
|
||||
nsRefPtr<nsDocShell> parent = GetParentDocshell();
|
||||
while (parent) {
|
||||
bool inUnload = false;
|
||||
parent->GetIsInUnload(&inUnload);
|
||||
if (inUnload) {
|
||||
return true;
|
||||
}
|
||||
currentTreeItem.swap(parentTreeItem);
|
||||
parent = parent->GetParentDocshell();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "nsWeakReference.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsMimeTypeArray.h"
|
||||
#include "nsPluginTags.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ public:
|
|||
virtual nsresult Get(uint32_t aKey, int32_t& aValue) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Set(uint32_t aKey, int64_t aValue) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Get(uint32_t aKey, int64_t& aValue) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Set(uint32_t aKey, bool aValue) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Get(uint32_t aKey, bool& aValue) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Set(uint32_t aKey, const Size& aValue) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Get(uint32_t aKey, Size& aValue) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult Set(uint32_t aKey, const nsTArray<Region>& aRegions) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
|
|
|
@ -474,6 +474,19 @@ nsGonkCameraControl::Get(uint32_t aKey, int64_t& aRet)
|
|||
return mParams.Get(aKey, aRet);
|
||||
}
|
||||
|
||||
// Boolean parameter accessors.
|
||||
nsresult
|
||||
nsGonkCameraControl::Set(uint32_t aKey, bool aValue)
|
||||
{
|
||||
return SetAndPush(aKey, aValue);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::Get(uint32_t aKey, bool& aRet)
|
||||
{
|
||||
return mParams.Get(aKey, aRet);
|
||||
}
|
||||
|
||||
// Weighted-region parameter accessors.
|
||||
nsresult
|
||||
nsGonkCameraControl::Set(uint32_t aKey, const nsTArray<Region>& aRegions)
|
||||
|
|
|
@ -65,6 +65,8 @@ public:
|
|||
virtual nsresult Get(uint32_t aKey, int32_t& aValue) MOZ_OVERRIDE;
|
||||
virtual nsresult Set(uint32_t aKey, int64_t aValue) MOZ_OVERRIDE;
|
||||
virtual nsresult Get(uint32_t aKey, int64_t& aValue) MOZ_OVERRIDE;
|
||||
virtual nsresult Set(uint32_t aKey, bool aValue) MOZ_OVERRIDE;
|
||||
virtual nsresult Get(uint32_t aKey, bool& aValue) MOZ_OVERRIDE;
|
||||
virtual nsresult Set(uint32_t aKey, const Size& aValue) MOZ_OVERRIDE;
|
||||
virtual nsresult Get(uint32_t aKey, Size& aValue) MOZ_OVERRIDE;
|
||||
virtual nsresult Set(uint32_t aKey, const nsTArray<Region>& aRegions) MOZ_OVERRIDE;
|
||||
|
|
|
@ -206,6 +206,8 @@ public:
|
|||
virtual nsresult Get(uint32_t aKey, int32_t& aValue) = 0;
|
||||
virtual nsresult Set(uint32_t aKey, int64_t aValue) = 0;
|
||||
virtual nsresult Get(uint32_t aKey, int64_t& aValue) = 0;
|
||||
virtual nsresult Set(uint32_t aKey, bool aValue) = 0;
|
||||
virtual nsresult Get(uint32_t aKey, bool& aValue) = 0;
|
||||
virtual nsresult Set(uint32_t aKey, const Size& aValue) = 0;
|
||||
virtual nsresult Get(uint32_t aKey, Size& aValue) = 0;
|
||||
virtual nsresult Set(uint32_t aKey, const nsTArray<Region>& aRegions) = 0;
|
||||
|
|
|
@ -66,6 +66,7 @@ struct FontListEntry {
|
|||
int16_t stretch;
|
||||
uint8_t italic;
|
||||
uint8_t index;
|
||||
bool isHidden;
|
||||
};
|
||||
|
||||
struct DeviceStorageFreeSpaceParams
|
||||
|
|
|
@ -16,6 +16,7 @@ qemu = true
|
|||
[test_mobile_data_state.js]
|
||||
[test_mobile_mmi.js]
|
||||
[test_mobile_mmi_change_pin.js]
|
||||
[test_mobile_mmi_call_forwarding.js]
|
||||
[test_mobile_roaming_preference.js]
|
||||
[test_call_barring_get_option.js]
|
||||
[test_call_barring_set_error.js]
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_TIMEOUT = 60000;
|
||||
MARIONETTE_HEAD_JS = "head.js";
|
||||
|
||||
const TEST_DATA = [
|
||||
{
|
||||
reason: MozMobileConnection.CALL_FORWARD_REASON_UNCONDITIONAL,
|
||||
number: "+886912345678",
|
||||
serviceClass: MozMobileConnection.ICC_SERVICE_CLASS_VOICE,
|
||||
timeSeconds: 5
|
||||
}, {
|
||||
reason: MozMobileConnection.CALL_FORWARD_REASON_MOBILE_BUSY,
|
||||
number: "0912345678",
|
||||
serviceClass: MozMobileConnection.ICC_SERVICE_CLASS_VOICE,
|
||||
timeSeconds: 10
|
||||
}, {
|
||||
reason: MozMobileConnection.CALL_FORWARD_REASON_NO_REPLY,
|
||||
number: "+886987654321",
|
||||
serviceClass: MozMobileConnection.ICC_SERVICE_CLASS_VOICE,
|
||||
timeSeconds: 15
|
||||
}, {
|
||||
reason: MozMobileConnection.CALL_FORWARD_REASON_NOT_REACHABLE,
|
||||
number: "+0987654321",
|
||||
serviceClass: MozMobileConnection.ICC_SERVICE_CLASS_VOICE,
|
||||
timeSeconds: 20
|
||||
}
|
||||
];
|
||||
|
||||
// Please see TS 22.030 Annex B
|
||||
const CF_REASON_TO_MMI = {
|
||||
/* CALL_FORWARD_REASON_UNCONDITIONAL */
|
||||
0: "21",
|
||||
/* CALL_FORWARD_REASON_MOBILE_BUSY */
|
||||
1: "67",
|
||||
/* CALL_FORWARD_REASON_NO_REPLY */
|
||||
2: "61",
|
||||
/* CALL_FORWARD_REASON_NOT_REACHABLE */
|
||||
3: "62",
|
||||
/* CALL_FORWARD_REASON_ALL_CALL_FORWARDING */
|
||||
4: "002",
|
||||
/* CALL_FORWARD_REASON_ALL_CONDITIONAL_CALL_FORWARDING */
|
||||
5: "004"
|
||||
};
|
||||
|
||||
// Please see TS 22.030 Annex C
|
||||
const SERVICE_CLASS_TO_MMI = {
|
||||
/* ICC_SERVICE_CLASS_VOICE */
|
||||
1: "11"
|
||||
};
|
||||
|
||||
function testSetCallForwarding(aData) {
|
||||
// Registration: **SC*SIA*SIB*SIC#
|
||||
let MMI_CODE = "**" + CF_REASON_TO_MMI[aData.reason] + "*" + aData.number +
|
||||
"*" + SERVICE_CLASS_TO_MMI[aData.serviceClass] +
|
||||
"*" + aData.timeSeconds + "#";
|
||||
log("Test " + MMI_CODE);
|
||||
|
||||
let promises = [];
|
||||
// Check cfstatechange event.
|
||||
promises.push(waitForManagerEvent("cfstatechange").then(function(aEvent) {
|
||||
is(aEvent.success, true, "check success");
|
||||
is(aEvent.action, MozMobileConnection.CALL_FORWARD_ACTION_REGISTRATION,
|
||||
"check action");
|
||||
is(aEvent.reason, aData.reason, "check reason");
|
||||
is(aEvent.number, aData.number, "check number");
|
||||
is(aEvent.timeSeconds, aData.timeSeconds, "check timeSeconds");
|
||||
is(aEvent.serviceClass, aData.serviceClass, "check serviceClass");
|
||||
}));
|
||||
// Check DOMRequest's result.
|
||||
promises.push(sendMMI(MMI_CODE)
|
||||
.then(function resolve(aResult) {
|
||||
is(aResult.serviceCode, "scCallForwarding", "Check service code");
|
||||
is(aResult.statusMessage, "smServiceRegistered", "Check status message");
|
||||
is(aResult.additionalInformation, undefined, "Check additional information");
|
||||
}, function reject(aError) {
|
||||
ok(false, "got '" + aError.name + "' error");
|
||||
}));
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
function testGetCallForwarding(aExpectedData) {
|
||||
// Interrogation: *#SC#
|
||||
let MMI_CODE = "*#" + CF_REASON_TO_MMI[aExpectedData.reason] + "#";
|
||||
log("Test " + MMI_CODE);
|
||||
|
||||
return sendMMI(MMI_CODE)
|
||||
.then(function resolve(aResult) {
|
||||
is(aResult.serviceCode, "scCallForwarding", "Check service code");
|
||||
is(aResult.statusMessage, "smServiceInterrogated", "Check status message");
|
||||
is(Array.isArray(aResult.additionalInformation), true,
|
||||
"additionalInformation should be an array");
|
||||
|
||||
for (let i = 0; i < aResult.additionalInformation.length; i++) {
|
||||
let result = aResult.additionalInformation[i];
|
||||
|
||||
// Only need to check the result containing the serviceClass that we are
|
||||
// interested in.
|
||||
if (!(result.serviceClass & aExpectedData.serviceClass)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
is(result.active, true, "check active");
|
||||
is(result.reason, aExpectedData.reason, "check reason");
|
||||
is(result.number, aExpectedData.number, "check number");
|
||||
is(result.timeSeconds, aExpectedData.timeSeconds, "check timeSeconds");
|
||||
}
|
||||
}, function reject(aError) {
|
||||
ok(false, MMI_CODE + " got error: " + aError.name);
|
||||
});
|
||||
}
|
||||
|
||||
function clearAllCallForwardingSettings() {
|
||||
log("Clear all call forwarding settings");
|
||||
|
||||
let promise = Promise.resolve();
|
||||
for (let reason = MozMobileConnection.CALL_FORWARD_REASON_UNCONDITIONAL;
|
||||
reason <= MozMobileConnection.CALL_FORWARD_REASON_ALL_CONDITIONAL_CALL_FORWARDING;
|
||||
reason++) {
|
||||
let options = {
|
||||
reason: reason,
|
||||
action: MozMobileConnection.CALL_FORWARD_ACTION_ERASURE
|
||||
};
|
||||
// Emulator doesn't support CALL_FORWARD_REASON_ALL_* yet, we catch the
|
||||
// reject here in order to avoid impact the test result.
|
||||
promise =
|
||||
promise.then(() => setCallForwardingOption(options).then(null, () => {}));
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
// Start tests
|
||||
startTestCommon(function() {
|
||||
let promise = Promise.resolve();
|
||||
for (let i = 0; i < TEST_DATA.length; i++) {
|
||||
let data = TEST_DATA[i];
|
||||
promise = promise.then(() => testSetCallForwarding(data))
|
||||
.then(() => testGetCallForwarding(data));
|
||||
}
|
||||
// reset call forwarding settings.
|
||||
return promise.then(null, () => { ok(false, "promise reject during test"); })
|
||||
.then(() => clearAllCallForwardingSettings());
|
||||
});
|
|
@ -7,7 +7,7 @@ skip-if = toolkit == "gonk"
|
|||
[test_tcpsocket_enabled_with_perm.html]
|
||||
skip-if = toolkit == "gonk" || e10s
|
||||
[test_networkstats_alarms.html]
|
||||
skip-if = toolkit != "gonk" || true # Intermittent failures (bug 1023341)
|
||||
skip-if = toolkit != "gonk"
|
||||
[test_networkstats_basics.html]
|
||||
skip-if = toolkit != "gonk" || (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(Will be fixed in bug 858005) b2g-desktop(Will be fixed in bug 858005)
|
||||
[test_networkstats_disabled.html]
|
||||
|
|
|
@ -92,7 +92,7 @@ var steps = [
|
|||
ok(true, "Calling addAlarm()");
|
||||
|
||||
req = navigator.mozNetworkStats
|
||||
.addAlarm(new window.MozNetworkStatsInterface(wifi), 1000000);
|
||||
.addAlarm(new window.MozNetworkStatsInterface(wifi), 100000000);
|
||||
|
||||
req.onsuccess = function () {
|
||||
ok(true, "Succeeded to add alarm. AlarmId: " + req.result);
|
||||
|
|
|
@ -243,10 +243,15 @@ WifiGeoPositionProvider.prototype = {
|
|||
let radio = radioService.getRadioInterface(i);
|
||||
let iccInfo = radio.rilContext.iccInfo;
|
||||
let cell = radio.rilContext.voice.cell;
|
||||
let type = radio.rilContext.voice.type;
|
||||
|
||||
if (iccInfo && cell) {
|
||||
// TODO type and signal strength
|
||||
result.push({ radio: "gsm",
|
||||
if (iccInfo && cell && type) {
|
||||
if (type === "gsm" || type === "gprs" || type === "edge") {
|
||||
type = "gsm";
|
||||
} else {
|
||||
type = "wcdma";
|
||||
}
|
||||
result.push({ radio: type,
|
||||
mobileCountryCode: iccInfo.mcc,
|
||||
mobileNetworkCode: iccInfo.mnc,
|
||||
locationAreaCode: cell.gsmLocationAreaCode,
|
||||
|
|
|
@ -246,7 +246,7 @@ GLContextEGL::GLContextEGL(
|
|||
mHwc = HwcComposer2D::GetInstance();
|
||||
MOZ_ASSERT(!mHwc->Initialized());
|
||||
|
||||
if (mHwc->Init(EGL_DISPLAY(), mSurface)) {
|
||||
if (mHwc->Init(EGL_DISPLAY(), mSurface, this)) {
|
||||
NS_WARNING("HWComposer initialization failed!");
|
||||
mHwc = nullptr;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
|
@ -43,6 +44,8 @@
|
|||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/scache/StartupCache.h"
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
using namespace mozilla;
|
||||
|
@ -582,7 +585,8 @@ FT2FontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
|
|||
*/
|
||||
|
||||
void
|
||||
FT2FontFamily::AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList)
|
||||
FT2FontFamily::AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList,
|
||||
Visibility aVisibility)
|
||||
{
|
||||
for (int i = 0, n = mAvailableFonts.Length(); i < n; ++i) {
|
||||
const FT2FontEntry *fe =
|
||||
|
@ -595,7 +599,8 @@ FT2FontFamily::AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList)
|
|||
fe->mFilename,
|
||||
fe->Weight(), fe->Stretch(),
|
||||
fe->IsItalic(),
|
||||
fe->mFTFontIndex));
|
||||
fe->mFTFontIndex,
|
||||
aVisibility == kHidden));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -827,9 +832,11 @@ gfxFT2FontList::gfxFT2FontList()
|
|||
}
|
||||
|
||||
void
|
||||
gfxFT2FontList::AppendFacesFromCachedFaceList(const nsCString& aFileName,
|
||||
bool aStdFile,
|
||||
const nsCString& aFaceList)
|
||||
gfxFT2FontList::AppendFacesFromCachedFaceList(
|
||||
const nsCString& aFileName,
|
||||
const nsCString& aFaceList,
|
||||
StandardFile aStdFile,
|
||||
FT2FontFamily::Visibility aVisibility)
|
||||
{
|
||||
const char *beginning = aFaceList.get();
|
||||
const char *end = strchr(beginning, ',');
|
||||
|
@ -865,7 +872,8 @@ gfxFT2FontList::AppendFacesFromCachedFaceList(const nsCString& aFileName,
|
|||
int32_t stretch = strtol(beginning, nullptr, 10);
|
||||
|
||||
FontListEntry fle(familyName, faceName, aFileName,
|
||||
weight, stretch, italic, index);
|
||||
weight, stretch, italic, index,
|
||||
aVisibility == FT2FontFamily::kHidden);
|
||||
AppendFaceFromFontListEntry(fle, aStdFile);
|
||||
|
||||
beginning = end + 1;
|
||||
|
@ -923,8 +931,9 @@ FT2FontEntry::CheckForBrokenFont(gfxFontFamily *aFamily)
|
|||
|
||||
void
|
||||
gfxFT2FontList::AppendFacesFromFontFile(const nsCString& aFileName,
|
||||
bool aStdFile,
|
||||
FontNameCache *aCache)
|
||||
FontNameCache *aCache,
|
||||
StandardFile aStdFile,
|
||||
FT2FontFamily::Visibility aVisibility)
|
||||
{
|
||||
nsCString faceList;
|
||||
uint32_t filesize = 0, timestamp = 0;
|
||||
|
@ -938,7 +947,8 @@ gfxFT2FontList::AppendFacesFromFontFile(const nsCString& aFileName,
|
|||
s.st_mtime == timestamp && s.st_size == filesize)
|
||||
{
|
||||
LOG(("using cached font info for %s", aFileName.get()));
|
||||
AppendFacesFromCachedFaceList(aFileName, aStdFile, faceList);
|
||||
AppendFacesFromCachedFaceList(aFileName, faceList, aStdFile,
|
||||
aVisibility);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -954,7 +964,7 @@ gfxFT2FontList::AppendFacesFromFontFile(const nsCString& aFileName,
|
|||
if (FT_Err_Ok != FT_New_Face(ftLibrary, aFileName.get(), i, &face)) {
|
||||
continue;
|
||||
}
|
||||
AddFaceToList(aFileName, i, aStdFile, face, faceList);
|
||||
AddFaceToList(aFileName, i, aStdFile, aVisibility, face, faceList);
|
||||
FT_Done_Face(face);
|
||||
}
|
||||
FT_Done_Face(dummy);
|
||||
|
@ -1017,7 +1027,9 @@ gfxFT2FontList::FindFontsInOmnijar(FontNameCache *aCache)
|
|||
// add the face to the available font list and to the faceList string
|
||||
void
|
||||
gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
|
||||
bool aStdFile, FT_Face aFace,
|
||||
StandardFile aStdFile,
|
||||
FT2FontFamily::Visibility aVisibility,
|
||||
FT_Face aFace,
|
||||
nsCString& aFaceList)
|
||||
{
|
||||
if (FT_Err_Ok != FT_Select_Charmap(aFace, FT_ENCODING_UNICODE)) {
|
||||
|
@ -1030,13 +1042,17 @@ gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
|
|||
FT2FontEntry* fe =
|
||||
CreateNamedFontEntry(aFace, aEntryName.get(), aIndex);
|
||||
|
||||
auto& fontFamilies =
|
||||
(aVisibility == FT2FontFamily::kHidden) ? mHiddenFontFamilies :
|
||||
mFontFamilies;
|
||||
|
||||
if (fe) {
|
||||
NS_ConvertUTF8toUTF16 name(aFace->family_name);
|
||||
BuildKeyNameFromFontName(name);
|
||||
gfxFontFamily *family = mFontFamilies.GetWeak(name);
|
||||
gfxFontFamily *family = fontFamilies.GetWeak(name);
|
||||
if (!family) {
|
||||
family = new FT2FontFamily(name);
|
||||
mFontFamilies.Put(name, family);
|
||||
fontFamilies.Put(name, family);
|
||||
if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
|
||||
family->SetSkipSpaceFeatureCheck(true);
|
||||
}
|
||||
|
@ -1044,7 +1060,7 @@ gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
|
|||
family->SetBadUnderlineFamily();
|
||||
}
|
||||
}
|
||||
fe->mStandardFace = aStdFile;
|
||||
fe->mStandardFace = (aStdFile == kStandard);
|
||||
family->AddFontEntry(fe);
|
||||
|
||||
fe->CheckForBrokenFont(family);
|
||||
|
@ -1074,7 +1090,7 @@ gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive,
|
|||
uint32_t filesize, timestamp;
|
||||
aCache->GetInfoForFile(aEntryName, faceList, ×tamp, &filesize);
|
||||
if (faceList.Length() > 0) {
|
||||
AppendFacesFromCachedFaceList(aEntryName, true, faceList);
|
||||
AppendFacesFromCachedFaceList(aEntryName, faceList);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1111,7 +1127,8 @@ gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive,
|
|||
if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf, bufSize, i, &face)) {
|
||||
continue;
|
||||
}
|
||||
AddFaceToList(aEntryName, i, true, face, faceList);
|
||||
AddFaceToList(aEntryName, i, kStandard, FT2FontFamily::kVisible,
|
||||
face, faceList);
|
||||
FT_Done_Face(face);
|
||||
}
|
||||
|
||||
|
@ -1160,14 +1177,19 @@ gfxFT2FontList::FindFonts()
|
|||
InfallibleTArray<FontListEntry> fonts;
|
||||
mozilla::dom::ContentChild::GetSingleton()->SendReadFontList(&fonts);
|
||||
for (uint32_t i = 0, n = fonts.Length(); i < n; ++i) {
|
||||
AppendFaceFromFontListEntry(fonts[i], false);
|
||||
// We don't need to identify "standard" font files here,
|
||||
// as the faces are already sorted.
|
||||
AppendFaceFromFontListEntry(fonts[i], kUnknown);
|
||||
}
|
||||
// Passing null for userdata tells Finalize that it does not need
|
||||
// to sort faces (because they were already sorted by chrome,
|
||||
// so we just maintain the existing order)
|
||||
mFontFamilies.Enumerate(FinalizeFamilyMemberList, nullptr);
|
||||
LOG(("got font list from chrome process: %d faces in %d families",
|
||||
fonts.Length(), mFontFamilies.Count()));
|
||||
mHiddenFontFamilies.Enumerate(FinalizeFamilyMemberList, nullptr);
|
||||
LOG(("got font list from chrome process: %d faces in %d families "
|
||||
"and %d in hidden families",
|
||||
fonts.Length(), mFontFamilies.Count(),
|
||||
mHiddenFontFamilies.Count()));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1185,13 +1207,20 @@ gfxFT2FontList::FindFonts()
|
|||
}
|
||||
root.AppendLiteral("/fonts");
|
||||
|
||||
FindFontsInDir(root, &fnc);
|
||||
FindFontsInDir(root, &fnc, FT2FontFamily::kVisible);
|
||||
|
||||
if (mFontFamilies.Count() == 0) {
|
||||
// if we can't find/read the font directory, we are doomed!
|
||||
NS_RUNTIMEABORT("Could not read the system fonts directory");
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Look for fonts in /system/fonts/hidden and preload them to the
|
||||
// user-font cache as data: URIs
|
||||
root.AppendLiteral("/hidden");
|
||||
FindFontsInDir(root, &fnc, FT2FontFamily::kHidden);
|
||||
#endif
|
||||
|
||||
// Look for fonts stored in omnijar, unless we're on a low-memory
|
||||
// device where we don't want to spend the RAM to decompress them.
|
||||
// (Prefs may disable this, or force-enable it even with low memory.)
|
||||
|
@ -1212,7 +1241,7 @@ gfxFT2FontList::FindFonts()
|
|||
nsCString localPath;
|
||||
rv = localDir->GetNativePath(localPath);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
FindFontsInDir(localPath, &fnc);
|
||||
FindFontsInDir(localPath, &fnc, FT2FontFamily::kVisible);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1220,10 +1249,13 @@ gfxFT2FontList::FindFonts()
|
|||
// and marking "simple" families.
|
||||
// Passing non-null userData here says that we want faces to be sorted.
|
||||
mFontFamilies.Enumerate(FinalizeFamilyMemberList, this);
|
||||
mHiddenFontFamilies.Enumerate(FinalizeFamilyMemberList, this);
|
||||
}
|
||||
|
||||
void
|
||||
gfxFT2FontList::FindFontsInDir(const nsCString& aDir, FontNameCache *aFNC)
|
||||
gfxFT2FontList::FindFontsInDir(const nsCString& aDir,
|
||||
FontNameCache *aFNC,
|
||||
FT2FontFamily::Visibility aVisibility)
|
||||
{
|
||||
static const char* sStandardFonts[] = {
|
||||
"DroidSans.ttf",
|
||||
|
@ -1272,7 +1304,8 @@ gfxFT2FontList::FindFontsInDir(const nsCString& aDir, FontNameCache *aFNC)
|
|||
// note that if we have cached info for this file in fnc,
|
||||
// and the file is unchanged, we won't actually need to read it.
|
||||
// If the file is new/changed, this will update the FontNameCache.
|
||||
AppendFacesFromFontFile(s, isStdFont, aFNC);
|
||||
AppendFacesFromFontFile(s, aFNC, isStdFont ? kStandard : kUnknown,
|
||||
aVisibility);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1281,16 +1314,18 @@ gfxFT2FontList::FindFontsInDir(const nsCString& aDir, FontNameCache *aFNC)
|
|||
|
||||
void
|
||||
gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry& aFLE,
|
||||
bool aStdFile)
|
||||
StandardFile aStdFile)
|
||||
{
|
||||
FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(aFLE);
|
||||
if (fe) {
|
||||
fe->mStandardFace = aStdFile;
|
||||
auto& fontFamilies =
|
||||
aFLE.isHidden() ? mHiddenFontFamilies : mFontFamilies;
|
||||
fe->mStandardFace = (aStdFile == kStandard);
|
||||
nsAutoString name(aFLE.familyName());
|
||||
gfxFontFamily *family = mFontFamilies.GetWeak(name);
|
||||
gfxFontFamily *family = fontFamilies.GetWeak(name);
|
||||
if (!family) {
|
||||
family = new FT2FontFamily(name);
|
||||
mFontFamilies.Put(name, family);
|
||||
fontFamilies.Put(name, family);
|
||||
if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
|
||||
family->SetSkipSpaceFeatureCheck(true);
|
||||
}
|
||||
|
@ -1313,7 +1348,21 @@ AddFamilyToFontList(nsStringHashKey::KeyType aKey,
|
|||
reinterpret_cast<InfallibleTArray<FontListEntry>*>(aUserArg);
|
||||
|
||||
FT2FontFamily *family = static_cast<FT2FontFamily*>(aFamily.get());
|
||||
family->AddFacesToFontList(fontlist);
|
||||
family->AddFacesToFontList(fontlist, FT2FontFamily::kVisible);
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
AddHiddenFamilyToFontList(nsStringHashKey::KeyType aKey,
|
||||
nsRefPtr<gfxFontFamily>& aFamily,
|
||||
void* aUserArg)
|
||||
{
|
||||
InfallibleTArray<FontListEntry>* fontlist =
|
||||
reinterpret_cast<InfallibleTArray<FontListEntry>*>(aUserArg);
|
||||
|
||||
FT2FontFamily *family = static_cast<FT2FontFamily*>(aFamily.get());
|
||||
family->AddFacesToFontList(fontlist, FT2FontFamily::kHidden);
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
@ -1322,6 +1371,7 @@ void
|
|||
gfxFT2FontList::GetFontList(InfallibleTArray<FontListEntry>* retValue)
|
||||
{
|
||||
mFontFamilies.Enumerate(AddFamilyToFontList, retValue);
|
||||
mHiddenFontFamilies.Enumerate(AddHiddenFamilyToFontList, retValue);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1338,16 +1388,93 @@ LoadSkipSpaceLookupCheck(nsTHashtable<nsStringHashKey>& aSkipSpaceLookupCheck)
|
|||
}
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
PreloadAsUserFontFaces(nsStringHashKey::KeyType aKey,
|
||||
nsRefPtr<gfxFontFamily>& aFamily,
|
||||
void* aUserArg)
|
||||
{
|
||||
gfxFontFamily *family = aFamily.get();
|
||||
|
||||
auto& faces = family->GetFontList();
|
||||
size_t count = faces.Length();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
FT2FontEntry* fe = static_cast<FT2FontEntry*>(faces[i].get());
|
||||
if (fe->mFTFontIndex != 0) {
|
||||
NS_NOTREACHED("don't try to preload a multi-face font");
|
||||
continue;
|
||||
}
|
||||
|
||||
// XXX Should we move the i/o here off the main thread?
|
||||
|
||||
// Map the font data in fe->mFilename, so we can generate a data: URI.
|
||||
int fd = open(fe->mFilename.get(), O_RDONLY);
|
||||
if (fd < 0) {
|
||||
continue;
|
||||
}
|
||||
struct stat buf;
|
||||
if (fstat(fd, &buf) != 0 || buf.st_size < 12) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
char* data = static_cast<char*>(
|
||||
mmap(0, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0));
|
||||
close(fd);
|
||||
if (data == MAP_FAILED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// First byte is sufficient to distinguish WOFF from uncompressed
|
||||
// OpenType (either TrueType or CFF).
|
||||
bool isWoff = (data[0] == 'w');
|
||||
|
||||
// Generate a corresponding data: URI that apps could use.
|
||||
nsCString encodedData;
|
||||
nsresult rv = Base64Encode(Substring(data, buf.st_size), encodedData);
|
||||
munmap(data, buf.st_size);
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
nsCString spec("data:font/");
|
||||
spec.Append(isWoff ? "woff" : "opentype");
|
||||
spec.Append(";base64,");
|
||||
spec.Append(encodedData);
|
||||
#if 0
|
||||
ALOG("\n**** Preloading family [%s] face [%s]:\n%s\n\n",
|
||||
NS_ConvertUTF16toUTF8(family->Name()).get(),
|
||||
fe->mFilename.get(),
|
||||
spec.get());
|
||||
#endif
|
||||
|
||||
// Record the URI in gfxUserFontData on the entry.
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), spec))) {
|
||||
continue;
|
||||
}
|
||||
fe->mUserFontData = new gfxUserFontData;
|
||||
fe->mUserFontData->mURI = uri;
|
||||
fe->mUserFontData->mRealName = fe->Name();
|
||||
|
||||
// Stash it persistently in the user-font cache.
|
||||
gfxUserFontSet::UserFontCache::CacheFont(
|
||||
fe, gfxUserFontSet::UserFontCache::kPersistent);
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsresult
|
||||
gfxFT2FontList::InitFontList()
|
||||
{
|
||||
// reset font lists
|
||||
gfxPlatformFontList::InitFontList();
|
||||
mHiddenFontFamilies.Clear();
|
||||
|
||||
LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies);
|
||||
|
||||
FindFonts();
|
||||
|
||||
mHiddenFontFamilies.Enumerate(PreloadAsUserFontFaces, this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1402,6 +1529,8 @@ gfxFT2FontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
|
|||
// walk over list of names
|
||||
FullFontNameSearch data(aFontName);
|
||||
|
||||
// Note that we only check mFontFamilies here, not mHiddenFontFamilies;
|
||||
// hence @font-face { src:local(...) } will not find hidden fonts.
|
||||
mFontFamilies.Enumerate(FindFullName, &data);
|
||||
|
||||
if (!data.mFontEntry) {
|
||||
|
@ -1459,3 +1588,21 @@ gfxFT2FontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
|
|||
return FT2FontEntry::CreateFontEntry(*aProxyEntry, aFontData, aLength);
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
AppendFamily(nsStringHashKey::KeyType aKey,
|
||||
nsRefPtr<gfxFontFamily>& aFamily,
|
||||
void* aUserArg)
|
||||
{
|
||||
nsTArray<nsRefPtr<gfxFontFamily> > * familyArray =
|
||||
reinterpret_cast<nsTArray<nsRefPtr<gfxFontFamily>>*>(aUserArg);
|
||||
|
||||
familyArray->AppendElement(aFamily);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
gfxFT2FontList::GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray)
|
||||
{
|
||||
mFontFamilies.Enumerate(AppendFamily, &aFamilyArray);
|
||||
mHiddenFontFamilies.Enumerate(AppendFamily, &aFamilyArray);
|
||||
}
|
||||
|
|
|
@ -95,11 +95,20 @@ public:
|
|||
class FT2FontFamily : public gfxFontFamily
|
||||
{
|
||||
public:
|
||||
// Flags to indicate whether a font should be "visible" in the global
|
||||
// font list (available for use in font-family), or "hidden" (available
|
||||
// only to support a matching data: URI used in @font-face).
|
||||
typedef enum {
|
||||
kVisible,
|
||||
kHidden
|
||||
} Visibility;
|
||||
|
||||
FT2FontFamily(const nsAString& aName) :
|
||||
gfxFontFamily(aName) { }
|
||||
|
||||
// Append this family's faces to the IPC fontlist
|
||||
void AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList);
|
||||
void AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList,
|
||||
Visibility aVisibility);
|
||||
};
|
||||
|
||||
class gfxFT2FontList : public gfxPlatformFontList
|
||||
|
@ -122,35 +131,52 @@ public:
|
|||
return static_cast<gfxFT2FontList*>(gfxPlatformFontList::PlatformFontList());
|
||||
}
|
||||
|
||||
virtual void GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray);
|
||||
|
||||
protected:
|
||||
typedef enum {
|
||||
kUnknown,
|
||||
kStandard
|
||||
} StandardFile;
|
||||
|
||||
virtual nsresult InitFontList();
|
||||
|
||||
void AppendFaceFromFontListEntry(const FontListEntry& aFLE,
|
||||
bool isStdFile);
|
||||
StandardFile aStdFile);
|
||||
|
||||
void AppendFacesFromFontFile(const nsCString& aFileName,
|
||||
bool isStdFile = false,
|
||||
FontNameCache *aCache = nullptr);
|
||||
FontNameCache *aCache,
|
||||
StandardFile aStdFile,
|
||||
FT2FontFamily::Visibility aVisibility);
|
||||
|
||||
void AppendFacesFromOmnijarEntry(nsZipArchive *aReader,
|
||||
const nsCString& aEntryName,
|
||||
FontNameCache *aCache,
|
||||
bool aJarChanged);
|
||||
|
||||
// the defaults here are suitable for reading bundled fonts from omnijar
|
||||
void AppendFacesFromCachedFaceList(const nsCString& aFileName,
|
||||
bool isStdFile,
|
||||
const nsCString& aFaceList);
|
||||
const nsCString& aFaceList,
|
||||
StandardFile aStdFile = kStandard,
|
||||
FT2FontFamily::Visibility aVisibility =
|
||||
FT2FontFamily::kVisible);
|
||||
|
||||
void AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
|
||||
bool aStdFile, FT_Face aFace, nsCString& aFaceList);
|
||||
StandardFile aStdFile,
|
||||
FT2FontFamily::Visibility aVisibility,
|
||||
FT_Face aFace, nsCString& aFaceList);
|
||||
|
||||
void FindFonts();
|
||||
|
||||
void FindFontsInOmnijar(FontNameCache *aCache);
|
||||
|
||||
void FindFontsInDir(const nsCString& aDir, FontNameCache* aFNC);
|
||||
void FindFontsInDir(const nsCString& aDir, FontNameCache* aFNC,
|
||||
FT2FontFamily::Visibility aVisibility);
|
||||
|
||||
nsTHashtable<nsStringHashKey> mSkipSpaceLookupCheckFamilies;
|
||||
|
||||
private:
|
||||
nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> mHiddenFontFamilies;
|
||||
};
|
||||
|
||||
#endif /* GFX_FT2FONTLIST_H */
|
||||
|
|
|
@ -828,6 +828,14 @@ nsTHashtable<gfxUserFontSet::UserFontCache::Entry>*
|
|||
|
||||
NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::Flusher, nsIObserver)
|
||||
|
||||
PLDHashOperator
|
||||
gfxUserFontSet::UserFontCache::Entry::RemoveUnlessPersistent(Entry* aEntry,
|
||||
void* aUserData)
|
||||
{
|
||||
return aEntry->mPersistence == kPersistent ? PL_DHASH_NEXT :
|
||||
PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
gfxUserFontSet::UserFontCache::Entry::RemoveIfPrivate(Entry* aEntry,
|
||||
void* aUserData)
|
||||
|
@ -861,7 +869,7 @@ gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports* aSubject,
|
|||
}
|
||||
|
||||
if (!strcmp(aTopic, "cacheservice:empty-cache")) {
|
||||
sUserFonts->Clear();
|
||||
sUserFonts->EnumerateEntries(Entry::RemoveUnlessPersistent, nullptr);
|
||||
} else if (!strcmp(aTopic, "last-pb-context-exited")) {
|
||||
sUserFonts->EnumerateEntries(Entry::RemoveIfPrivate, nullptr);
|
||||
} else if (!strcmp(aTopic, "xpcom-shutdown")) {
|
||||
|
@ -919,7 +927,8 @@ gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const
|
|||
}
|
||||
|
||||
void
|
||||
gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry *aFontEntry)
|
||||
gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry *aFontEntry,
|
||||
EntryPersistence aPersistence)
|
||||
{
|
||||
NS_ASSERTION(aFontEntry->mFamilyName.Length() != 0,
|
||||
"caching a font associated with no family yet");
|
||||
|
@ -948,7 +957,7 @@ gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry *aFontEntry)
|
|||
principal = data->mPrincipal;
|
||||
}
|
||||
sUserFonts->PutEntry(Key(data->mURI, principal, aFontEntry,
|
||||
data->mPrivate));
|
||||
data->mPrivate, aPersistence));
|
||||
|
||||
#ifdef DEBUG_USERFONT_CACHE
|
||||
printf("userfontcache added fontentry: %p\n", aFontEntry);
|
||||
|
|
|
@ -61,7 +61,7 @@ operator==(const gfxFontFaceSrc& a, const gfxFontFaceSrc& b)
|
|||
class gfxUserFontData {
|
||||
public:
|
||||
gfxUserFontData()
|
||||
: mSrcIndex(0), mFormat(0), mMetaOrigLen(0)
|
||||
: mSrcIndex(0), mFormat(0), mMetaOrigLen(0), mPrivate(false)
|
||||
{ }
|
||||
virtual ~gfxUserFontData() { }
|
||||
|
||||
|
@ -248,10 +248,21 @@ public:
|
|||
|
||||
class UserFontCache {
|
||||
public:
|
||||
// Flag passed when caching a font entry, to specify whether the entry
|
||||
// should persist in the cache or be discardable.
|
||||
typedef enum {
|
||||
kDiscardable,
|
||||
kPersistent
|
||||
} EntryPersistence;
|
||||
|
||||
// Record a loaded user-font in the cache. This requires that the
|
||||
// font-entry's userFontData has been set up already, as it relies
|
||||
// on the URI and Principal recorded there.
|
||||
static void CacheFont(gfxFontEntry *aFontEntry);
|
||||
// If aPersistence is Persistent, the entry will remain in the cache
|
||||
// across cacheservice:empty-cache notifications. This is used for
|
||||
// "preloaded hidden fonts" on FxOS.
|
||||
static void CacheFont(gfxFontEntry *aFontEntry,
|
||||
EntryPersistence aPersistence = kDiscardable);
|
||||
|
||||
// The given gfxFontEntry is being destroyed, so remove any record that
|
||||
// refers to it.
|
||||
|
@ -298,13 +309,16 @@ public:
|
|||
nsCOMPtr<nsIPrincipal> mPrincipal; // use nullptr with data: URLs
|
||||
gfxFontEntry *mFontEntry;
|
||||
bool mPrivate;
|
||||
EntryPersistence mPersistence;
|
||||
|
||||
Key(nsIURI* aURI, nsIPrincipal* aPrincipal,
|
||||
gfxFontEntry* aFontEntry, bool aPrivate)
|
||||
gfxFontEntry* aFontEntry, bool aPrivate,
|
||||
EntryPersistence aPersistence = kDiscardable)
|
||||
: mURI(aURI),
|
||||
mPrincipal(aPrincipal),
|
||||
mFontEntry(aFontEntry),
|
||||
mPrivate(aPrivate)
|
||||
mPrivate(aPrivate),
|
||||
mPersistence(aPersistence)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -317,14 +331,16 @@ public:
|
|||
: mURI(aKey->mURI),
|
||||
mPrincipal(aKey->mPrincipal),
|
||||
mFontEntry(aKey->mFontEntry),
|
||||
mPrivate(aKey->mPrivate)
|
||||
mPrivate(aKey->mPrivate),
|
||||
mPersistence(aKey->mPersistence)
|
||||
{ }
|
||||
|
||||
Entry(const Entry& aOther)
|
||||
: mURI(aOther.mURI),
|
||||
mPrincipal(aOther.mPrincipal),
|
||||
mFontEntry(aOther.mFontEntry),
|
||||
mPrivate(aOther.mPrivate)
|
||||
mPrivate(aOther.mPrivate),
|
||||
mPersistence(aOther.mPersistence)
|
||||
{ }
|
||||
|
||||
~Entry() { }
|
||||
|
@ -352,9 +368,14 @@ public:
|
|||
|
||||
gfxFontEntry* GetFontEntry() const { return mFontEntry; }
|
||||
|
||||
static PLDHashOperator RemoveIfPrivate(Entry* aEntry, void* aUserData);
|
||||
static PLDHashOperator RemoveIfMatches(Entry* aEntry, void* aUserData);
|
||||
static PLDHashOperator DisconnectSVG(Entry* aEntry, void* aUserData);
|
||||
static PLDHashOperator
|
||||
RemoveUnlessPersistent(Entry* aEntry, void* aUserData);
|
||||
static PLDHashOperator
|
||||
RemoveIfPrivate(Entry* aEntry, void* aUserData);
|
||||
static PLDHashOperator
|
||||
RemoveIfMatches(Entry* aEntry, void* aUserData);
|
||||
static PLDHashOperator
|
||||
DisconnectSVG(Entry* aEntry, void* aUserData);
|
||||
|
||||
#ifdef DEBUG_USERFONT_CACHE
|
||||
static PLDHashOperator DumpEntry(Entry* aEntry, void* aUserData);
|
||||
|
@ -377,6 +398,9 @@ public:
|
|||
|
||||
// Whether this font was loaded from a private window.
|
||||
bool mPrivate;
|
||||
|
||||
// Whether this entry should survive cache-flushing.
|
||||
EntryPersistence mPersistence;
|
||||
};
|
||||
|
||||
static nsTHashtable<Entry> *sUserFonts;
|
||||
|
|
|
@ -23,14 +23,14 @@
|
|||
|
||||
#define FLOAT32X4_UNARY_FUNCTION_LIST(V) \
|
||||
V(abs, (Func<Float32x4, Abs<float, Float32x4>, Float32x4>), 1, 0, Abs) \
|
||||
V(bitsToInt32x4, (FuncConvertBits<Float32x4, Int32x4>), 1, 0, BitsToInt32x4) \
|
||||
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Float32x4>), 1, 0, FromInt32x4Bits) \
|
||||
V(neg, (Func<Float32x4, Neg<float, Float32x4>, Float32x4>), 1, 0, Neg) \
|
||||
V(not, (CoercedFunc<Float32x4, Int32x4, Not<int32_t, Int32x4>, Float32x4>), 1, 0, Not) \
|
||||
V(reciprocal, (Func<Float32x4, Rec<float, Float32x4>, Float32x4>), 1, 0, Reciprocal) \
|
||||
V(reciprocalSqrt, (Func<Float32x4, RecSqrt<float, Float32x4>, Float32x4>), 1, 0, ReciprocalSqrt) \
|
||||
V(splat, (FuncSplat<Float32x4>), 1, 0, Splat) \
|
||||
V(sqrt, (Func<Float32x4, Sqrt<float, Float32x4>, Float32x4>), 1, 0, Sqrt) \
|
||||
V(toInt32x4, (FuncConvert<Float32x4, Int32x4>), 1, 0, ToInt32x4)
|
||||
V(fromInt32x4, (FuncConvert<Int32x4, Float32x4> ), 1, 0, FromInt32x4)
|
||||
|
||||
#define FLOAT32X4_BINARY_FUNCTION_LIST(V) \
|
||||
V(add, (Func<Float32x4, Add<float, Float32x4>, Float32x4>), 2, 0, Add) \
|
||||
|
@ -69,11 +69,11 @@
|
|||
V(zero, (FuncZero<Int32x4>), 0, 0, Zero)
|
||||
|
||||
#define INT32X4_UNARY_FUNCTION_LIST(V) \
|
||||
V(bitsToFloat32x4, (FuncConvertBits<Int32x4, Float32x4>), 1, 0, BitsToFloat32x4) \
|
||||
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int32x4>), 1, 0, FromFloat32x4Bits) \
|
||||
V(neg, (Func<Int32x4, Neg<int32_t, Int32x4>, Int32x4>), 1, 0, Neg) \
|
||||
V(not, (Func<Int32x4, Not<int32_t, Int32x4>, Int32x4>), 1, 0, Not) \
|
||||
V(splat, (FuncSplat<Int32x4>), 0, 0, Splat) \
|
||||
V(toFloat32x4, (FuncConvert<Int32x4, Float32x4>), 1, 0, ToFloat32x4)
|
||||
V(fromFloat32x4, (FuncConvert<Float32x4, Int32x4>), 1, 0, FromFloat32x4)
|
||||
|
||||
#define INT32X4_BINARY_FUNCTION_LIST(V) \
|
||||
V(add, (Func<Int32x4, Add<int32_t, Int32x4>, Int32x4>), 2, 0, Add) \
|
||||
|
|
|
@ -143,22 +143,6 @@ function CheckObjectCoercible(v) {
|
|||
ThrowError(JSMSG_CANT_CONVERT_TO, ToString(v), "object");
|
||||
}
|
||||
|
||||
|
||||
/********** Various utility functions **********/
|
||||
|
||||
|
||||
/** Returns true iff Type(v) is Object; see ES5 8.6. */
|
||||
function IsObject(v) {
|
||||
// Watch out for |typeof null === "object"| as the most obvious pitfall.
|
||||
// But also be careful of SpiderMonkey's objects that emulate undefined
|
||||
// (i.e. |document.all|), which have bogus |typeof| behavior. Detect
|
||||
// these objects using strict equality, which said bogosity doesn't affect.
|
||||
return (typeof v === "object" && v !== null) ||
|
||||
typeof v === "function" ||
|
||||
(typeof v === "undefined" && v !== undefined);
|
||||
}
|
||||
|
||||
|
||||
/********** Testing code **********/
|
||||
|
||||
#ifdef ENABLE_PARALLEL_JS
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
setJitCompilerOption("baseline.usecount.trigger", 10);
|
||||
setJitCompilerOption("ion.usecount.trigger", 20);
|
||||
|
||||
function myCeil(x) {
|
||||
if(x >= 0) {
|
||||
var r = Math.abs(x % 1);
|
||||
if(r != 0)
|
||||
return (x + 1) - r;
|
||||
else
|
||||
return x;
|
||||
}
|
||||
else
|
||||
return x + Math.abs(x % 1);
|
||||
}
|
||||
|
||||
function ceilRangeTest(x) {
|
||||
if(10 < x) {
|
||||
if(x < 100) {
|
||||
assertEq(Math.ceil(x), myCeil(x));
|
||||
}
|
||||
}
|
||||
|
||||
if(-100 < x) {
|
||||
if(x < -10) {
|
||||
assertEq(Math.ceil(x), myCeil(x));
|
||||
}
|
||||
}
|
||||
|
||||
if (-(4294967296 - 1) < x) {
|
||||
if(x < 10) {
|
||||
assertEq(Math.ceil(x), myCeil(x));
|
||||
}
|
||||
}
|
||||
|
||||
if (-10 < x) {
|
||||
if(x < 4294967296) {
|
||||
assertEq(Math.ceil(x), myCeil(x));
|
||||
}
|
||||
}
|
||||
|
||||
if (-2147483648 < x) {
|
||||
if(x < 10) {
|
||||
assertEq(Math.ceil(x), myCeil(x));
|
||||
}
|
||||
}
|
||||
|
||||
if ((-2147483648 -1) < x) {
|
||||
if(x < 10) {
|
||||
assertEq(Math.ceil(x), myCeil(x));
|
||||
}
|
||||
}
|
||||
|
||||
if (10 < x) {
|
||||
if(x < 2147483648) {
|
||||
assertEq(Math.ceil(x), myCeil(x));
|
||||
}
|
||||
}
|
||||
|
||||
if (10 < x) {
|
||||
if(x < 2147483649) {
|
||||
assertEq(Math.ceil(x), myCeil(x));
|
||||
}
|
||||
}
|
||||
|
||||
if (Math.pow(2,31) < x) {
|
||||
if(x < Math.pow(2,33)) {
|
||||
assertEq(Math.ceil(x), myCeil(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var a = [Math.pow(2,31), Math.pow(2,33), -4294967296.4, 214748364.2, -50.4, 50.4];
|
||||
|
||||
for(var i = 0; i < 10; i++) {
|
||||
for (var j = 0; j < a.length; j++) {
|
||||
ceilRangeTest(a[j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = 0; j < 30; j++) {
|
||||
(function() {
|
||||
Math.ceil(1.5);
|
||||
})()
|
||||
}
|
||||
|
||||
for (var j = 0; j < 30; j++) {
|
||||
(function() {
|
||||
Math.ceil(-1.5);
|
||||
})()
|
||||
}
|
||||
|
||||
for (var j = 0; j < 30; j++) {
|
||||
(function() {
|
||||
Math.ceil(-127.5);
|
||||
})()
|
||||
}
|
|
@ -296,6 +296,14 @@ function rconcat_number(i) {
|
|||
return i;
|
||||
}
|
||||
|
||||
var uceFault_string_length = eval(uneval(uceFault).replace('uceFault', 'uceFault_string_length'));
|
||||
function rstring_length(i) {
|
||||
var x = i.toString().length;
|
||||
if (uceFault_string_length(i) || uceFault_string_length(i))
|
||||
assertEq(x, 2);
|
||||
return i;
|
||||
}
|
||||
|
||||
var uceFault_floor_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_floor_number'));
|
||||
function rfloor_number(i) {
|
||||
var x = Math.floor(i + 0.1111);
|
||||
|
@ -425,6 +433,7 @@ for (i = 0; i < 100; i++) {
|
|||
rmod_object(i);
|
||||
rconcat_string(i);
|
||||
rconcat_number(i);
|
||||
rstring_length(i);
|
||||
rfloor_number(i);
|
||||
rfloor_object(i);
|
||||
rround_number(i);
|
||||
|
|
|
@ -8484,6 +8484,15 @@ CodeGenerator::visitIsCallable(LIsCallable *ins)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitIsObject(LIsObject *ins)
|
||||
{
|
||||
Register output = ToRegister(ins->output());
|
||||
ValueOperand value = ToValue(ins, LIsObject::Input);
|
||||
masm.testObjectSet(Assembler::Equal, value, output);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::loadOutermostJSScript(Register reg)
|
||||
{
|
||||
|
|
|
@ -290,6 +290,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
bool visitCallDOMNative(LCallDOMNative *lir);
|
||||
bool visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir);
|
||||
bool visitIsCallable(LIsCallable *lir);
|
||||
bool visitIsObject(LIsObject *lir);
|
||||
bool visitHaveSameClass(LHaveSameClass *lir);
|
||||
bool visitHasClass(LHasClass *lir);
|
||||
bool visitAsmJSCall(LAsmJSCall *lir);
|
||||
|
|
|
@ -730,6 +730,7 @@ class IonBuilder : public MIRGenerator
|
|||
|
||||
// Utility intrinsics.
|
||||
InliningStatus inlineIsCallable(CallInfo &callInfo);
|
||||
InliningStatus inlineIsObject(CallInfo &callInfo);
|
||||
InliningStatus inlineHaveSameClass(CallInfo &callInfo);
|
||||
InliningStatus inlineToObject(CallInfo &callInfo);
|
||||
InliningStatus inlineToInteger(CallInfo &callInfo);
|
||||
|
|
|
@ -5789,6 +5789,16 @@ class LIsCallable : public LInstructionHelper<1, 1, 0>
|
|||
}
|
||||
};
|
||||
|
||||
class LIsObject : public LInstructionHelper<1, BOX_PIECES, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(IsObject);
|
||||
static const size_t Input = 0;
|
||||
MIsObject *mir() const {
|
||||
return mir_->toIsObject();
|
||||
}
|
||||
};
|
||||
|
||||
class LHaveSameClass : public LInstructionHelper<1, 2, 1>
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -282,6 +282,7 @@
|
|||
_(SetDOMProperty) \
|
||||
_(CallDOMNative) \
|
||||
_(IsCallable) \
|
||||
_(IsObject) \
|
||||
_(HaveSameClass) \
|
||||
_(HasClass) \
|
||||
_(AsmJSLoadHeap) \
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#ifndef jit_Label_h
|
||||
#define jit_Label_h
|
||||
|
||||
#include "jit/Ion.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
|
@ -25,10 +27,6 @@ struct LabelBase
|
|||
|
||||
LabelBase() : offset_(INVALID_OFFSET), bound_(false)
|
||||
{ }
|
||||
LabelBase(const LabelBase &label)
|
||||
: offset_(label.offset_),
|
||||
bound_(label.bound_)
|
||||
{ }
|
||||
|
||||
// If the label is bound, all incoming edges have been patched and any
|
||||
// future incoming edges will be immediately patched.
|
||||
|
@ -79,10 +77,20 @@ struct LabelBase
|
|||
class Label : public LabelBase
|
||||
{
|
||||
public:
|
||||
Label()
|
||||
{ }
|
||||
Label(const Label &label) : LabelBase(label)
|
||||
{ }
|
||||
~Label()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// The assertion below doesn't hold if an error occurred.
|
||||
if (OOM_counter > OOM_maxAllocations)
|
||||
return;
|
||||
if (IonContext *context = MaybeGetIonContext()) {
|
||||
if (context->runtime->hadOutOfMemory())
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!used());
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
// Label's destructor asserts that if it has been used it has also been bound.
|
||||
|
@ -90,6 +98,14 @@ class Label : public LabelBase
|
|||
// trigger this failure innocuously. This Label silences the assertion.
|
||||
class NonAssertingLabel : public Label
|
||||
{
|
||||
public:
|
||||
~NonAssertingLabel()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (used())
|
||||
bind(0);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} } // namespace js::jit
|
||||
|
|
|
@ -622,27 +622,58 @@ ReorderComparison(JSOp op, MDefinition **lhsp, MDefinition **rhsp)
|
|||
return op;
|
||||
}
|
||||
|
||||
static bool
|
||||
ShouldReorderCommutative(MDefinition *lhs, MDefinition *rhs, MInstruction *ins)
|
||||
{
|
||||
// lhs and rhs are used by the commutative operator.
|
||||
JS_ASSERT(lhs->hasDefUses());
|
||||
JS_ASSERT(rhs->hasDefUses());
|
||||
|
||||
// Ensure that if there is a constant, then it is in rhs.
|
||||
if (rhs->isConstant())
|
||||
return false;
|
||||
if (lhs->isConstant())
|
||||
return true;
|
||||
|
||||
// Since clobbering binary operations clobber the left operand, prefer a
|
||||
// non-constant lhs operand with no further uses. To be fully precise, we
|
||||
// should check whether this is the *last* use, but checking hasOneDefUse()
|
||||
// is a decent approximation which doesn't require any extra analysis.
|
||||
bool rhsSingleUse = rhs->hasOneDefUse();
|
||||
bool lhsSingleUse = lhs->hasOneDefUse();
|
||||
if (rhsSingleUse) {
|
||||
if (!lhsSingleUse)
|
||||
return true;
|
||||
} else {
|
||||
if (lhsSingleUse)
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this is a reduction-style computation, such as
|
||||
//
|
||||
// sum = 0;
|
||||
// for (...)
|
||||
// sum += ...;
|
||||
//
|
||||
// put the phi on the left to promote coalescing. This is fairly specific.
|
||||
if (rhsSingleUse &&
|
||||
rhs->isPhi() &&
|
||||
rhs->block()->isLoopHeader() &&
|
||||
ins == rhs->toPhi()->getLoopBackedgeOperand())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
ReorderCommutative(MDefinition **lhsp, MDefinition **rhsp)
|
||||
ReorderCommutative(MDefinition **lhsp, MDefinition **rhsp, MInstruction *ins)
|
||||
{
|
||||
MDefinition *lhs = *lhsp;
|
||||
MDefinition *rhs = *rhsp;
|
||||
|
||||
// Ensure that if there is a constant, then it is in rhs.
|
||||
// In addition, since clobbering binary operations clobber the left
|
||||
// operand, prefer a non-constant lhs operand with no further uses.
|
||||
|
||||
if (rhs->isConstant())
|
||||
return;
|
||||
|
||||
// lhs and rhs are used by the commutative operator. If they have any
|
||||
// *other* uses besides, try to reorder to avoid clobbering them. To
|
||||
// be fully precise, we should check whether this is the *last* use,
|
||||
// but checking hasOneDefUse() is a decent approximation which doesn't
|
||||
// require any extra analysis.
|
||||
JS_ASSERT(lhs->hasDefUses());
|
||||
JS_ASSERT(rhs->hasDefUses());
|
||||
if (lhs->isConstant() || (rhs->hasOneDefUse() && !lhs->hasOneDefUse())) {
|
||||
if (ShouldReorderCommutative(lhs, rhs, ins)) {
|
||||
*rhsp = lhs;
|
||||
*lhsp = rhs;
|
||||
}
|
||||
|
@ -828,7 +859,7 @@ LIRGenerator::visitTest(MTest *test)
|
|||
MDefinition *lhs = opd->getOperand(0);
|
||||
MDefinition *rhs = opd->getOperand(1);
|
||||
if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
|
||||
ReorderCommutative(&lhs, &rhs);
|
||||
ReorderCommutative(&lhs, &rhs, test);
|
||||
return lowerForBitAndAndBranch(new(alloc()) LBitAndAndBranch(ifTrue, ifFalse), test, lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
@ -1011,7 +1042,7 @@ LIRGenerator::lowerBitOp(JSOp op, MInstruction *ins)
|
|||
MDefinition *rhs = ins->getOperand(1);
|
||||
|
||||
if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
|
||||
ReorderCommutative(&lhs, &rhs);
|
||||
ReorderCommutative(&lhs, &rhs, ins);
|
||||
return lowerForALU(new(alloc()) LBitOpI(op), ins, lhs, rhs);
|
||||
}
|
||||
|
||||
|
@ -1228,7 +1259,7 @@ LIRGenerator::visitMinMax(MMinMax *ins)
|
|||
MDefinition *first = ins->getOperand(0);
|
||||
MDefinition *second = ins->getOperand(1);
|
||||
|
||||
ReorderCommutative(&first, &second);
|
||||
ReorderCommutative(&first, &second, ins);
|
||||
|
||||
if (ins->specialization() == MIRType_Int32) {
|
||||
LMinMaxI *lir = new(alloc()) LMinMaxI(useRegisterAtStart(first), useRegisterOrConstant(second));
|
||||
|
@ -1390,7 +1421,7 @@ LIRGenerator::visitAdd(MAdd *ins)
|
|||
|
||||
if (ins->specialization() == MIRType_Int32) {
|
||||
JS_ASSERT(lhs->type() == MIRType_Int32);
|
||||
ReorderCommutative(&lhs, &rhs);
|
||||
ReorderCommutative(&lhs, &rhs, ins);
|
||||
LAddI *lir = new(alloc()) LAddI;
|
||||
|
||||
if (ins->fallible() && !assignSnapshot(lir, Bailout_OverflowInvalidate))
|
||||
|
@ -1405,13 +1436,13 @@ LIRGenerator::visitAdd(MAdd *ins)
|
|||
|
||||
if (ins->specialization() == MIRType_Double) {
|
||||
JS_ASSERT(lhs->type() == MIRType_Double);
|
||||
ReorderCommutative(&lhs, &rhs);
|
||||
ReorderCommutative(&lhs, &rhs, ins);
|
||||
return lowerForFPU(new(alloc()) LMathD(JSOP_ADD), ins, lhs, rhs);
|
||||
}
|
||||
|
||||
if (ins->specialization() == MIRType_Float32) {
|
||||
JS_ASSERT(lhs->type() == MIRType_Float32);
|
||||
ReorderCommutative(&lhs, &rhs);
|
||||
ReorderCommutative(&lhs, &rhs, ins);
|
||||
return lowerForFPU(new(alloc()) LMathF(JSOP_ADD), ins, lhs, rhs);
|
||||
}
|
||||
|
||||
|
@ -1460,7 +1491,7 @@ LIRGenerator::visitMul(MMul *ins)
|
|||
|
||||
if (ins->specialization() == MIRType_Int32) {
|
||||
JS_ASSERT(lhs->type() == MIRType_Int32);
|
||||
ReorderCommutative(&lhs, &rhs);
|
||||
ReorderCommutative(&lhs, &rhs, ins);
|
||||
|
||||
// If our RHS is a constant -1 and we don't have to worry about
|
||||
// overflow, we can optimize to an LNegI.
|
||||
|
@ -1471,7 +1502,7 @@ LIRGenerator::visitMul(MMul *ins)
|
|||
}
|
||||
if (ins->specialization() == MIRType_Double) {
|
||||
JS_ASSERT(lhs->type() == MIRType_Double);
|
||||
ReorderCommutative(&lhs, &rhs);
|
||||
ReorderCommutative(&lhs, &rhs, ins);
|
||||
|
||||
// If our RHS is a constant -1.0, we can optimize to an LNegD.
|
||||
if (rhs->isConstant() && rhs->toConstant()->value() == DoubleValue(-1.0))
|
||||
|
@ -1481,7 +1512,7 @@ LIRGenerator::visitMul(MMul *ins)
|
|||
}
|
||||
if (ins->specialization() == MIRType_Float32) {
|
||||
JS_ASSERT(lhs->type() == MIRType_Float32);
|
||||
ReorderCommutative(&lhs, &rhs);
|
||||
ReorderCommutative(&lhs, &rhs, ins);
|
||||
|
||||
// We apply the same optimizations as for doubles
|
||||
if (rhs->isConstant() && rhs->toConstant()->value() == Float32Value(-1.0f))
|
||||
|
@ -3429,6 +3460,17 @@ LIRGenerator::visitIsCallable(MIsCallable *ins)
|
|||
return define(new(alloc()) LIsCallable(useRegister(ins->object())), ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitIsObject(MIsObject *ins)
|
||||
{
|
||||
MDefinition *opd = ins->input();
|
||||
JS_ASSERT(opd->type() == MIRType_Value);
|
||||
LIsObject *lir = new(alloc()) LIsObject();
|
||||
if (!useBoxAtStart(lir, LIsObject::Input, opd))
|
||||
return false;
|
||||
return define(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitHaveSameClass(MHaveSameClass *ins)
|
||||
{
|
||||
|
|
|
@ -245,6 +245,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
bool visitCallInstanceOf(MCallInstanceOf *ins);
|
||||
bool visitProfilerStackOp(MProfilerStackOp *ins);
|
||||
bool visitIsCallable(MIsCallable *ins);
|
||||
bool visitIsObject(MIsObject *ins);
|
||||
bool visitHaveSameClass(MHaveSameClass *ins);
|
||||
bool visitHasClass(MHasClass *ins);
|
||||
bool visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins);
|
||||
|
|
|
@ -163,6 +163,8 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target)
|
|||
return inlineHaveSameClass(callInfo);
|
||||
if (native == intrinsic_ToObject)
|
||||
return inlineToObject(callInfo);
|
||||
if (native == intrinsic_IsObject)
|
||||
return inlineIsObject(callInfo);
|
||||
if (native == intrinsic_ToInteger)
|
||||
return inlineToInteger(callInfo);
|
||||
if (native == intrinsic_ToString)
|
||||
|
@ -1902,6 +1904,21 @@ IonBuilder::inlineIsCallable(CallInfo &callInfo)
|
|||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineIsObject(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
if (getInlineReturnType() != MIRType_Boolean)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
MIsObject *isObject = MIsObject::New(alloc(), callInfo.getArg(0));
|
||||
current->add(isObject);
|
||||
current->push(isObject);
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineToObject(CallInfo &callInfo)
|
||||
{
|
||||
|
|
|
@ -909,6 +909,23 @@ MTypeBarrier::printOpcode(FILE *fp) const
|
|||
getOperand(0)->printName(fp);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
MPhi::assertLoopPhi() const
|
||||
{
|
||||
// getLoopPredecessorOperand and getLoopBackedgeOperand rely on these
|
||||
// predecessors being at indices 0 and 1.
|
||||
MBasicBlock *pred = block()->getPredecessor(0);
|
||||
MBasicBlock *back = block()->getPredecessor(1);
|
||||
JS_ASSERT(pred == block()->loopPredecessor());
|
||||
JS_ASSERT(pred->successorWithPhis() == block());
|
||||
JS_ASSERT(pred->positionInPhiSuccessor() == 0);
|
||||
JS_ASSERT(back == block()->backedge());
|
||||
JS_ASSERT(back->successorWithPhis() == block());
|
||||
JS_ASSERT(back->positionInPhiSuccessor() == 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
MPhi::removeOperand(size_t index)
|
||||
{
|
||||
|
@ -3065,6 +3082,19 @@ MSqrt::trySpecializeFloat32(TempAllocator &alloc) {
|
|||
setPolicyType(MIRType_Float32);
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
MBoundsCheck::foldsTo(TempAllocator &alloc)
|
||||
{
|
||||
if (index()->isConstant() && length()->isConstant()) {
|
||||
uint32_t len = length()->toConstant()->value().toInt32();
|
||||
uint32_t idx = index()->toConstant()->value().toInt32();
|
||||
if (idx + uint32_t(minimum()) < len && idx + uint32_t(maximum()) < len)
|
||||
return index();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
bool
|
||||
jit::ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id)
|
||||
{
|
||||
|
|
|
@ -5026,6 +5026,28 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineListNode<MPhi>
|
|||
}
|
||||
bool specializeType();
|
||||
|
||||
#ifdef DEBUG
|
||||
// Assert that this is a phi in a loop header with a unique predecessor and
|
||||
// a unique backedge.
|
||||
void assertLoopPhi() const;
|
||||
#else
|
||||
void assertLoopPhi() const {}
|
||||
#endif
|
||||
|
||||
// Assuming this phi is in a loop header with a unique loop entry, return
|
||||
// the phi operand along the loop entry.
|
||||
MDefinition *getLoopPredecessorOperand() const {
|
||||
assertLoopPhi();
|
||||
return getOperand(0);
|
||||
}
|
||||
|
||||
// Assuming this phi is in a loop header with a unique loop entry, return
|
||||
// the phi operand along the loop backedge.
|
||||
MDefinition *getLoopBackedgeOperand() const {
|
||||
assertLoopPhi();
|
||||
return getOperand(1);
|
||||
}
|
||||
|
||||
// Whether this phi's type already includes information for def.
|
||||
bool typeIncludes(MDefinition *def);
|
||||
|
||||
|
@ -6270,6 +6292,7 @@ class MBoundsCheck
|
|||
void setMaximum(int32_t n) {
|
||||
maximum_ = n;
|
||||
}
|
||||
MDefinition *foldsTo(TempAllocator &alloc);
|
||||
bool congruentTo(const MDefinition *ins) const {
|
||||
if (!ins->isBoundsCheck())
|
||||
return false;
|
||||
|
@ -8897,6 +8920,11 @@ class MStringLength
|
|||
}
|
||||
|
||||
void computeRange(TempAllocator &alloc);
|
||||
|
||||
bool writeRecoverData(CompactBufferWriter &writer) const;
|
||||
bool canRecoverOnBailout() const {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Inlined version of Math.floor().
|
||||
|
@ -8988,6 +9016,7 @@ class MCeil
|
|||
bool congruentTo(const MDefinition *ins) const {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
void computeRange(TempAllocator &alloc);
|
||||
};
|
||||
|
||||
// Inlined version of Math.round().
|
||||
|
@ -10093,7 +10122,32 @@ class MIsCallable
|
|||
static MIsCallable *New(TempAllocator &alloc, MDefinition *obj) {
|
||||
return new(alloc) MIsCallable(obj);
|
||||
}
|
||||
TypePolicy *typePolicy() {
|
||||
return this;
|
||||
}
|
||||
MDefinition *object() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
class MIsObject
|
||||
: public MUnaryInstruction,
|
||||
public BoxInputsPolicy
|
||||
{
|
||||
explicit MIsObject(MDefinition *object)
|
||||
: MUnaryInstruction(object)
|
||||
{
|
||||
setResultType(MIRType_Boolean);
|
||||
setMovable();
|
||||
}
|
||||
public:
|
||||
INSTRUCTION_HEADER(IsObject);
|
||||
static MIsObject *New(TempAllocator &alloc, MDefinition *obj) {
|
||||
return new(alloc) MIsObject(obj);
|
||||
}
|
||||
TypePolicy *typePolicy() {
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -197,6 +197,7 @@ namespace jit {
|
|||
_(GetDOMMember) \
|
||||
_(SetDOMProperty) \
|
||||
_(IsCallable) \
|
||||
_(IsObject) \
|
||||
_(HaveSameClass) \
|
||||
_(HasClass) \
|
||||
_(AsmJSNeg) \
|
||||
|
|
|
@ -306,6 +306,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
|
|||
SAFE_OP(FunctionDispatch)
|
||||
SAFE_OP(TypeObjectDispatch)
|
||||
SAFE_OP(IsCallable)
|
||||
SAFE_OP(IsObject)
|
||||
SAFE_OP(HaveSameClass)
|
||||
SAFE_OP(HasClass)
|
||||
UNSAFE_OP(EffectiveAddress)
|
||||
|
|
|
@ -563,7 +563,7 @@ Range::setDouble(double l, double h)
|
|||
hasInt32LowerBound_ = false;
|
||||
}
|
||||
if (h >= INT32_MIN && h <= INT32_MAX) {
|
||||
upper_ = int32_t(ceil(h));
|
||||
upper_ = int32_t(::ceil(h));
|
||||
hasInt32UpperBound_ = true;
|
||||
} else {
|
||||
upper_ = INT32_MAX;
|
||||
|
@ -979,6 +979,25 @@ Range::floor(TempAllocator &alloc, const Range *op)
|
|||
return copy;
|
||||
}
|
||||
|
||||
Range *
|
||||
Range::ceil(TempAllocator &alloc, const Range *op)
|
||||
{
|
||||
Range *copy = new(alloc) Range(*op);
|
||||
|
||||
// We need to refine max_exponent_ because ceil may have incremented the int value.
|
||||
// If we have got int32 bounds defined, just deduce it using the defined bounds.
|
||||
// Else we can just increment its value,
|
||||
// as we are looking to maintain an over estimation.
|
||||
if (copy->hasInt32Bounds())
|
||||
copy->max_exponent_ = copy->exponentImpliedByInt32Bounds();
|
||||
else if (copy->max_exponent_ < MaxFiniteExponent)
|
||||
copy->max_exponent_++;
|
||||
|
||||
copy->canHaveFractionalPart_ = false;
|
||||
copy->assertInvariants();
|
||||
return copy;
|
||||
}
|
||||
|
||||
bool
|
||||
Range::negativeZeroMul(const Range *lhs, const Range *rhs)
|
||||
{
|
||||
|
@ -1207,6 +1226,13 @@ MFloor::computeRange(TempAllocator &alloc)
|
|||
setRange(Range::floor(alloc, &other));
|
||||
}
|
||||
|
||||
void
|
||||
MCeil::computeRange(TempAllocator &alloc)
|
||||
{
|
||||
Range other(getOperand(0));
|
||||
setRange(Range::ceil(alloc, &other));
|
||||
}
|
||||
|
||||
void
|
||||
MMinMax::computeRange(TempAllocator &alloc)
|
||||
{
|
||||
|
@ -1697,13 +1723,13 @@ RangeAnalysis::analyzeLoopIterationCount(MBasicBlock *header,
|
|||
// The first operand of the phi should be the lhs' value at the start of
|
||||
// the first executed iteration, and not a value written which could
|
||||
// replace the second operand below during the middle of execution.
|
||||
MDefinition *lhsInitial = lhs.term->toPhi()->getOperand(0);
|
||||
MDefinition *lhsInitial = lhs.term->toPhi()->getLoopPredecessorOperand();
|
||||
if (lhsInitial->block()->isMarked())
|
||||
return nullptr;
|
||||
|
||||
// The second operand of the phi should be a value written by an add/sub
|
||||
// in every loop iteration, i.e. in a block which dominates the backedge.
|
||||
MDefinition *lhsWrite = lhs.term->toPhi()->getOperand(1);
|
||||
MDefinition *lhsWrite = lhs.term->toPhi()->getLoopBackedgeOperand();
|
||||
if (lhsWrite->isBeta())
|
||||
lhsWrite = lhsWrite->getOperand(0);
|
||||
if (!lhsWrite->isAdd() && !lhsWrite->isSub())
|
||||
|
@ -1783,17 +1809,11 @@ RangeAnalysis::analyzeLoopPhi(MBasicBlock *header, LoopIterationBound *loopBound
|
|||
|
||||
JS_ASSERT(phi->numOperands() == 2);
|
||||
|
||||
MBasicBlock *preLoop = header->loopPredecessor();
|
||||
JS_ASSERT(!preLoop->isMarked() && preLoop->successorWithPhis() == header);
|
||||
|
||||
MBasicBlock *backedge = header->backedge();
|
||||
JS_ASSERT(backedge->isMarked() && backedge->successorWithPhis() == header);
|
||||
|
||||
MDefinition *initial = phi->getOperand(preLoop->positionInPhiSuccessor());
|
||||
MDefinition *initial = phi->getLoopPredecessorOperand();
|
||||
if (initial->block()->isMarked())
|
||||
return;
|
||||
|
||||
SimpleLinearSum modified = ExtractLinearSum(phi->getOperand(backedge->positionInPhiSuccessor()));
|
||||
SimpleLinearSum modified = ExtractLinearSum(phi->getLoopBackedgeOperand());
|
||||
|
||||
if (modified.term != phi || modified.constant == 0)
|
||||
return;
|
||||
|
|
|
@ -415,6 +415,7 @@ class Range : public TempObject {
|
|||
static Range *min(TempAllocator &alloc, const Range *lhs, const Range *rhs);
|
||||
static Range *max(TempAllocator &alloc, const Range *lhs, const Range *rhs);
|
||||
static Range *floor(TempAllocator &alloc, const Range *op);
|
||||
static Range *ceil(TempAllocator &alloc, const Range *op);
|
||||
|
||||
static bool negativeZeroMul(const Range *lhs, const Range *rhs);
|
||||
|
||||
|
|
|
@ -510,6 +510,31 @@ RConcat::recover(JSContext *cx, SnapshotIterator &iter) const
|
|||
return true;
|
||||
}
|
||||
|
||||
RStringLength::RStringLength(CompactBufferReader &reader)
|
||||
{}
|
||||
|
||||
bool
|
||||
RStringLength::recover(JSContext *cx, SnapshotIterator &iter) const
|
||||
{
|
||||
RootedValue operand(cx, iter.read());
|
||||
RootedValue result(cx);
|
||||
|
||||
MOZ_ASSERT(!operand.isObject());
|
||||
if (!js::GetLengthProperty(operand, &result))
|
||||
return false;
|
||||
|
||||
iter.storeInstructionResult(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MStringLength::writeRecoverData(CompactBufferWriter &writer) const
|
||||
{
|
||||
MOZ_ASSERT(canRecoverOnBailout());
|
||||
writer.writeUnsigned(uint32_t(RInstruction::Recover_StringLength));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MFloor::writeRecoverData(CompactBufferWriter &writer) const
|
||||
{
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace jit {
|
|||
_(Div) \
|
||||
_(Mod) \
|
||||
_(Concat) \
|
||||
_(StringLength) \
|
||||
_(Floor) \
|
||||
_(Round) \
|
||||
_(CharCodeAt) \
|
||||
|
@ -274,6 +275,18 @@ class RConcat MOZ_FINAL : public RInstruction
|
|||
bool recover(JSContext *cx, SnapshotIterator &iter) const;
|
||||
};
|
||||
|
||||
class RStringLength MOZ_FINAL : public RInstruction
|
||||
{
|
||||
public:
|
||||
RINSTRUCTION_HEADER_(StringLength)
|
||||
|
||||
virtual uint32_t numOperands() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool recover(JSContext *cx, SnapshotIterator &iter) const;
|
||||
};
|
||||
|
||||
class RFloor MOZ_FINAL : public RInstruction
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -1508,6 +1508,12 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||
cond = testNull(cond, value);
|
||||
emitSet(cond, dest);
|
||||
}
|
||||
|
||||
void testObjectSet(Condition cond, const ValueOperand &value, Register dest) {
|
||||
cond = testObject(cond, value);
|
||||
emitSet(cond, dest);
|
||||
}
|
||||
|
||||
void testUndefinedSet(Condition cond, const ValueOperand &value, Register dest) {
|
||||
cond = testUndefined(cond, value);
|
||||
emitSet(cond, dest);
|
||||
|
|
|
@ -2298,6 +2298,12 @@ MacroAssemblerMIPSCompat::branchTestObject(Condition cond, const BaseIndex &src,
|
|||
ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_OBJECT), label, cond);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::testObjectSet(Condition cond, const ValueOperand &value, Register dest)
|
||||
{
|
||||
MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
||||
ma_cmp_set(dest, value.typeReg(), ImmType(JSVAL_TYPE_OBJECT), cond);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::branchTestString(Condition cond, const ValueOperand &value, Label *label)
|
||||
|
|
|
@ -686,6 +686,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
|||
void branchTestObject(Condition cond, const ValueOperand &value, Label *label);
|
||||
void branchTestObject(Condition cond, Register tag, Label *label);
|
||||
void branchTestObject(Condition cond, const BaseIndex &src, Label *label);
|
||||
void testObjectSet(Condition cond, const ValueOperand &value, Register dest);
|
||||
|
||||
void branchTestString(Condition cond, const ValueOperand &value, Label *label);
|
||||
void branchTestString(Condition cond, Register tag, Label *label);
|
||||
|
|
|
@ -1029,6 +1029,12 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
|||
cond = testNull(cond, value);
|
||||
emitSet(cond, dest);
|
||||
}
|
||||
|
||||
void testObjectSet(Condition cond, const ValueOperand &value, Register dest) {
|
||||
cond = testObject(cond, value);
|
||||
emitSet(cond, dest);
|
||||
}
|
||||
|
||||
void testUndefinedSet(Condition cond, const ValueOperand &value, Register dest) {
|
||||
cond = testUndefined(cond, value);
|
||||
emitSet(cond, dest);
|
||||
|
|
|
@ -486,6 +486,12 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
|||
cond = testNull(cond, value);
|
||||
emitSet(cond, dest);
|
||||
}
|
||||
|
||||
void testObjectSet(Condition cond, const ValueOperand &value, Register dest) {
|
||||
cond = testObject(cond, value);
|
||||
emitSet(cond, dest);
|
||||
}
|
||||
|
||||
void testUndefinedSet(Condition cond, const ValueOperand &value, Register dest) {
|
||||
cond = testUndefined(cond, value);
|
||||
emitSet(cond, dest);
|
||||
|
|
|
@ -480,6 +480,11 @@ js::Atomize(ExclusiveContext *cx, const char *bytes, size_t length, InternBehavi
|
|||
if (!JSString::validateLength(cx, length))
|
||||
return nullptr;
|
||||
|
||||
if (EnableLatin1Strings) {
|
||||
const Latin1Char *chars = reinterpret_cast<const Latin1Char*>(bytes);
|
||||
return AtomizeAndCopyChars(cx, chars, length, ib);
|
||||
}
|
||||
|
||||
static const unsigned ATOMIZE_BUF_MAX = 32;
|
||||
if (length < ATOMIZE_BUF_MAX) {
|
||||
/*
|
||||
|
|
|
@ -1007,6 +1007,7 @@ class ContextAllocPolicy
|
|||
|
||||
/* Exposed intrinsics so that Ion may inline them. */
|
||||
bool intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool intrinsic_IsObject(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool intrinsic_ToInteger(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool intrinsic_ToString(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
|
|
@ -308,7 +308,7 @@ CopyStringPure(JSContext *cx, JSString *str)
|
|||
if (!str->asRope().copyTwoByteCharsZ(cx, copiedChars))
|
||||
return nullptr;
|
||||
|
||||
return NewString<CanGC>(cx, copiedChars.forget(), len);
|
||||
return NewStringDontDeflate<CanGC>(cx, copiedChars.forget(), len);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -1540,7 +1540,6 @@ ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject
|
|||
}
|
||||
|
||||
// step 22
|
||||
// FIXME: This is incorrect with respect to [[Origin]]. See bug 999156.
|
||||
resultDesc.populatePropertyDescriptor(proxy, desc);
|
||||
return true;
|
||||
}
|
||||
|
@ -1567,8 +1566,7 @@ ScriptedDirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, Ha
|
|||
if (trap.isUndefined())
|
||||
return DirectProxyHandler::defineProperty(cx, proxy, id, desc);
|
||||
|
||||
// step 8-9, with 9 blatantly defied.
|
||||
// FIXME: This is incorrect with respect to [[Origin]]. See bug 601379.
|
||||
// step 8-9
|
||||
RootedValue descObj(cx);
|
||||
if (!NewPropertyDescriptorObject(cx, desc, &descObj))
|
||||
return false;
|
||||
|
|
115
js/src/jsstr.cpp
115
js/src/jsstr.cpp
|
@ -719,7 +719,7 @@ ToLowerCase(JSContext *cx, JSLinearString *str)
|
|||
newChars[length] = 0;
|
||||
}
|
||||
|
||||
JSString *res = NewString<CanGC>(cx, newChars.get(), length);
|
||||
JSString *res = NewStringDontDeflate<CanGC>(cx, newChars.get(), length);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
|
||||
|
@ -4205,16 +4205,10 @@ js::str_fromCharCode_one_arg(JSContext *cx, HandleValue code, MutableHandleValue
|
|||
return true;
|
||||
}
|
||||
|
||||
jschar *chars = cx->pod_malloc<jschar>(2);
|
||||
if (!chars)
|
||||
jschar c = jschar(ucode);
|
||||
JSString *str = NewStringCopyN<CanGC>(cx, &c, 1);
|
||||
if (!str)
|
||||
return false;
|
||||
chars[0] = jschar(ucode);
|
||||
chars[1] = 0;
|
||||
JSString *str = NewString<CanGC>(cx, chars, 1);
|
||||
if (!str) {
|
||||
js_free(chars);
|
||||
return false;
|
||||
}
|
||||
|
||||
rval.setString(str);
|
||||
return true;
|
||||
|
@ -4281,35 +4275,6 @@ js_InitStringClass(JSContext *cx, HandleObject obj)
|
|||
return proto;
|
||||
}
|
||||
|
||||
template <AllowGC allowGC, typename CharT>
|
||||
JSFlatString *
|
||||
js::NewString(ThreadSafeContext *cx, CharT *chars, size_t length)
|
||||
{
|
||||
if (length == 1) {
|
||||
jschar c = chars[0];
|
||||
if (StaticStrings::hasUnit(c)) {
|
||||
// Free |chars| because we're taking possession of it, but it's no
|
||||
// longer needed because we use the static string instead.
|
||||
js_free(chars);
|
||||
return cx->staticStrings().getUnit(c);
|
||||
}
|
||||
}
|
||||
|
||||
return JSFlatString::new_<allowGC>(cx, chars, length);
|
||||
}
|
||||
|
||||
template JSFlatString *
|
||||
js::NewString<CanGC>(ThreadSafeContext *cx, jschar *chars, size_t length);
|
||||
|
||||
template JSFlatString *
|
||||
js::NewString<NoGC>(ThreadSafeContext *cx, jschar *chars, size_t length);
|
||||
|
||||
template JSFlatString *
|
||||
js::NewString<CanGC>(ThreadSafeContext *cx, Latin1Char *chars, size_t length);
|
||||
|
||||
template JSFlatString *
|
||||
js::NewString<NoGC>(ThreadSafeContext *cx, Latin1Char *chars, size_t length);
|
||||
|
||||
JSLinearString *
|
||||
js::NewDependentString(JSContext *cx, JSString *baseArg, size_t start, size_t length)
|
||||
{
|
||||
|
@ -4415,7 +4380,7 @@ NewStringDeflated(ThreadSafeContext *cx, const jschar *s, size_t n)
|
|||
}
|
||||
news[n] = '\0';
|
||||
|
||||
JSFlatString *str = NewString<allowGC>(cx, news.get(), n);
|
||||
JSFlatString *str = JSFlatString::new_<allowGC>(cx, news.get(), n);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
|
||||
|
@ -4430,6 +4395,72 @@ NewStringDeflated(ThreadSafeContext *cx, const Latin1Char *s, size_t n)
|
|||
MOZ_CRASH("Shouldn't be called for Latin1 chars");
|
||||
}
|
||||
|
||||
template <AllowGC allowGC, typename CharT>
|
||||
JSFlatString *
|
||||
js::NewStringDontDeflate(ThreadSafeContext *cx, CharT *chars, size_t length)
|
||||
{
|
||||
if (length == 1) {
|
||||
jschar c = chars[0];
|
||||
if (StaticStrings::hasUnit(c)) {
|
||||
// Free |chars| because we're taking possession of it, but it's no
|
||||
// longer needed because we use the static string instead.
|
||||
js_free(chars);
|
||||
return cx->staticStrings().getUnit(c);
|
||||
}
|
||||
}
|
||||
|
||||
return JSFlatString::new_<allowGC>(cx, chars, length);
|
||||
}
|
||||
|
||||
template JSFlatString *
|
||||
js::NewStringDontDeflate<CanGC>(ThreadSafeContext *cx, jschar *chars, size_t length);
|
||||
|
||||
template JSFlatString *
|
||||
js::NewStringDontDeflate<NoGC>(ThreadSafeContext *cx, jschar *chars, size_t length);
|
||||
|
||||
template JSFlatString *
|
||||
js::NewStringDontDeflate<CanGC>(ThreadSafeContext *cx, Latin1Char *chars, size_t length);
|
||||
|
||||
template JSFlatString *
|
||||
js::NewStringDontDeflate<NoGC>(ThreadSafeContext *cx, Latin1Char *chars, size_t length);
|
||||
|
||||
template <AllowGC allowGC, typename CharT>
|
||||
JSFlatString *
|
||||
js::NewString(ThreadSafeContext *cx, CharT *chars, size_t length)
|
||||
{
|
||||
if (IsSame<CharT, jschar>::value && CanStoreCharsAsLatin1(chars, length)) {
|
||||
if (length == 1) {
|
||||
jschar c = chars[0];
|
||||
if (StaticStrings::hasUnit(c)) {
|
||||
js_free(chars);
|
||||
return cx->staticStrings().getUnit(c);
|
||||
}
|
||||
}
|
||||
|
||||
JSFlatString *s = NewStringDeflated<allowGC>(cx, chars, length);
|
||||
if (!s)
|
||||
return nullptr;
|
||||
|
||||
// Free |chars| because we're taking possession of it but not using it.
|
||||
js_free(chars);
|
||||
return s;
|
||||
}
|
||||
|
||||
return NewStringDontDeflate<allowGC>(cx, chars, length);
|
||||
}
|
||||
|
||||
template JSFlatString *
|
||||
js::NewString<CanGC>(ThreadSafeContext *cx, jschar *chars, size_t length);
|
||||
|
||||
template JSFlatString *
|
||||
js::NewString<NoGC>(ThreadSafeContext *cx, jschar *chars, size_t length);
|
||||
|
||||
template JSFlatString *
|
||||
js::NewString<CanGC>(ThreadSafeContext *cx, Latin1Char *chars, size_t length);
|
||||
|
||||
template JSFlatString *
|
||||
js::NewString<NoGC>(ThreadSafeContext *cx, Latin1Char *chars, size_t length);
|
||||
|
||||
namespace js {
|
||||
|
||||
template <AllowGC allowGC, typename CharT>
|
||||
|
@ -4447,7 +4478,7 @@ NewStringCopyNDontDeflate(ThreadSafeContext *cx, const CharT *s, size_t n)
|
|||
PodCopy(news.get(), s, n);
|
||||
news[n] = 0;
|
||||
|
||||
JSFlatString *str = NewString<allowGC>(cx, news.get(), n);
|
||||
JSFlatString *str = JSFlatString::new_<allowGC>(cx, news.get(), n);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
|
||||
|
@ -4465,7 +4496,7 @@ NewStringCopyNDontDeflate(ThreadSafeContext *cx, const CharT *s, size_t n)
|
|||
CopyCharsMaybeInflate(news.get(), s, n);
|
||||
news[n] = 0;
|
||||
|
||||
JSFlatString *str = NewString<allowGC>(cx, news.get(), n);
|
||||
JSFlatString *str = JSFlatString::new_<allowGC>(cx, news.get(), n);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -131,6 +131,11 @@ template <js::AllowGC allowGC, typename CharT>
|
|||
extern JSFlatString *
|
||||
NewString(js::ThreadSafeContext *cx, CharT *chars, size_t length);
|
||||
|
||||
/* Like NewString, but doesn't try to deflate to Latin1. */
|
||||
template <js::AllowGC allowGC, typename CharT>
|
||||
extern JSFlatString *
|
||||
NewStringDontDeflate(js::ThreadSafeContext *cx, CharT *chars, size_t length);
|
||||
|
||||
extern JSLinearString *
|
||||
NewDependentString(JSContext *cx, JSString *base, size_t start, size_t length);
|
||||
|
||||
|
|
|
@ -13,14 +13,14 @@ var summary = 'Handles';
|
|||
|
||||
var int32x4 = SIMD.int32x4;
|
||||
var a = int32x4((4294967295), 200, 300, 400);
|
||||
var c = SIMD.int32x4.bitsToFloat32x4(a);
|
||||
var c = SIMD.float32x4.fromInt32x4Bits(a);
|
||||
|
||||
// NaN canonicalization occurs when extracting out x lane:
|
||||
assertEq(c.x, NaN);
|
||||
|
||||
// but underlying bits are faithfully transmitted
|
||||
// (though reinterpreted as a signed integer):
|
||||
var d = SIMD.float32x4.bitsToInt32x4(c);
|
||||
var d = SIMD.int32x4.fromFloat32x4Bits(c);
|
||||
assertEq(d.x, -1);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
|
|
@ -3,7 +3,7 @@ var BUGNUMBER = 946042;
|
|||
var float32x4 = SIMD.float32x4;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'int32x4 toFloat32x4';
|
||||
var summary = 'float32x4 fromInt32x4';
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
@ -11,7 +11,7 @@ function test() {
|
|||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(1, 2, 3, 4);
|
||||
var c = SIMD.int32x4.toFloat32x4(a);
|
||||
var c = SIMD.float32x4.fromInt32x4(a);
|
||||
assertEq(c.x, 1);
|
||||
assertEq(c.y, 2);
|
||||
assertEq(c.z, 3);
|
|
@ -3,7 +3,7 @@ var BUGNUMBER = 946042;
|
|||
var float32x4 = SIMD.float32x4;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'int32x4 bitsToFloat32x4';
|
||||
var summary = 'float32x4 fromInt32x4Bits';
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
@ -11,7 +11,7 @@ function test() {
|
|||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = int32x4(100, 200, 300, 400);
|
||||
var c = SIMD.int32x4.bitsToFloat32x4(a);
|
||||
var c = SIMD.float32x4.fromInt32x4Bits(a);
|
||||
assertEq(c.x, 1.401298464324817e-43);
|
||||
assertEq(c.y, 2.802596928649634e-43);
|
||||
assertEq(c.z, 4.203895392974451e-43);
|
|
@ -3,7 +3,7 @@ var BUGNUMBER = 946042;
|
|||
var float32x4 = SIMD.float32x4;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'float32x4 toInt32x4';
|
||||
var summary = 'int32x4 fromFloat32x4';
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
@ -11,7 +11,7 @@ function test() {
|
|||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1.1, 2.2, 3.3, 4.6);
|
||||
var c = SIMD.float32x4.toInt32x4(a);
|
||||
var c = SIMD.int32x4.fromFloat32x4(a);
|
||||
assertEq(c.x, 1);
|
||||
assertEq(c.y, 2);
|
||||
assertEq(c.z, 3);
|
|
@ -3,7 +3,7 @@ var BUGNUMBER = 946042;
|
|||
var float32x4 = SIMD.float32x4;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'float32x4 bitsToInt32x4';
|
||||
var summary = 'int32x4 fromFloat32x4Bits';
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
@ -11,7 +11,7 @@ function test() {
|
|||
// FIXME -- Bug 948379: Amend to check for correctness of border cases.
|
||||
|
||||
var a = float32x4(1, 2, 3, 4);
|
||||
var c = SIMD.float32x4.bitsToInt32x4(a);
|
||||
var c = SIMD.int32x4.fromFloat32x4Bits(a);
|
||||
assertEq(c.x, 1065353216);
|
||||
assertEq(c.y, 1073741824);
|
||||
assertEq(c.z, 1077936128);
|
|
@ -63,6 +63,16 @@ js::intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intrinsic_IsObject(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
Value val = args[0];
|
||||
bool isObject = val.isObject();
|
||||
args.rval().setBoolean(isObject);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intrinsic_ToInteger(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
|
@ -766,6 +776,7 @@ intrinsic_RuntimeDefaultLocale(JSContext *cx, unsigned argc, Value *vp)
|
|||
|
||||
static const JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FN("ToObject", intrinsic_ToObject, 1,0),
|
||||
JS_FN("IsObject", intrinsic_IsObject, 1,0),
|
||||
JS_FN("ToInteger", intrinsic_ToInteger, 1,0),
|
||||
JS_FN("ToString", intrinsic_ToString, 1,0),
|
||||
JS_FN("IsCallable", intrinsic_IsCallable, 1,0),
|
||||
|
|
|
@ -77,7 +77,7 @@ FinishStringFlat(ExclusiveContext *cx, StringBuffer &sb, Buffer &cb)
|
|||
if (!buf)
|
||||
return nullptr;
|
||||
|
||||
JSFlatString *str = NewString<CanGC>(cx, buf.get(), len);
|
||||
JSFlatString *str = NewStringDontDeflate<CanGC>(cx, buf.get(), len);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ LOCAL_INCLUDES += [
|
|||
'/js/ipc',
|
||||
'/layout/base',
|
||||
'/layout/style',
|
||||
'/xpcom/reflect/xptinfo/src',
|
||||
'/xpcom/reflect/xptinfo',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_B2G_BT']:
|
||||
|
|
|
@ -4137,6 +4137,8 @@ PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush)
|
|||
NS_ASSERTION(!isSafeToFlush || mViewManager, "Must have view manager");
|
||||
// Make sure the view manager stays alive.
|
||||
nsRefPtr<nsViewManager> viewManagerDeathGrip = mViewManager;
|
||||
bool didStyleFlush = false;
|
||||
bool didLayoutFlush = false;
|
||||
if (isSafeToFlush && mViewManager) {
|
||||
// Processing pending notifications can kill us, and some callers only
|
||||
// hold weak refs when calling FlushPendingNotifications(). :(
|
||||
|
@ -4226,6 +4228,8 @@ PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush)
|
|||
mPresContext->RestyleManager()->ProcessPendingRestyles();
|
||||
}
|
||||
|
||||
didStyleFlush = true;
|
||||
|
||||
|
||||
// There might be more pending constructors now, but we're not going to
|
||||
// worry about them. They can't be triggered during reflow, so we should
|
||||
|
@ -4233,6 +4237,7 @@ PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush)
|
|||
|
||||
if (flushType >= (mSuppressInterruptibleReflows ? Flush_Layout : Flush_InterruptibleLayout) &&
|
||||
!mIsDestroying) {
|
||||
didLayoutFlush = true;
|
||||
mFrameConstructor->RecalcQuotesAndCounters();
|
||||
mViewManager->FlushDelayedResize(true);
|
||||
if (ProcessReflowCommands(flushType < Flush_Layout) && mContentToScrollTo) {
|
||||
|
@ -4243,11 +4248,6 @@ PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush)
|
|||
mContentToScrollTo = nullptr;
|
||||
}
|
||||
}
|
||||
} else if (!mIsDestroying && mSuppressInterruptibleReflows &&
|
||||
flushType == Flush_InterruptibleLayout) {
|
||||
// We suppressed this flush, but the document thinks it doesn't
|
||||
// need to flush anymore. Let it know what's really going on.
|
||||
mDocument->SetNeedLayoutFlush();
|
||||
}
|
||||
|
||||
if (flushType >= Flush_Layout) {
|
||||
|
@ -4256,6 +4256,19 @@ PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!didStyleFlush && flushType >= Flush_Style && !mIsDestroying) {
|
||||
mDocument->SetNeedStyleFlush();
|
||||
}
|
||||
|
||||
if (!didLayoutFlush && !mIsDestroying &&
|
||||
(flushType >=
|
||||
(mSuppressInterruptibleReflows ? Flush_Layout : Flush_InterruptibleLayout))) {
|
||||
// We suppressed this flush due to mSuppressInterruptibleReflows or
|
||||
// !isSafeToFlush, but the document thinks it doesn't
|
||||
// need to flush anymore. Let it know what's really going on.
|
||||
mDocument->SetNeedLayoutFlush();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -180,22 +180,22 @@ public:
|
|||
// mContentArea.BSize is not NS_UNCONSTRAINEDSIZE; otherwise
|
||||
// coordinate overflow may occur.
|
||||
mozilla::LogicalRect mContentArea;
|
||||
nscoord ContentIStart() {
|
||||
nscoord ContentIStart() const {
|
||||
return mContentArea.IStart(mReflowState.GetWritingMode());
|
||||
}
|
||||
nscoord ContentISize() {
|
||||
nscoord ContentISize() const {
|
||||
return mContentArea.ISize(mReflowState.GetWritingMode());
|
||||
}
|
||||
nscoord ContentIEnd() {
|
||||
nscoord ContentIEnd() const {
|
||||
return mContentArea.IEnd(mReflowState.GetWritingMode());
|
||||
}
|
||||
nscoord ContentBStart() {
|
||||
nscoord ContentBStart() const {
|
||||
return mContentArea.BStart(mReflowState.GetWritingMode());
|
||||
}
|
||||
nscoord ContentBSize() {
|
||||
nscoord ContentBSize() const {
|
||||
return mContentArea.BSize(mReflowState.GetWritingMode());
|
||||
}
|
||||
nscoord ContentBEnd() {
|
||||
nscoord ContentBEnd() const {
|
||||
return mContentArea.BEnd(mReflowState.GetWritingMode());
|
||||
}
|
||||
nscoord mContainerWidth;
|
||||
|
|
|
@ -8,7 +8,7 @@ $6 = (nsIContent * const) 0x0
|
|||
(gdb) bt 3
|
||||
#0 0xb6457185 in nsIContent::SetAttr (this=0x0, aNameSpaceID=0, aName=0xb0cb064c, aValue=..., aNotify=1) at ../../dist/include/nsIContent.h:285
|
||||
#1 0xb6b72072 in nsTreeColumns::RestoreNaturalOrder (this=0xaaf83cc0) at layout/xul/base/src/tree/src/nsTreeColumns.cpp:605
|
||||
#2 0xb736c76f in NS_InvokeByIndex_P () at xpcom/reflect/xptcall/src/md/unix/xptcinvoke_gcc_x86_unix.cpp:69
|
||||
#2 0xb736c76f in NS_InvokeByIndex_P () at xpcom/reflect/xptcall/md/unix/xptcinvoke_gcc_x86_unix.cpp:69
|
||||
(More stack frames follow...)
|
||||
(gdb) frame 1
|
||||
#1 0xb6b72072 in nsTreeColumns::RestoreNaturalOrder (this=0xaaf83cc0) at layout/xul/base/src/tree/src/nsTreeColumns.cpp:605
|
||||
|
|
|
@ -5,7 +5,7 @@ Program received signal SIGSEGV, Segmentation fault.
|
|||
610 mTree->Invalidate();
|
||||
(gdb) bt 3
|
||||
#0 0xb6b720a6 in nsTreeColumns::RestoreNaturalOrder (this=0xa947a580) at layout/xul/base/src/tree/src/nsTreeColumns.cpp:610
|
||||
#1 0xb736c76f in NS_InvokeByIndex_P () at xpcom/reflect/xptcall/src/md/unix/xptcinvoke_gcc_x86_unix.cpp:69
|
||||
#1 0xb736c76f in NS_InvokeByIndex_P () at xpcom/reflect/xptcall/md/unix/xptcinvoke_gcc_x86_unix.cpp:69
|
||||
#2 0xb6171901 in XPCWrappedNative::CallMethod (ccx=..., mode=XPCWrappedNative::CALL_METHOD)
|
||||
at js/src/xpconnect/src/xpcwrappednative.cpp:2722
|
||||
(More stack frames follow...)
|
||||
|
|
|
@ -7,7 +7,7 @@ Program received signal SIGSEGV, Segmentation fault.
|
|||
571 boxObject->GetElement(getter_AddRefs(element));
|
||||
(gdb) bt 3
|
||||
#0 0xb6b7463a in nsTreeContentView::SetTree (this=0xb0ba2510, aTree=0xaaecece0) at layout/xul/base/src/tree/src/nsTreeContentView.cpp:571
|
||||
#1 0xb736c76f in NS_InvokeByIndex_P () at xpcom/reflect/xptcall/src/md/unix/xptcinvoke_gcc_x86_unix.cpp:69
|
||||
#1 0xb736c76f in NS_InvokeByIndex_P () at xpcom/reflect/xptcall/md/unix/xptcinvoke_gcc_x86_unix.cpp:69
|
||||
#2 0xb6171901 in XPCWrappedNative::CallMethod (ccx=..., mode=XPCWrappedNative::CALL_METHOD)
|
||||
at js/src/xpconnect/src/xpcwrappednative.cpp:2722
|
||||
(More stack frames follow...)
|
||||
|
|
|
@ -14,21 +14,21 @@ Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, int buffer_size, unsigne
|
|||
/* buffer_size:
|
||||
* 1 - int8_t;
|
||||
* 2 - int16_t;
|
||||
* 3 - int32_t;
|
||||
* 4 - int64_t;
|
||||
* 4 - int32_t;
|
||||
* 8 - int64_t;
|
||||
*/
|
||||
long i;
|
||||
for(i = len-1; i >= 0; i--) {
|
||||
unsigned char x;
|
||||
if (buffer_size == 1) {
|
||||
x = (char)(*(const int8_t *)buffer_in >> (i * 8));
|
||||
} else if (buffer_size == 2) {
|
||||
} else if (buffer_size == 2) {
|
||||
x = (char)(*(const int16_t *)buffer_in >> (i * 8));
|
||||
} else if (buffer_size == 4) {
|
||||
} else if (buffer_size == 4) {
|
||||
x = (char)(*(const int32_t *)buffer_in >> (i * 8));
|
||||
} else if (buffer_size == 8) {
|
||||
} else if (buffer_size == 8) {
|
||||
x = (char)(*(const int64_t *)buffer_in >> (i * 8));
|
||||
}
|
||||
}
|
||||
Ebml_Write(glob, &x, 1);
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ void Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc, unsigned long clas
|
|||
Ebml_WriteID(glob, class_id);
|
||||
ebmlLoc->offset = glob->offset;
|
||||
// todo this is always taking 8 bytes, this may need later optimization
|
||||
Ebml_Serialize(glob, (void *)&unknownLen,sizeof(unknownLen), 8); // this is a key that says lenght unknown
|
||||
Ebml_Serialize(glob, (void *)&unknownLen,sizeof(unknownLen), 8); // this is a key that says length unknown
|
||||
}
|
||||
|
||||
void Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc) {
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
diff --git a/media/libmkv/EbmlBufferWriter.c b/media/libmkv/EbmlBufferWriter.c
|
||||
index 8c26e80..5925504 100644
|
||||
--- a/media/libmkv/EbmlBufferWriter.c
|
||||
+++ b/media/libmkv/EbmlBufferWriter.c
|
||||
@@ -14,21 +14,21 @@ Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in, int buffer_size, unsigne
|
||||
/* buffer_size:
|
||||
* 1 - int8_t;
|
||||
* 2 - int16_t;
|
||||
- * 3 - int32_t;
|
||||
- * 4 - int64_t;
|
||||
+ * 4 - int32_t;
|
||||
+ * 8 - int64_t;
|
||||
*/
|
||||
long i;
|
||||
for(i = len-1; i >= 0; i--) {
|
||||
unsigned char x;
|
||||
if (buffer_size == 1) {
|
||||
x = (char)(*(const int8_t *)buffer_in >> (i * 8));
|
||||
- } else if (buffer_size == 2) {
|
||||
+ } else if (buffer_size == 2) {
|
||||
x = (char)(*(const int16_t *)buffer_in >> (i * 8));
|
||||
- } else if (buffer_size == 4) {
|
||||
+ } else if (buffer_size == 4) {
|
||||
x = (char)(*(const int32_t *)buffer_in >> (i * 8));
|
||||
- } else if (buffer_size == 8) {
|
||||
+ } else if (buffer_size == 8) {
|
||||
x = (char)(*(const int64_t *)buffer_in >> (i * 8));
|
||||
- }
|
||||
+ }
|
||||
Ebml_Write(glob, &x, 1);
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ void Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc, unsigned long clas
|
||||
Ebml_WriteID(glob, class_id);
|
||||
ebmlLoc->offset = glob->offset;
|
||||
// todo this is always taking 8 bytes, this may need later optimization
|
||||
- Ebml_Serialize(glob, (void *)&unknownLen,sizeof(unknownLen), 8); // this is a key that says lenght unknown
|
||||
+ Ebml_Serialize(glob, (void *)&unknownLen,sizeof(unknownLen), 8); // this is a key that says length unknown
|
||||
}
|
||||
|
||||
void Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc) {
|
|
@ -36,3 +36,4 @@ patch -p1 < gecko_fix.patch
|
|||
patch -p1 < const_fix.patch
|
||||
patch -p3 < bock_fix.patch
|
||||
patch -p3 < bug970774.patch
|
||||
patch -p3 < cleanup.patch
|
||||
|
|
|
@ -102,8 +102,6 @@ add_test(function test_safebrowsing_update() {
|
|||
var streamUpdater = Cc["@mozilla.org/url-classifier/streamupdater;1"]
|
||||
.getService(Ci.nsIUrlClassifierStreamUpdater);
|
||||
|
||||
streamUpdater.updateUrl = URL + safebrowsingUpdatePath;
|
||||
|
||||
function onSuccess() {
|
||||
run_next_test();
|
||||
}
|
||||
|
@ -115,7 +113,7 @@ add_test(function test_safebrowsing_update() {
|
|||
}
|
||||
|
||||
streamUpdater.downloadUpdates("test-phish-simple,test-malware-simple", "",
|
||||
onSuccess, onUpdateError, onDownloadError);
|
||||
URL + safebrowsingUpdatePath, onSuccess, onUpdateError, onDownloadError);
|
||||
});
|
||||
|
||||
add_test(function test_non_safebrowsing_cookie() {
|
||||
|
|
|
@ -7,6 +7,7 @@ import datetime
|
|||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
|
@ -17,7 +18,7 @@ from .emulator_battery import EmulatorBattery
|
|||
from .emulator_geo import EmulatorGeo
|
||||
from .emulator_screen import EmulatorScreen
|
||||
from ..utils import uses_marionette
|
||||
from ..errors import TimeoutException, ScriptTimeoutException
|
||||
from ..errors import TimeoutException
|
||||
|
||||
class ArchContext(object):
|
||||
def __init__(self, arch, context, binary=None):
|
||||
|
@ -207,7 +208,13 @@ waitFor(
|
|||
function() { return isSystemMessageListenerReady(); }
|
||||
);
|
||||
""")
|
||||
except ScriptTimeoutException:
|
||||
except:
|
||||
# Look for ScriptTimeoutException this way to avoid a
|
||||
# dependency on the marionette python client.
|
||||
exc_name = sys.exc_info()[0].__name__
|
||||
if exc_name != 'ScriptTimeoutException':
|
||||
raise
|
||||
|
||||
print 'timed out'
|
||||
# We silently ignore the timeout if it occurs, since
|
||||
# isSystemMessageListenerReady() isn't available on
|
||||
|
|
|
@ -11,6 +11,3 @@ class RunnerNotStartedError(RunnerException):
|
|||
|
||||
class TimeoutException(RunnerException):
|
||||
"""Raised on timeout waiting for targets to start."""
|
||||
|
||||
class ScriptTimeoutException(RunnerException):
|
||||
"""Raised on timeout waiting for execute_script to finish."""
|
||||
|
|
|
@ -121,7 +121,8 @@ def main():
|
|||
|
||||
# Update config file
|
||||
config_in_path = os.path.join(here, 'config', 'config.json.in')
|
||||
replacements = {'__TESTDIR__': testdir, '__EXTENSIONDIR__': extdir}
|
||||
replacements = {'__TESTDIR__': testdir.replace('\\','/'),
|
||||
'__EXTENSIONDIR__': extdir.replace('\\','/')}
|
||||
if options.username and options.password:
|
||||
replacements.update({
|
||||
'__FX_ACCOUNT_USERNAME__': options.username,
|
||||
|
|
|
@ -150,9 +150,9 @@
|
|||
#define NS_URLCLASSIFIERDBSERVICE_CID \
|
||||
{ 0x8a389f21, 0xf821, 0x4e29, { 0x9c, 0x6b, 0x3d, 0xe6, 0xf3, 0x3c, 0xd7, 0xcf} }
|
||||
|
||||
// {79e6b710-ce68-4639-ac6b-7d293af424a1}
|
||||
// e1797597-f4d6-4dd3-a1e1-745ad352cd80
|
||||
#define NS_URLCLASSIFIERSTREAMUPDATER_CID \
|
||||
{ 0x79e6b710, 0xce68, 0x4639, { 0xac, 0x6b, 0x7d, 0x29, 0x3a, 0xf4, 0x24, 0xa1} }
|
||||
{ 0xe1797597, 0xf4d6, 0x4dd3, { 0xa1, 0xe1, 0x74, 0x5a, 0xd3, 0x52, 0xcd, 0x80 }}
|
||||
|
||||
// {b7b2ccec-7912-4ea6-a548-b038447004bd}
|
||||
#define NS_URLCLASSIFIERUTILS_CID \
|
||||
|
|
|
@ -205,7 +205,6 @@ add_test(function test_local_list() {
|
|||
|
||||
let streamUpdater = Cc["@mozilla.org/url-classifier/streamupdater;1"]
|
||||
.getService(Ci.nsIUrlClassifierStreamUpdater);
|
||||
streamUpdater.updateUrl = "http://localhost:4444/downloads";
|
||||
|
||||
// Load up some update chunks for the safebrowsing server to serve.
|
||||
// This chunk contains the hash of blocklisted.com/.
|
||||
|
@ -228,6 +227,7 @@ add_test(function test_local_list() {
|
|||
streamUpdater.downloadUpdates(
|
||||
"goog-downloadwhite-digest256,goog-badbinurl-shavar",
|
||||
"goog-downloadwhite-digest256,goog-badbinurl-shavar;\n",
|
||||
"http://localhost:4444/downloads",
|
||||
updateSuccess, handleError, handleError);
|
||||
});
|
||||
|
||||
|
|
|
@ -257,7 +257,6 @@ function waitForUpdates() {
|
|||
|
||||
let streamUpdater = Cc["@mozilla.org/url-classifier/streamupdater;1"]
|
||||
.getService(Ci.nsIUrlClassifierStreamUpdater);
|
||||
streamUpdater.updateUrl = "http://localhost:4444/downloads";
|
||||
|
||||
// Load up some update chunks for the safebrowsing server to serve. This
|
||||
// particular chunk contains the hash of whitelisted.com/ and
|
||||
|
@ -280,6 +279,7 @@ function waitForUpdates() {
|
|||
streamUpdater.downloadUpdates(
|
||||
"goog-downloadwhite-digest256",
|
||||
"goog-downloadwhite-digest256;\n",
|
||||
"http://localhost:4444/downloads",
|
||||
updateSuccess, handleError, handleError);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -318,7 +318,7 @@ Classifier::ApplyUpdates(nsTArray<TableUpdate*>* aUpdates)
|
|||
nsresult rv = BackupTables();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Applying table updates."));
|
||||
LOG(("Applying %d table updates.", aUpdates->Length()));
|
||||
|
||||
for (uint32_t i = 0; i < aUpdates->Length(); i++) {
|
||||
// Previous ApplyTableUpdates() may have consumed this update..
|
||||
|
@ -576,8 +576,9 @@ Classifier::ApplyTableUpdates(nsTArray<TableUpdate*>* aUpdates,
|
|||
|
||||
nsAutoPtr<HashStore> store(new HashStore(aTable, mStoreDirectory));
|
||||
|
||||
if (!store)
|
||||
if (!store) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// take the quick exit if there is no valid update for us
|
||||
// (common case)
|
||||
|
|
|
@ -54,16 +54,16 @@ this.SafeBrowsing = {
|
|||
let listManager = Cc["@mozilla.org/url-classifier/listmanager;1"].
|
||||
getService(Ci.nsIUrlListManager);
|
||||
for (let i = 0; i < phishingLists.length; ++i) {
|
||||
listManager.registerTable(phishingLists[i], false);
|
||||
listManager.registerTable(phishingLists[i], this.updateURL, this.gethashURL);
|
||||
}
|
||||
for (let i = 0; i < malwareLists.length; ++i) {
|
||||
listManager.registerTable(malwareLists[i], false);
|
||||
listManager.registerTable(malwareLists[i], this.updateURL, this.gethashURL);
|
||||
}
|
||||
for (let i = 0; i < downloadBlockLists.length; ++i) {
|
||||
listManager.registerTable(downloadBlockLists[i], false);
|
||||
listManager.registerTable(downloadBlockLists[i], this.updateURL, this.gethashURL);
|
||||
}
|
||||
for (let i = 0; i < downloadAllowLists.length; ++i) {
|
||||
listManager.registerTable(downloadAllowLists[i], false);
|
||||
listManager.registerTable(downloadAllowLists[i], this.updateURL, this.gethashURL);
|
||||
}
|
||||
this.addMozEntries();
|
||||
|
||||
|
@ -134,15 +134,8 @@ this.SafeBrowsing = {
|
|||
|
||||
this.updateURL = this.updateURL.replace("SAFEBROWSING_ID", clientID);
|
||||
this.gethashURL = this.gethashURL.replace("SAFEBROWSING_ID", clientID);
|
||||
|
||||
let listManager = Cc["@mozilla.org/url-classifier/listmanager;1"].
|
||||
getService(Ci.nsIUrlListManager);
|
||||
|
||||
listManager.setUpdateUrl(this.updateURL);
|
||||
listManager.setGethashUrl(this.gethashURL);
|
||||
},
|
||||
|
||||
|
||||
controlUpdateChecking: function() {
|
||||
log("phishingEnabled:", this.phishingEnabled, "malwareEnabled:", this.malwareEnabled);
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
// This is the only implementation of nsIUrlListManager.
|
||||
// A class that manages lists, namely white and black lists for
|
||||
// phishing or malware protection. The ListManager knows how to fetch,
|
||||
|
@ -13,6 +12,17 @@
|
|||
// TODO more comprehensive update tests, for example add unittest check
|
||||
// that the listmanagers tables are properly written on updates
|
||||
|
||||
// Log only if browser.safebrowsing.debug is true
|
||||
var debug = false;
|
||||
function log(...stuff) {
|
||||
if (!debug) {
|
||||
return;
|
||||
}
|
||||
|
||||
let msg = "listmanager: " + stuff.join(" ");
|
||||
dump(msg + "\n");
|
||||
}
|
||||
|
||||
function QueryAdapter(callback) {
|
||||
this.callback_ = callback;
|
||||
};
|
||||
|
@ -28,46 +38,31 @@ QueryAdapter.prototype.handleResponse = function(value) {
|
|||
* @constructor
|
||||
*/
|
||||
function PROT_ListManager() {
|
||||
this.debugZone = "listmanager";
|
||||
G_debugService.enableZone(this.debugZone);
|
||||
|
||||
this.currentUpdateChecker_ = null; // set when we toggle updates
|
||||
this.prefs_ = new G_Preferences();
|
||||
this.updateInterval = this.prefs_.getPref("urlclassifier.updateinterval", 30 * 60) * 1000;
|
||||
|
||||
this.updateserverURL_ = null;
|
||||
this.gethashURL_ = null;
|
||||
|
||||
this.isTesting_ = false;
|
||||
|
||||
// A map of tableNames to objects of type
|
||||
// { updateUrl: <updateUrl>, gethashUrl: <gethashUrl> }
|
||||
this.tablesData = {};
|
||||
// A map of updateUrls to maps of tables requiring updates, e.g.
|
||||
// { safebrowsing-update-url: { goog-phish-shavar: true,
|
||||
// goog-malware-shavar: true }
|
||||
this.needsUpdate_ = {};
|
||||
|
||||
this.observerServiceObserver_ = new G_ObserverServiceObserver(
|
||||
'quit-application',
|
||||
BindToObject(this.shutdown_, this),
|
||||
true /*only once*/);
|
||||
|
||||
this.cookieObserver_ = new G_ObserverServiceObserver(
|
||||
'cookie-changed',
|
||||
BindToObject(this.cookieChanged_, this),
|
||||
false);
|
||||
|
||||
/* Backoff interval should be between 30 and 60 minutes. */
|
||||
var backoffInterval = 30 * 60 * 1000;
|
||||
backoffInterval += Math.floor(Math.random() * (30 * 60 * 1000));
|
||||
|
||||
this.requestBackoff_ = new RequestBackoff(2 /* max errors */,
|
||||
60*1000 /* retry interval, 1 min */,
|
||||
4 /* num requests */,
|
||||
60*60*1000 /* request time, 60 min */,
|
||||
backoffInterval /* backoff interval, 60 min */,
|
||||
8*60*60*1000 /* max backoff, 8hr */);
|
||||
|
||||
this.updateCheckers_ = {};
|
||||
this.requestBackoffs_ = {};
|
||||
this.dbService_ = Cc["@mozilla.org/url-classifier/dbservice;1"]
|
||||
.getService(Ci.nsIUrlClassifierDBService);
|
||||
|
||||
|
||||
this.hashCompleter_ = Cc["@mozilla.org/url-classifier/hashcompleter;1"]
|
||||
.getService(Ci.nsIUrlClassifierHashCompleter);
|
||||
debug = this.prefs_.getPref("browser.safebrowsing.debug");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,53 +75,53 @@ PROT_ListManager.prototype.shutdown_ = function() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the url we check for updates. If the new url is valid and different,
|
||||
* update our table list.
|
||||
*
|
||||
* After setting the update url, the caller is responsible for registering
|
||||
* tables and then toggling update checking. All the code for this logic is
|
||||
* currently in browser/components/safebrowsing. Maybe it should be part of
|
||||
* the listmanger?
|
||||
*/
|
||||
PROT_ListManager.prototype.setUpdateUrl = function(url) {
|
||||
G_Debug(this, "Set update url: " + url);
|
||||
if (url != this.updateserverURL_) {
|
||||
this.updateserverURL_ = url;
|
||||
this.requestBackoff_.reset();
|
||||
|
||||
// Remove old tables which probably aren't valid for the new provider.
|
||||
for (var name in this.tablesData) {
|
||||
delete this.tablesData[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the gethash url.
|
||||
*/
|
||||
PROT_ListManager.prototype.setGethashUrl = function(url) {
|
||||
G_Debug(this, "Set gethash url: " + url);
|
||||
if (url != this.gethashURL_) {
|
||||
this.gethashURL_ = url;
|
||||
this.hashCompleter_.gethashUrl = url;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new table table
|
||||
* @param tableName - the name of the table
|
||||
* @param opt_requireMac true if a mac is required on update, false otherwise
|
||||
* @param updateUrl - the url for updating the table
|
||||
* @param gethashUrl - the url for fetching hash completions
|
||||
* @returns true if the table could be created; false otherwise
|
||||
*/
|
||||
PROT_ListManager.prototype.registerTable = function(tableName,
|
||||
opt_requireMac) {
|
||||
PROT_ListManager.prototype.registerTable = function(tableName,
|
||||
updateUrl,
|
||||
gethashUrl) {
|
||||
log("registering " + tableName + " with " + updateUrl);
|
||||
if (!updateUrl) {
|
||||
log("Can't register table " + tableName + " without updateUrl");
|
||||
return false;
|
||||
}
|
||||
this.tablesData[tableName] = {};
|
||||
this.tablesData[tableName].needsUpdate = false;
|
||||
this.tablesData[tableName].updateUrl = updateUrl;
|
||||
this.tablesData[tableName].gethashUrl = gethashUrl;
|
||||
|
||||
// Keep track of all of our update URLs.
|
||||
if (!this.needsUpdate_[updateUrl]) {
|
||||
this.needsUpdate_[updateUrl] = {};
|
||||
/* Backoff interval should be between 30 and 60 minutes. */
|
||||
var backoffInterval = 30 * 60 * 1000;
|
||||
backoffInterval += Math.floor(Math.random() * (30 * 60 * 1000));
|
||||
|
||||
log("Creating request backoff for " + updateUrl);
|
||||
this.requestBackoffs_[updateUrl] = new RequestBackoff(2 /* max errors */,
|
||||
60*1000 /* retry interval, 1 min */,
|
||||
4 /* num requests */,
|
||||
60*60*1000 /* request time, 60 min */,
|
||||
backoffInterval /* backoff interval, 60 min */,
|
||||
8*60*60*1000 /* max backoff, 8hr */);
|
||||
|
||||
}
|
||||
this.needsUpdate_[updateUrl][tableName] = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PROT_ListManager.prototype.getGethashUrl = function(tableName) {
|
||||
if (this.tablesData[tableName] && this.tablesData[tableName].gethashUrl) {
|
||||
return this.tablesData[tableName].gethashUrl;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable updates for some tables
|
||||
* @param tables - an array of table names that need updating
|
||||
|
@ -135,13 +130,14 @@ PROT_ListManager.prototype.enableUpdate = function(tableName) {
|
|||
var changed = false;
|
||||
var table = this.tablesData[tableName];
|
||||
if (table) {
|
||||
G_Debug(this, "Enabling table updates for " + tableName);
|
||||
table.needsUpdate = true;
|
||||
log("Enabling table updates for " + tableName);
|
||||
this.needsUpdate_[table.updateUrl][tableName] = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed === true)
|
||||
if (changed) {
|
||||
this.maybeToggleUpdateChecking();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,42 +148,30 @@ PROT_ListManager.prototype.disableUpdate = function(tableName) {
|
|||
var changed = false;
|
||||
var table = this.tablesData[tableName];
|
||||
if (table) {
|
||||
G_Debug(this, "Disabling table updates for " + tableName);
|
||||
table.needsUpdate = false;
|
||||
log("Disabling table updates for " + tableName);
|
||||
this.needsUpdate_[table.updateUrl][tableName] = false;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed === true)
|
||||
if (changed) {
|
||||
this.maybeToggleUpdateChecking();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we have some tables that need updating.
|
||||
*/
|
||||
PROT_ListManager.prototype.requireTableUpdates = function() {
|
||||
for (var type in this.tablesData) {
|
||||
// Tables that need updating even if other tables dont require it
|
||||
if (this.tablesData[type].needsUpdate)
|
||||
for (var name in this.tablesData) {
|
||||
// Tables that need updating even if other tables don't require it
|
||||
if (this.needsUpdate_[this.tablesData[name].updateUrl][name]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start managing the lists we know about. We don't do this automatically
|
||||
* when the listmanager is instantiated because their profile directory
|
||||
* (where we store the lists) might not be available.
|
||||
*/
|
||||
PROT_ListManager.prototype.maybeStartManagingUpdates = function() {
|
||||
if (this.isTesting_)
|
||||
return;
|
||||
|
||||
// We might have been told about tables already, so see if we should be
|
||||
// actually updating.
|
||||
this.maybeToggleUpdateChecking();
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts as a nsIUrlClassifierCallback for getTables.
|
||||
*/
|
||||
|
@ -197,25 +181,40 @@ PROT_ListManager.prototype.kickoffUpdate_ = function (onDiskTableData)
|
|||
var initialUpdateDelay = 3000;
|
||||
|
||||
// Check if any table registered for updates has ever been downloaded.
|
||||
var diskTablesAreUpdating = false;
|
||||
var updatingExisting = false;
|
||||
for (var tableName in this.tablesData) {
|
||||
if (this.tablesData[tableName].needsUpdate) {
|
||||
if (this.needsUpdate_[this.tablesData[tableName].updateUrl][tableName]) {
|
||||
if (onDiskTableData.indexOf(tableName) != -1) {
|
||||
diskTablesAreUpdating = true;
|
||||
updatingExisting = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the user has never downloaded tables, do the check now.
|
||||
// If the user has tables, add a fuzz of a few minutes.
|
||||
if (diskTablesAreUpdating) {
|
||||
// Add a fuzz of 0-5 minutes.
|
||||
initialUpdateDelay += Math.floor(Math.random() * (5 * 60 * 1000));
|
||||
log("needsUpdate: " + JSON.stringify(this.needsUpdate_, undefined, 2));
|
||||
for (var url in this.needsUpdate_) {
|
||||
// If the user has tables, add a fuzz of a few minutes.
|
||||
if (updatingExisting) {
|
||||
// Add a fuzz of 0-5 minutes.
|
||||
initialUpdateDelay += Math.floor(Math.random() * (5 * 60 * 1000));
|
||||
}
|
||||
// If we haven't already kicked off updates for this updateUrl, set a
|
||||
// repeating timer for it. The delay will be reset either on updateSuccess
|
||||
// to this.updateinterval, or backed off on downloadError.
|
||||
if (!this.updateCheckers_[url]) {
|
||||
this.updateCheckers_[url] =
|
||||
new G_Alarm(BindToObject(this.checkForUpdates, this, url),
|
||||
initialUpdateDelay, true /* repeating */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.currentUpdateChecker_ =
|
||||
new G_Alarm(BindToObject(this.checkForUpdates, this),
|
||||
initialUpdateDelay);
|
||||
PROT_ListManager.prototype.stopUpdateCheckers = function() {
|
||||
log("Stopping updates");
|
||||
for (var updateUrl in this.updateCheckers_) {
|
||||
this.updateCheckers_[updateUrl].cancel();
|
||||
this.updateCheckers_[updateUrl] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,70 +222,21 @@ PROT_ListManager.prototype.kickoffUpdate_ = function (onDiskTableData)
|
|||
* Wardens may call us with new tables that need to be updated.
|
||||
*/
|
||||
PROT_ListManager.prototype.maybeToggleUpdateChecking = function() {
|
||||
// If we are testing or dont have an application directory yet, we should
|
||||
// not start reading tables from disk or schedule remote updates
|
||||
if (this.isTesting_)
|
||||
return;
|
||||
|
||||
// We update tables if we have some tables that want updates. If there
|
||||
// are no tables that want to be updated - we dont need to check anything.
|
||||
if (this.requireTableUpdates() === true) {
|
||||
G_Debug(this, "Starting managing lists");
|
||||
this.startUpdateChecker();
|
||||
if (this.requireTableUpdates()) {
|
||||
log("Starting managing lists");
|
||||
|
||||
// Multiple warden can ask us to reenable updates at the same time, but we
|
||||
// really just need to schedule a single update.
|
||||
if (!this.currentUpdateChecker && !this.startingUpdate_) {
|
||||
// Get the list of existing tables from the DBService before making any
|
||||
// update requests.
|
||||
if (!this.startingUpdate_) {
|
||||
this.startingUpdate_ = true;
|
||||
// check the current state of tables in the database
|
||||
this.dbService_.getTables(BindToObject(this.kickoffUpdate_, this));
|
||||
}
|
||||
} else {
|
||||
G_Debug(this, "Stopping managing lists (if currently active)");
|
||||
this.stopUpdateChecker(); // Cancel pending updates
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start periodic checks for updates. Idempotent.
|
||||
* We want to distribute update checks evenly across the update period (an
|
||||
* hour). The first update is scheduled for a random time between 0.5 and 1.5
|
||||
* times the update interval.
|
||||
*/
|
||||
PROT_ListManager.prototype.startUpdateChecker = function() {
|
||||
this.stopUpdateChecker();
|
||||
|
||||
// Schedule the first check for between 15 and 45 minutes.
|
||||
var repeatingUpdateDelay = this.updateInterval / 2;
|
||||
repeatingUpdateDelay += Math.floor(Math.random() * this.updateInterval);
|
||||
this.updateChecker_ = new G_Alarm(BindToObject(this.initialUpdateCheck_,
|
||||
this),
|
||||
repeatingUpdateDelay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the first update check.
|
||||
* We go ahead and check for table updates, then start a regular timer (once
|
||||
* every update interval).
|
||||
*/
|
||||
PROT_ListManager.prototype.initialUpdateCheck_ = function() {
|
||||
this.checkForUpdates();
|
||||
this.updateChecker_ = new G_Alarm(BindToObject(this.checkForUpdates, this),
|
||||
this.updateInterval, true /* repeat */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop checking for updates. Idempotent.
|
||||
*/
|
||||
PROT_ListManager.prototype.stopUpdateChecker = function() {
|
||||
if (this.updateChecker_) {
|
||||
this.updateChecker_.cancel();
|
||||
this.updateChecker_ = null;
|
||||
}
|
||||
// Cancel the oneoff check from maybeToggleUpdateChecking.
|
||||
if (this.currentUpdateChecker_) {
|
||||
this.currentUpdateChecker_.cancel();
|
||||
this.currentUpdateChecker_ = null;
|
||||
log("Stopping managing lists (if currently active)");
|
||||
this.stopUpdateCheckers(); // Cancel pending updates
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -303,13 +253,13 @@ PROT_ListManager.prototype.stopUpdateChecker = function() {
|
|||
*/
|
||||
PROT_ListManager.prototype.safeLookup = function(key, callback) {
|
||||
try {
|
||||
G_Debug(this, "safeLookup: " + key);
|
||||
log("safeLookup: " + key);
|
||||
var cb = new QueryAdapter(callback);
|
||||
this.dbService_.lookup(key,
|
||||
BindToObject(cb.handleResponse, cb),
|
||||
true);
|
||||
} catch(e) {
|
||||
G_Debug(this, "safeLookup masked failure for key " + key + ": " + e);
|
||||
log("safeLookup masked failure for key " + key + ": " + e);
|
||||
callback.handleEvent("");
|
||||
}
|
||||
}
|
||||
|
@ -317,24 +267,22 @@ PROT_ListManager.prototype.safeLookup = function(key, callback) {
|
|||
/**
|
||||
* Updates our internal tables from the update server
|
||||
*
|
||||
* @returns true when a new request was scheduled, false if an old request
|
||||
* was still pending.
|
||||
* @param updateUrl: request updates for tables associated with that url, or
|
||||
* for all tables if the url is empty.
|
||||
*/
|
||||
PROT_ListManager.prototype.checkForUpdates = function() {
|
||||
// Allow new updates to be scheduled from maybeToggleUpdateChecking()
|
||||
this.currentUpdateChecker_ = null;
|
||||
|
||||
if (!this.updateserverURL_) {
|
||||
G_Debug(this, 'checkForUpdates: no update server url');
|
||||
PROT_ListManager.prototype.checkForUpdates = function(updateUrl) {
|
||||
log("checkForUpdates with " + updateUrl);
|
||||
// See if we've triggered the request backoff logic.
|
||||
if (!updateUrl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// See if we've triggered the request backoff logic.
|
||||
if (!this.requestBackoff_.canMakeRequest())
|
||||
if (!this.requestBackoffs_[updateUrl] ||
|
||||
!this.requestBackoffs_[updateUrl].canMakeRequest()) {
|
||||
return false;
|
||||
|
||||
}
|
||||
// Grab the current state of the tables from the database
|
||||
this.dbService_.getTables(BindToObject(this.makeUpdateRequest_, this));
|
||||
this.dbService_.getTables(BindToObject(this.makeUpdateRequest_, this,
|
||||
updateUrl));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -344,56 +292,76 @@ PROT_ListManager.prototype.checkForUpdates = function() {
|
|||
* @param tableData List of table data already in the database, in the form
|
||||
* tablename;<chunk ranges>\n
|
||||
*/
|
||||
PROT_ListManager.prototype.makeUpdateRequest_ = function(tableData) {
|
||||
var tableList;
|
||||
var tableNames = {};
|
||||
PROT_ListManager.prototype.makeUpdateRequest_ = function(updateUrl, tableData) {
|
||||
log("this.tablesData: " + JSON.stringify(this.tablesData, undefined, 2));
|
||||
log("existing chunks: " + tableData + "\n");
|
||||
// Disallow blank updateUrls
|
||||
if (!updateUrl) {
|
||||
return;
|
||||
}
|
||||
// An object of the form
|
||||
// { tableList: comma-separated list of tables to request,
|
||||
// tableNames: map of tables that need updating,
|
||||
// request: list of tables and existing chunk ranges from tableData
|
||||
// }
|
||||
var streamerMap = { tableList: null, tableNames: {}, request: "" };
|
||||
for (var tableName in this.tablesData) {
|
||||
if (this.tablesData[tableName].needsUpdate)
|
||||
tableNames[tableName] = true;
|
||||
if (!tableList) {
|
||||
tableList = tableName;
|
||||
// Skip tables not matching this update url
|
||||
if (this.tablesData[tableName].updateUrl != updateUrl) {
|
||||
continue;
|
||||
}
|
||||
if (this.needsUpdate_[this.tablesData[tableName].updateUrl][tableName]) {
|
||||
streamerMap.tableNames[tableName] = true;
|
||||
}
|
||||
if (!streamerMap.tableList) {
|
||||
streamerMap.tableList = tableName;
|
||||
} else {
|
||||
tableList += "," + tableName;
|
||||
streamerMap.tableList += "," + tableName;
|
||||
}
|
||||
}
|
||||
|
||||
var request = "";
|
||||
|
||||
// For each table already in the database, include the chunk data from
|
||||
// the database
|
||||
// Build the request. For each table already in the database, include the
|
||||
// chunk data from the database
|
||||
var lines = tableData.split("\n");
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var fields = lines[i].split(";");
|
||||
if (tableNames[fields[0]]) {
|
||||
request += lines[i] + "\n";
|
||||
delete tableNames[fields[0]];
|
||||
var name = fields[0];
|
||||
if (streamerMap.tableNames[name]) {
|
||||
streamerMap.request += lines[i] + "\n";
|
||||
delete streamerMap.tableNames[name];
|
||||
}
|
||||
}
|
||||
|
||||
// For each requested table that didn't have chunk data in the database,
|
||||
// request it fresh
|
||||
for (var tableName in tableNames) {
|
||||
request += tableName + ";\n";
|
||||
for (let tableName in streamerMap.tableNames) {
|
||||
streamerMap.request += tableName + ";\n";
|
||||
}
|
||||
|
||||
G_Debug(this, 'checkForUpdates: scheduling request..');
|
||||
log("update request: " + JSON.stringify(streamerMap, undefined, 2) + "\n");
|
||||
|
||||
if (Object.keys(streamerMap.tableNames).length > 0) {
|
||||
this.makeUpdateRequestForEntry_(updateUrl, streamerMap.tableList,
|
||||
streamerMap.request);
|
||||
}
|
||||
}
|
||||
|
||||
PROT_ListManager.prototype.makeUpdateRequestForEntry_ = function(updateUrl,
|
||||
tableList,
|
||||
request) {
|
||||
log("makeUpdateRequestForEntry_: request " + request + " update: " +
|
||||
updateUrl + " tablelist: " + tableList + "\n");
|
||||
var streamer = Cc["@mozilla.org/url-classifier/streamupdater;1"]
|
||||
.getService(Ci.nsIUrlClassifierStreamUpdater);
|
||||
try {
|
||||
streamer.updateUrl = this.updateserverURL_;
|
||||
} catch (e) {
|
||||
G_Debug(this, 'invalid url');
|
||||
return;
|
||||
}
|
||||
|
||||
this.requestBackoff_.noteRequest();
|
||||
this.requestBackoffs_[updateUrl].noteRequest();
|
||||
|
||||
if (!streamer.downloadUpdates(tableList,
|
||||
request,
|
||||
BindToObject(this.updateSuccess_, this),
|
||||
BindToObject(this.updateError_, this),
|
||||
BindToObject(this.downloadError_, this))) {
|
||||
G_Debug(this, "pending update, wait until later");
|
||||
if (!streamer.downloadUpdates(
|
||||
tableList,
|
||||
request,
|
||||
updateUrl,
|
||||
BindToObject(this.updateSuccess_, this, tableList, updateUrl),
|
||||
BindToObject(this.updateError_, this, tableList, updateUrl),
|
||||
BindToObject(this.downloadError_, this, tableList, updateUrl))) {
|
||||
log("pending update, wait until later");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -402,26 +370,34 @@ PROT_ListManager.prototype.makeUpdateRequest_ = function(tableData) {
|
|||
* @param waitForUpdate String The number of seconds that the client should
|
||||
* wait before requesting again.
|
||||
*/
|
||||
PROT_ListManager.prototype.updateSuccess_ = function(waitForUpdate) {
|
||||
G_Debug(this, "update success: " + waitForUpdate);
|
||||
PROT_ListManager.prototype.updateSuccess_ = function(tableList, updateUrl,
|
||||
waitForUpdate) {
|
||||
log("update success for " + tableList + " from " + updateUrl + ": " +
|
||||
waitForUpdate + "\n");
|
||||
if (waitForUpdate) {
|
||||
var delay = parseInt(waitForUpdate, 10);
|
||||
// As long as the delay is something sane (5 minutes or more), update
|
||||
// our delay time for requesting updates
|
||||
if (delay >= (5 * 60) && this.updateChecker_)
|
||||
this.updateChecker_.setDelay(delay * 1000);
|
||||
// our delay time for requesting updates. Setting the delay requires a
|
||||
// repeating timer, so always use one.
|
||||
if (delay >= (5 * 60) && this.updateCheckers_[updateUrl]) {
|
||||
log("Waiting " + delay);
|
||||
this.updateCheckers_[updateUrl].setDelay(delay * 1000);
|
||||
} else {
|
||||
log("Ignoring delay from server, waiting " + this.updateInterval);
|
||||
this.updateCheckers_[updateUrl].setDelay(this.updateInterval);
|
||||
}
|
||||
}
|
||||
|
||||
// Let the backoff object know that we completed successfully.
|
||||
this.requestBackoff_.noteServerResponse(200);
|
||||
this.requestBackoffs_[updateUrl].noteServerResponse(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function if the update request succeeded.
|
||||
* @param result String The error code of the failure
|
||||
*/
|
||||
PROT_ListManager.prototype.updateError_ = function(result) {
|
||||
G_Debug(this, "update error: " + result);
|
||||
PROT_ListManager.prototype.updateError_ = function(table, updateUrl, result) {
|
||||
log("update error for " + table + " from " + updateUrl + ": " + result + "\n");
|
||||
// XXX: there was some trouble applying the updates.
|
||||
}
|
||||
|
||||
|
@ -429,34 +405,22 @@ PROT_ListManager.prototype.updateError_ = function(result) {
|
|||
* Callback function when the download failed
|
||||
* @param status String http status or an empty string if connection refused.
|
||||
*/
|
||||
PROT_ListManager.prototype.downloadError_ = function(status) {
|
||||
G_Debug(this, "download error: " + status);
|
||||
PROT_ListManager.prototype.downloadError_ = function(table, updateUrl, status) {
|
||||
log("download error for " + table + ": " + status + "\n");
|
||||
// If status is empty, then we assume that we got an NS_CONNECTION_REFUSED
|
||||
// error. In this case, we treat this is a http 500 error.
|
||||
if (!status) {
|
||||
status = 500;
|
||||
}
|
||||
status = parseInt(status, 10);
|
||||
this.requestBackoff_.noteServerResponse(status);
|
||||
|
||||
if (this.requestBackoff_.isErrorStatus(status)) {
|
||||
this.requestBackoffs_[updateUrl].noteServerResponse(status);
|
||||
if (this.requestBackoffs_[updateUrl].isErrorStatus(status)) {
|
||||
// Schedule an update for when our backoff is complete
|
||||
this.currentUpdateChecker_ =
|
||||
new G_Alarm(BindToObject(this.checkForUpdates, this),
|
||||
this.requestBackoff_.nextRequestDelay());
|
||||
this.updateCheckers_[updateUrl].setDelay(
|
||||
this.requestBackoffs_[updateUrl].nextRequestDelay());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when cookies are cleared
|
||||
*/
|
||||
PROT_ListManager.prototype.cookieChanged_ = function(subject, topic, data) {
|
||||
if (data != "cleared")
|
||||
return;
|
||||
|
||||
G_Debug(this, "cookies cleared");
|
||||
}
|
||||
|
||||
PROT_ListManager.prototype.QueryInterface = function(iid) {
|
||||
if (iid.equals(Ci.nsISupports) ||
|
||||
iid.equals(Ci.nsIUrlListManager) ||
|
||||
|
|
|
@ -21,7 +21,7 @@ const HTTP_TEMPORARY_REDIRECT = 307;
|
|||
* @param retryIncrement Time (ms) for each retry before backing off.
|
||||
* @param maxRequests Number the number of requests needed to trigger backoff
|
||||
* @param requestPeriod Number time (ms) in which maxRequests have to occur to
|
||||
* trigger the backoff behavior
|
||||
* trigger the backoff behavior (0 to disable maxRequests)
|
||||
* @param timeoutIncrement Number time (ms) the starting timeout period
|
||||
* we double this time for consecutive errors
|
||||
* @param maxTimeout Number time (ms) maximum timeout period
|
||||
|
@ -45,7 +45,7 @@ function RequestBackoff(maxErrors, retryIncrement,
|
|||
}
|
||||
|
||||
/**
|
||||
* Reset the object for reuse.
|
||||
* Reset the object for reuse. This deliberately doesn't clear requestTimes_.
|
||||
*/
|
||||
RequestBackoff.prototype.reset = function() {
|
||||
this.numErrors_ = 0;
|
||||
|
|
|
@ -46,21 +46,20 @@ interface nsIUrlClassifierHashCompleterCallback : nsISupports
|
|||
* This is only ever used for testing and should absolutely be deleted (I
|
||||
* think).
|
||||
*/
|
||||
[scriptable, uuid(ade9b72b-3562-44f5-aba6-e63246be53ae)]
|
||||
[scriptable, uuid(231fb2ad-ea8a-4e63-a331-eafc3b434811)]
|
||||
interface nsIUrlClassifierHashCompleter : nsISupports
|
||||
{
|
||||
/**
|
||||
* Request a completed hash.
|
||||
* Request a completed hash from the given gethash url.
|
||||
*
|
||||
* @param partialHash
|
||||
* The 32-bit hash encountered by the url-classifier.
|
||||
* @param gethashUrl
|
||||
* The gethash url to use.
|
||||
* @param callback
|
||||
* An nsIUrlClassifierCompleterCallback instance.
|
||||
*/
|
||||
void complete(in ACString partialHash,
|
||||
in ACString gethashUrl,
|
||||
in nsIUrlClassifierHashCompleterCallback callback);
|
||||
/**
|
||||
* The URL for the gethash request
|
||||
*/
|
||||
attribute ACString gethashUrl;
|
||||
};
|
||||
|
|
|
@ -11,22 +11,17 @@
|
|||
* downloading the whole update and then updating the sqlite database, we
|
||||
* update tables as the data is streaming in.
|
||||
*/
|
||||
[scriptable, uuid(79e6b710-ce68-4639-ac6b-7d293af424a1)]
|
||||
[scriptable, uuid(e1797597-f4d6-4dd3-a1e1-745ad352cd80)]
|
||||
interface nsIUrlClassifierStreamUpdater : nsISupports
|
||||
{
|
||||
/**
|
||||
* The Url to download from. Should be plain ascii text.
|
||||
*/
|
||||
attribute ACString updateUrl;
|
||||
|
||||
/**
|
||||
* Try to download updates from updateUrl. Only one instance of this
|
||||
* runs at a time, so we return false if another instance is already
|
||||
* running.
|
||||
* This is used in nsIUrlListManager as well as in testing.
|
||||
* Try to download updates from updateUrl. If an update is already in
|
||||
* progress, queues the requested update. This is used in nsIUrlListManager
|
||||
* as well as in testing.
|
||||
* @param aRequestTables Comma-separated list of tables included in this
|
||||
* update.
|
||||
* @param aRequestBody The body for the request.
|
||||
* @param aUpdateUrl The plaintext url from which to request updates.
|
||||
* @param aSuccessCallback Called after a successful update.
|
||||
* @param aUpdateErrorCallback Called for problems applying the update
|
||||
* @param aDownloadErrorCallback Called if we get an http error or a
|
||||
|
@ -34,6 +29,7 @@ interface nsIUrlClassifierStreamUpdater : nsISupports
|
|||
*/
|
||||
boolean downloadUpdates(in ACString aRequestTables,
|
||||
in ACString aRequestBody,
|
||||
in ACString aUpdateUrl,
|
||||
in nsIUrlClassifierCallback aSuccessCallback,
|
||||
in nsIUrlClassifierCallback aUpdateErrorCallback,
|
||||
in nsIUrlClassifierCallback aDownloadErrorCallback);
|
||||
|
|
|
@ -18,29 +18,29 @@ interface nsIUrlListManagerCallback : nsISupports {
|
|||
};
|
||||
|
||||
|
||||
[scriptable, uuid(62484bb5-bf7e-4988-9055-8803b16b48a1)]
|
||||
[scriptable, uuid(653afdc4-ad4e-4ee0-8375-c576e85ebc40)]
|
||||
interface nsIUrlListManager : nsISupports
|
||||
{
|
||||
/**
|
||||
* Set the URL we check for updates.
|
||||
* Get the gethash url for this table
|
||||
*/
|
||||
void setUpdateUrl(in ACString url);
|
||||
ACString getGethashUrl(in ACString tableName);
|
||||
|
||||
/**
|
||||
* Set the URL that we will query for complete hashes after a partial
|
||||
* hash match.
|
||||
*/
|
||||
void setGethashUrl(in ACString url);
|
||||
|
||||
/**
|
||||
* Add a table to the list of tables we are managing. The name is a
|
||||
* Add a table to the list of tables we are managing. The name is a
|
||||
* string of the format provider_name-semantic_type-table_type. For
|
||||
* example, goog-white-enchash or goog-black-url.
|
||||
* @param tableName A string of the format
|
||||
* provider_name-semantic_type-table_type. For example,
|
||||
* goog-white-enchash or goog-black-url.
|
||||
* @param updateUrl The URL from which to fetch updates.
|
||||
* @param gethashUrl The URL from which to fetch hash completions.
|
||||
*/
|
||||
boolean registerTable(in ACString tableName);
|
||||
boolean registerTable(in ACString tableName,
|
||||
in ACString updateUrl,
|
||||
in ACString gethashUrl);
|
||||
|
||||
/**
|
||||
* Turn on update checking for a table. I.e., during the next server
|
||||
* Turn on update checking for a table. I.e., during the next server
|
||||
* check, download updates for this table.
|
||||
*/
|
||||
void enableUpdate(in ACString tableName);
|
||||
|
@ -57,6 +57,4 @@ interface nsIUrlListManager : nsISupports
|
|||
*/
|
||||
void safeLookup(in nsIPrincipal key,
|
||||
in nsIUrlListManagerCallback cb);
|
||||
|
||||
void checkForUpdates();
|
||||
};
|
||||
|
|
|
@ -94,7 +94,7 @@ static bool gShuttingDownThread = false;
|
|||
static mozilla::Atomic<int32_t> gFreshnessGuarantee(CONFIRM_AGE_DEFAULT_SEC);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Actual worker implemenatation
|
||||
// Actual worker implementation
|
||||
class nsUrlClassifierDBServiceWorker MOZ_FINAL :
|
||||
public nsIUrlClassifierDBServiceWorker
|
||||
{
|
||||
|
@ -428,8 +428,9 @@ nsUrlClassifierDBServiceWorker::BeginUpdate(nsIUrlClassifierUpdateObserver *obse
|
|||
{
|
||||
LOG(("nsUrlClassifierDBServiceWorker::BeginUpdate [%s]", PromiseFlatCString(tables).get()));
|
||||
|
||||
if (gShuttingDownThread)
|
||||
if (gShuttingDownThread) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
NS_ENSURE_STATE(!mUpdateObserver);
|
||||
|
||||
|
@ -523,8 +524,10 @@ nsUrlClassifierDBServiceWorker::UpdateStream(const nsACString& chunk)
|
|||
NS_IMETHODIMP
|
||||
nsUrlClassifierDBServiceWorker::FinishStream()
|
||||
{
|
||||
if (gShuttingDownThread)
|
||||
if (gShuttingDownThread) {
|
||||
LOG(("shutting down"));
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
NS_ENSURE_STATE(mInStream);
|
||||
NS_ENSURE_STATE(mUpdateObserver);
|
||||
|
@ -822,13 +825,26 @@ nsUrlClassifierLookupCallback::LookupComplete(nsTArray<LookupResult>* results)
|
|||
// We will complete partial matches and matches that are stale.
|
||||
if (!result.Confirmed()) {
|
||||
nsCOMPtr<nsIUrlClassifierHashCompleter> completer;
|
||||
nsCString gethashUrl;
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIUrlListManager> listManager = do_GetService(
|
||||
"@mozilla.org/url-classifier/listmanager;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = listManager->GetGethashUrl(result.mTableName, gethashUrl);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// We only allow empty gethashUrls for test- tables
|
||||
if (gethashUrl.IsEmpty()) {
|
||||
MOZ_ASSERT(
|
||||
StringBeginsWith(result.mTableName, NS_LITERAL_CSTRING("test-")),
|
||||
"Only test tables may have empty gethash urls");
|
||||
}
|
||||
if (mDBService->GetCompleter(result.mTableName,
|
||||
getter_AddRefs(completer))) {
|
||||
nsAutoCString partialHash;
|
||||
partialHash.Assign(reinterpret_cast<char*>(&result.hash.prefix),
|
||||
PREFIX_SIZE);
|
||||
|
||||
nsresult rv = completer->Complete(partialHash, this);
|
||||
nsresult rv = completer->Complete(partialHash, gethashUrl, this);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mPendingCompletions++;
|
||||
}
|
||||
|
@ -1342,8 +1358,10 @@ nsUrlClassifierDBService::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
|
|||
{
|
||||
NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (mInUpdate)
|
||||
if (mInUpdate) {
|
||||
LOG(("Already updating, not available"));
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
mInUpdate = true;
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "nsIObserver.h"
|
||||
#include "nsUrlClassifierPrefixSet.h"
|
||||
#include "nsIUrlClassifierHashCompleter.h"
|
||||
#include "nsIUrlListManager.h"
|
||||
#include "nsIUrlClassifierDBService.h"
|
||||
#include "nsIURIClassifier.h"
|
||||
#include "nsToolkitCompsCID.h"
|
||||
|
|
|
@ -14,51 +14,37 @@ const COMPLETE_LENGTH = 32;
|
|||
const PARTIAL_LENGTH = 4;
|
||||
|
||||
// These backoff related constants are taken from v2 of the Google Safe Browsing
|
||||
// API.
|
||||
// API. All times are in milliseconds.
|
||||
// BACKOFF_ERRORS: the number of errors incurred until we start to back off.
|
||||
// BACKOFF_INTERVAL: the initial time, in seconds, to wait once we start backing
|
||||
// BACKOFF_INTERVAL: the initial time to wait once we start backing
|
||||
// off.
|
||||
// BACKOFF_MAX: as the backoff time doubles after each failure, this is a
|
||||
// ceiling on the time to wait, in seconds.
|
||||
// BACKOFF_TIME: length of the interval of time, in seconds, during which errors
|
||||
// are taken into account.
|
||||
// ceiling on the time to wait.
|
||||
|
||||
const BACKOFF_ERRORS = 2;
|
||||
const BACKOFF_INTERVAL = 30 * 60;
|
||||
const BACKOFF_MAX = 8 * 60 * 60;
|
||||
const BACKOFF_TIME = 5 * 60;
|
||||
const BACKOFF_INTERVAL = 30 * 60 * 1000;
|
||||
const BACKOFF_MAX = 8 * 60 * 60 * 1000;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function HashCompleter() {
|
||||
// This is a HashCompleterRequest and is used by multiple calls to |complete|
|
||||
// in succession to avoid unnecessarily creating requests. Once it has been
|
||||
// started, this is set to null again.
|
||||
// The current HashCompleterRequest in flight. Once it is started, it is set
|
||||
// to null. It may be used by multiple calls to |complete| in succession to
|
||||
// avoid creating multiple requests to the same gethash URL.
|
||||
this._currentRequest = null;
|
||||
// A map of gethashUrls to HashCompleterRequests that haven't yet begun.
|
||||
this._pendingRequests = {};
|
||||
|
||||
// A map of gethash URLs to RequestBackoff objects.
|
||||
this._backoffs = {};
|
||||
|
||||
// Whether we have been informed of a shutdown by the xpcom-shutdown event.
|
||||
this._shuttingDown = false;
|
||||
|
||||
// All of these backoff properties are different per completer as the DB
|
||||
// service keeps one completer per table.
|
||||
//
|
||||
// _backoff tells us whether we are "backing off" from making requests.
|
||||
// It is set in |noteServerResponse| and set after a number of failures.
|
||||
this._backoff = false;
|
||||
// _backoffTime tells us how long we should wait (in seconds) before making
|
||||
// another request.
|
||||
this._backoffTime = 0;
|
||||
// _nextRequestTime is the earliest time at which we are allowed to make
|
||||
// another request by the backoff policy. It is measured in milliseconds.
|
||||
this._nextRequestTime = 0;
|
||||
// A list of the times at which a failed request was made, recorded in
|
||||
// |noteServerResponse|. Sorted by oldest to newest and its length is clamped
|
||||
// by BACKOFF_ERRORS.
|
||||
this._errorTimes = [];
|
||||
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", true);
|
||||
}
|
||||
|
||||
HashCompleter.prototype = {
|
||||
classID: Components.ID("{9111de73-9322-4bfc-8b65-2b727f3e6ec8}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIUrlClassifierHashCompleter,
|
||||
|
@ -70,108 +56,91 @@ HashCompleter.prototype = {
|
|||
// This is mainly how the HashCompleter interacts with other components.
|
||||
// Even though it only takes one partial hash and callback, subsequent
|
||||
// calls are made into the same HTTP request by using a thread dispatch.
|
||||
complete: function HC_complete(aPartialHash, aCallback) {
|
||||
if (!this._currentRequest) {
|
||||
this._currentRequest = new HashCompleterRequest(this);
|
||||
|
||||
// It's possible for getHashUrl to not be set, usually at start-up.
|
||||
if (this._getHashUrl) {
|
||||
Services.tm.currentThread.dispatch(this, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
complete: function HC_complete(aPartialHash, aGethashUrl, aCallback) {
|
||||
if (!aGethashUrl) {
|
||||
throw Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
this._currentRequest.add(aPartialHash, aCallback);
|
||||
if (!this._currentRequest) {
|
||||
this._currentRequest = new HashCompleterRequest(this, aGethashUrl);
|
||||
}
|
||||
if (this._currentRequest.gethashUrl == aGethashUrl) {
|
||||
this._currentRequest.add(aPartialHash, aCallback);
|
||||
} else {
|
||||
if (!this._pendingRequests[aGethashUrl]) {
|
||||
this._pendingRequests[aGethashUrl] =
|
||||
new HashCompleterRequest(this, aGethashUrl);
|
||||
}
|
||||
this._pendingRequests[aGethashUrl].add(aPartialHash, aCallback);
|
||||
}
|
||||
|
||||
if (!this._backoffs[aGethashUrl]) {
|
||||
// Initialize request backoffs separately, since requests are deleted
|
||||
// after they are dispatched.
|
||||
var jslib = Cc["@mozilla.org/url-classifier/jslib;1"]
|
||||
.getService().wrappedJSObject;
|
||||
this._backoffs[aGethashUrl] = new jslib.RequestBackoff(
|
||||
BACKOFF_ERRORS /* max errors */,
|
||||
60*1000 /* retry interval, 1 min */,
|
||||
10 /* keep track of max requests */,
|
||||
0 /* don't throttle on successful requests per time period */,
|
||||
BACKOFF_INTERVAL /* backoff interval, 60 min */,
|
||||
BACKOFF_MAX /* max backoff, 8hr */);
|
||||
}
|
||||
// Start off this request. Without dispatching to a thread, every call to
|
||||
// complete makes an individual HTTP request.
|
||||
Services.tm.currentThread.dispatch(this, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
},
|
||||
|
||||
// This is called when either the getHashUrl has been set or after a few calls
|
||||
// to |complete|. It starts off the HTTP request by making a |begin| call
|
||||
// to the HashCompleterRequest.
|
||||
run: function HC_run() {
|
||||
// This is called after several calls to |complete|, or after the
|
||||
// currentRequest has finished. It starts off the HTTP request by making a
|
||||
// |begin| call to the HashCompleterRequest.
|
||||
run: function() {
|
||||
// Clear everything on shutdown
|
||||
if (this._shuttingDown) {
|
||||
this._currentRequest = null;
|
||||
this._pendingRequests = null;
|
||||
for (var url in this._backoffs) {
|
||||
this._backoffs[url] = null;
|
||||
}
|
||||
throw Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!this._currentRequest) {
|
||||
return;
|
||||
// If we don't have an in-flight request, make one
|
||||
let pendingUrls = Object.keys(this._pendingRequests);
|
||||
if (!this._currentRequest && (pendingUrls.length > 0)) {
|
||||
let nextUrl = pendingUrls[0];
|
||||
this._currentRequest = this._pendingRequests[nextUrl];
|
||||
delete this._pendingRequests[nextUrl];
|
||||
}
|
||||
|
||||
if (!this._getHashUrl) {
|
||||
throw Cr.NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
let url = this._getHashUrl;
|
||||
|
||||
let uri = Services.io.newURI(url, null, null);
|
||||
this._currentRequest.setURI(uri);
|
||||
|
||||
// If |begin| fails, we should get rid of our request.
|
||||
try {
|
||||
this._currentRequest.begin();
|
||||
}
|
||||
finally {
|
||||
this._currentRequest = null;
|
||||
}
|
||||
},
|
||||
|
||||
get gethashUrl() {
|
||||
return this._getHashUrl;
|
||||
},
|
||||
// Because we hold off on making a request until we have a valid getHashUrl,
|
||||
// we kick off the process here.
|
||||
set gethashUrl(aNewUrl) {
|
||||
this._getHashUrl = aNewUrl;
|
||||
|
||||
if (this._currentRequest) {
|
||||
Services.tm.currentThread.dispatch(this, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
try {
|
||||
this._currentRequest.begin();
|
||||
} finally {
|
||||
// If |begin| fails, we should get rid of our request.
|
||||
this._currentRequest = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// This handles all the logic about setting a back off time based on
|
||||
// server responses. It should only be called once in the life time of a
|
||||
// request.
|
||||
// The logic behind backoffs is documented in the Google Safe Browsing API,
|
||||
// the general idea is that a completer should go into backoff mode after
|
||||
// BACKOFF_ERRORS errors in the last BACKOFF_TIME seconds. From there,
|
||||
// we do not make a request for BACKOFF_INTERVAL seconds and for every failed
|
||||
// request after that we double how long to wait, to a maximum of BACKOFF_MAX.
|
||||
// Note that even in the case of a successful response we still keep a history
|
||||
// of past errors.
|
||||
noteServerResponse: function HC_noteServerResponse(aSuccess) {
|
||||
if (aSuccess) {
|
||||
this._backoff = false;
|
||||
this._nextRequestTime = 0;
|
||||
this._backoffTime = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
let now = Date.now();
|
||||
|
||||
// We only alter the size of |_errorTimes| here, so we can guarantee that
|
||||
// its length is at most BACKOFF_ERRORS.
|
||||
this._errorTimes.push(now);
|
||||
if (this._errorTimes.length > BACKOFF_ERRORS) {
|
||||
this._errorTimes.shift();
|
||||
}
|
||||
|
||||
if (this._backoff) {
|
||||
this._backoffTime *= 2;
|
||||
} else if (this._errorTimes.length == BACKOFF_ERRORS &&
|
||||
((now - this._errorTimes[0]) / 1000) <= BACKOFF_TIME) {
|
||||
this._backoff = true;
|
||||
this._backoffTime = BACKOFF_INTERVAL;
|
||||
}
|
||||
|
||||
if (this._backoff) {
|
||||
this._backoffTime = Math.min(this._backoffTime, BACKOFF_MAX);
|
||||
this._nextRequestTime = now + (this._backoffTime * 1000);
|
||||
}
|
||||
// Pass the server response status to the RequestBackoff for the given
|
||||
// gethashUrl and fetch the next pending request, if there is one.
|
||||
finishRequest: function(url, aStatus) {
|
||||
this._backoffs[url].noteServerResponse(aStatus);
|
||||
Services.tm.currentThread.dispatch(this, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
},
|
||||
|
||||
// This is not included on the interface but is used to communicate to the
|
||||
// HashCompleterRequest. It returns a time in milliseconds.
|
||||
get nextRequestTime() {
|
||||
return this._nextRequestTime;
|
||||
// Returns true if we can make a request from the given url, false otherwise.
|
||||
canMakeRequest: function(aGethashUrl) {
|
||||
return this._backoffs[aGethashUrl].canMakeRequest();
|
||||
},
|
||||
|
||||
// Notifies the RequestBackoff of a new request so we can throttle based on
|
||||
// max requests/time period. This must be called before a channel is opened,
|
||||
// and finishRequest must be called once the response is received.
|
||||
noteRequest: function(aGethashUrl) {
|
||||
return this._backoffs[aGethashUrl].noteRequest();
|
||||
},
|
||||
|
||||
observe: function HC_observe(aSubject, aTopic, aData) {
|
||||
|
@ -181,20 +150,18 @@ HashCompleter.prototype = {
|
|||
},
|
||||
};
|
||||
|
||||
function HashCompleterRequest(aCompleter) {
|
||||
function HashCompleterRequest(aCompleter, aGethashUrl) {
|
||||
// HashCompleter object that created this HashCompleterRequest.
|
||||
this._completer = aCompleter;
|
||||
// The internal set of hashes and callbacks that this request corresponds to.
|
||||
this._requests = [];
|
||||
// URI to query for hash completions. Largely comes from the
|
||||
// browser.safebrowsing.gethashURL pref.
|
||||
this._uri = null;
|
||||
// nsIChannel that the hash completion query is transmitted over.
|
||||
this._channel = null;
|
||||
// Response body of hash completion. Created in onDataAvailable.
|
||||
this._response = "";
|
||||
// Whether we have been informed of a shutdown by the xpcom-shutdown event.
|
||||
this._shuttingDown = false;
|
||||
this.gethashUrl = aGethashUrl;
|
||||
}
|
||||
HashCompleterRequest.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver,
|
||||
|
@ -213,9 +180,11 @@ HashCompleterRequest.prototype = {
|
|||
},
|
||||
|
||||
// This initiates the HTTP request. It can fail due to backoff timings and
|
||||
// will notify all callbacks as necessary.
|
||||
// will notify all callbacks as necessary. We notify the backoff object on
|
||||
// begin.
|
||||
begin: function HCR_begin() {
|
||||
if (Date.now() < this._completer.nextRequestTime) {
|
||||
if (!this._completer.canMakeRequest(this.gethashUrl)) {
|
||||
dump("hashcompleter: Can't make request to " + this.gethashUrl + "\n");
|
||||
this.notifyFailure(Cr.NS_ERROR_ABORT);
|
||||
return;
|
||||
}
|
||||
|
@ -224,6 +193,9 @@ HashCompleterRequest.prototype = {
|
|||
|
||||
try {
|
||||
this.openChannel();
|
||||
// Notify the RequestBackoff if opening the channel succeeded. At this
|
||||
// point, finishRequest must be called.
|
||||
this._completer.noteRequest(this.gethashUrl);
|
||||
}
|
||||
catch (err) {
|
||||
this.notifyFailure(err);
|
||||
|
@ -231,16 +203,13 @@ HashCompleterRequest.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
setURI: function HCR_setURI(aURI) {
|
||||
this._uri = aURI;
|
||||
},
|
||||
|
||||
// Creates an nsIChannel for the request and fills the body.
|
||||
openChannel: function HCR_openChannel() {
|
||||
let loadFlags = Ci.nsIChannel.INHIBIT_CACHING |
|
||||
Ci.nsIChannel.LOAD_BYPASS_CACHE;
|
||||
|
||||
let channel = Services.io.newChannelFromURI(this._uri);
|
||||
let uri = Services.io.newURI(this.gethashUrl, null, null);
|
||||
let channel = Services.io.newChannelFromURI(uri);
|
||||
channel.loadFlags = loadFlags;
|
||||
|
||||
this._channel = channel;
|
||||
|
@ -306,12 +275,13 @@ HashCompleterRequest.prototype = {
|
|||
let start = 0;
|
||||
|
||||
let length = this._response.length;
|
||||
while (start != length)
|
||||
while (start != length) {
|
||||
start = this.handleTable(start);
|
||||
}
|
||||
},
|
||||
|
||||
// This parses a table entry in the response body and calls |handleItem|
|
||||
// for complete hash in the table entry.
|
||||
// for complete hash in the table entry.
|
||||
handleTable: function HCR_handleTable(aStart) {
|
||||
let body = this._response.substring(aStart);
|
||||
|
||||
|
@ -404,16 +374,21 @@ HashCompleterRequest.prototype = {
|
|||
throw Cr.NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
// Default HTTP status to service unavailable, in case we can't retrieve
|
||||
// the true status from the channel.
|
||||
let httpStatus = 503;
|
||||
if (Components.isSuccessCode(aStatusCode)) {
|
||||
let channel = aRequest.QueryInterface(Ci.nsIHttpChannel);
|
||||
let success = channel.requestSucceeded;
|
||||
httpStatus = channel.responseStatus;
|
||||
if (!success) {
|
||||
aStatusCode = Cr.NS_ERROR_ABORT;
|
||||
}
|
||||
}
|
||||
|
||||
let success = Components.isSuccessCode(aStatusCode);
|
||||
this._completer.noteServerResponse(success);
|
||||
// Notify the RequestBackoff once a response is received.
|
||||
this._completer.finishRequest(this.gethashUrl, httpStatus);
|
||||
|
||||
if (success) {
|
||||
try {
|
||||
|
|
|
@ -39,13 +39,13 @@ static const PRLogModuleInfo *gUrlClassifierStreamUpdaterLog = nullptr;
|
|||
|
||||
nsUrlClassifierStreamUpdater::nsUrlClassifierStreamUpdater()
|
||||
: mIsUpdating(false), mInitialized(false), mDownloadError(false),
|
||||
mBeganStream(false), mUpdateUrl(nullptr), mChannel(nullptr)
|
||||
mBeganStream(false), mChannel(nullptr)
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
if (!gUrlClassifierStreamUpdaterLog)
|
||||
gUrlClassifierStreamUpdaterLog = PR_NewLogModule("UrlClassifierStreamUpdater");
|
||||
#endif
|
||||
|
||||
LOG(("nsUrlClassifierStreamUpdater init [this=%p]", this));
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsUrlClassifierStreamUpdater,
|
||||
|
@ -76,33 +76,20 @@ nsUrlClassifierStreamUpdater::DownloadDone()
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
// nsIUrlClassifierStreamUpdater implementation
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::GetUpdateUrl(nsACString & aUpdateUrl)
|
||||
{
|
||||
if (mUpdateUrl) {
|
||||
mUpdateUrl->GetSpec(aUpdateUrl);
|
||||
} else {
|
||||
aUpdateUrl.Truncate();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::SetUpdateUrl(const nsACString & aUpdateUrl)
|
||||
{
|
||||
LOG(("Update URL is %s\n", PromiseFlatCString(aUpdateUrl).get()));
|
||||
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(mUpdateUrl), aUpdateUrl);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierStreamUpdater::FetchUpdate(nsIURI *aUpdateUrl,
|
||||
const nsACString & aRequestBody,
|
||||
const nsACString & aStreamTable)
|
||||
{
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
nsCString spec;
|
||||
aUpdateUrl->GetSpec(spec);
|
||||
LOG(("Fetching update %s from %s", aRequestBody.Data(), spec.get()));
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult rv;
|
||||
uint32_t loadFlags = nsIChannel::INHIBIT_CACHING |
|
||||
nsIChannel::LOAD_BYPASS_CACHE;
|
||||
|
@ -165,24 +152,33 @@ nsUrlClassifierStreamUpdater::FetchUpdate(const nsACString & aUpdateUrl,
|
|||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::DownloadUpdates(
|
||||
const nsACString &aRequestTables,
|
||||
const nsACString &aRequestBody,
|
||||
nsIUrlClassifierCallback *aSuccessCallback,
|
||||
nsIUrlClassifierCallback *aUpdateErrorCallback,
|
||||
nsIUrlClassifierCallback *aDownloadErrorCallback,
|
||||
bool *_retval)
|
||||
const nsACString &aRequestTables,
|
||||
const nsACString &aRequestBody,
|
||||
const nsACString &aUpdateUrl,
|
||||
nsIUrlClassifierCallback *aSuccessCallback,
|
||||
nsIUrlClassifierCallback *aUpdateErrorCallback,
|
||||
nsIUrlClassifierCallback *aDownloadErrorCallback,
|
||||
bool *_retval)
|
||||
{
|
||||
NS_ENSURE_ARG(aSuccessCallback);
|
||||
NS_ENSURE_ARG(aUpdateErrorCallback);
|
||||
NS_ENSURE_ARG(aDownloadErrorCallback);
|
||||
|
||||
if (mIsUpdating) {
|
||||
LOG(("already updating, skipping update"));
|
||||
LOG(("Already updating, queueing update %s from %s", aRequestBody.Data(),
|
||||
aUpdateUrl.Data()));
|
||||
*_retval = false;
|
||||
PendingRequest *request = mPendingRequests.AppendElement();
|
||||
request->mTables = aRequestTables;
|
||||
request->mRequest = aRequestBody;
|
||||
request->mUrl = aUpdateUrl;
|
||||
request->mSuccessCallback = aSuccessCallback;
|
||||
request->mUpdateErrorCallback = aUpdateErrorCallback;
|
||||
request->mDownloadErrorCallback = aDownloadErrorCallback;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mUpdateUrl) {
|
||||
if (aUpdateUrl.IsEmpty()) {
|
||||
NS_ERROR("updateUrl not set");
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
@ -208,10 +204,20 @@ nsUrlClassifierStreamUpdater::DownloadUpdates(
|
|||
|
||||
rv = mDBService->BeginUpdate(this, aRequestTables);
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
LOG(("already updating, skipping update"));
|
||||
LOG(("Service busy, already updating, queuing update %s from %s",
|
||||
aRequestBody.Data(), aUpdateUrl.Data()));
|
||||
*_retval = false;
|
||||
PendingRequest *request = mPendingRequests.AppendElement();
|
||||
request->mTables = aRequestTables;
|
||||
request->mRequest = aRequestBody;
|
||||
request->mUrl = aUpdateUrl;
|
||||
request->mSuccessCallback = aSuccessCallback;
|
||||
request->mUpdateErrorCallback = aUpdateErrorCallback;
|
||||
request->mDownloadErrorCallback = aDownloadErrorCallback;
|
||||
return NS_OK;
|
||||
} else if (NS_FAILED(rv)) {
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -222,14 +228,10 @@ nsUrlClassifierStreamUpdater::DownloadUpdates(
|
|||
mIsUpdating = true;
|
||||
*_retval = true;
|
||||
|
||||
nsAutoCString urlSpec;
|
||||
mUpdateUrl->GetAsciiSpec(urlSpec);
|
||||
|
||||
LOG(("FetchUpdate: %s", urlSpec.get()));
|
||||
LOG(("FetchUpdate: %s", aUpdateUrl.Data()));
|
||||
//LOG(("requestBody: %s", aRequestBody.Data()));
|
||||
|
||||
LOG(("Calling into FetchUpdate"));
|
||||
return FetchUpdate(mUpdateUrl, aRequestBody, EmptyCString());
|
||||
return FetchUpdate(aUpdateUrl, aRequestBody, EmptyCString());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -290,6 +292,34 @@ nsUrlClassifierStreamUpdater::FetchNext()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierStreamUpdater::FetchNextRequest()
|
||||
{
|
||||
if (mPendingRequests.Length() == 0) {
|
||||
LOG(("No more requests, returning"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PendingRequest &request = mPendingRequests[0];
|
||||
LOG(("Stream updater: fetching next request: %s, %s",
|
||||
request.mTables.get(), request.mUrl.get()));
|
||||
bool dummy;
|
||||
DownloadUpdates(
|
||||
request.mTables,
|
||||
request.mRequest,
|
||||
request.mUrl,
|
||||
request.mSuccessCallback,
|
||||
request.mUpdateErrorCallback,
|
||||
request.mDownloadErrorCallback,
|
||||
&dummy);
|
||||
request.mSuccessCallback = nullptr;
|
||||
request.mUpdateErrorCallback = nullptr;
|
||||
request.mDownloadErrorCallback = nullptr;
|
||||
mPendingRequests.RemoveElementAt(0);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::StreamFinished(nsresult status,
|
||||
uint32_t requestedDelay)
|
||||
|
@ -334,6 +364,9 @@ nsUrlClassifierStreamUpdater::UpdateSuccess(uint32_t requestedTimeout)
|
|||
if (successCallback) {
|
||||
successCallback->HandleEvent(strTimeout);
|
||||
}
|
||||
// Now fetch the next request
|
||||
LOG(("stream updater: calling into fetch next request"));
|
||||
FetchNextRequest();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -63,17 +63,29 @@ private:
|
|||
|
||||
// Fetches the next table, from mPendingUpdates.
|
||||
nsresult FetchNext();
|
||||
// Fetches the next request, from mPendingRequests
|
||||
nsresult FetchNextRequest();
|
||||
|
||||
|
||||
bool mIsUpdating;
|
||||
bool mInitialized;
|
||||
bool mDownloadError;
|
||||
bool mBeganStream;
|
||||
nsCOMPtr<nsIURI> mUpdateUrl;
|
||||
nsCString mStreamTable;
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
nsCOMPtr<nsIUrlClassifierDBService> mDBService;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
|
||||
struct PendingRequest {
|
||||
nsCString mTables;
|
||||
nsCString mRequest;
|
||||
nsCString mUrl;
|
||||
nsCOMPtr<nsIUrlClassifierCallback> mSuccessCallback;
|
||||
nsCOMPtr<nsIUrlClassifierCallback> mUpdateErrorCallback;
|
||||
nsCOMPtr<nsIUrlClassifierCallback> mDownloadErrorCallback;
|
||||
};
|
||||
nsTArray<PendingRequest> mPendingRequests;
|
||||
|
||||
struct PendingUpdate {
|
||||
nsCString mUrl;
|
||||
nsCString mTable;
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче