зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to fx-team
This commit is contained in:
Коммит
0f7453cae3
2
CLOBBER
2
CLOBBER
|
@ -18,4 +18,4 @@
|
|||
# Modifying this file will now automatically clobber the buildbot machines \o/
|
||||
#
|
||||
|
||||
Bug 872934 - Clobber needed for webidl updates for style sheet change events. Again and again.
|
||||
Bug 895047 - Clobber needed for touching js/src/js-confdefs.h.in
|
||||
|
|
|
@ -7,8 +7,9 @@ ifndef .PYMAKE
|
|||
ifeq (,$(MAKE_VERSION))
|
||||
$(error GNU Make is required)
|
||||
endif
|
||||
ifeq (,$(filter-out 3.78 3.79,$(MAKE_VERSION)))
|
||||
$(error GNU Make 3.80 or higher is required)
|
||||
make_min_ver := 3.81
|
||||
ifneq ($(make_min_ver),$(firstword $(sort $(make_min_ver) $(MAKE_VERSION))))
|
||||
$(error GNU Make $(make_min_ver) or higher is required)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
|
|
@ -466,12 +466,11 @@ sdnAccessible::get_innerHTML(BSTR __RPC_FAR* aInnerHTML)
|
|||
if (IsDefunct())
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(mNode);
|
||||
if (!htmlElement)
|
||||
if (!mNode->IsElement())
|
||||
return S_FALSE;
|
||||
|
||||
nsAutoString innerHTML;
|
||||
htmlElement->GetInnerHTML(innerHTML);
|
||||
mNode->AsElement()->GetInnerHTML(innerHTML);
|
||||
if (innerHTML.IsEmpty())
|
||||
return S_FALSE;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"revision": "86e06b1db110e34eb66826d3b1bdee3a5d57b3a7",
|
||||
"revision": "563d1aa93586165246ab2ab9d40566a598f56387",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -9,13 +9,18 @@ DEFINES += \
|
|||
-DB2G_NAME=L\"$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)\" \
|
||||
-DGAIA_PATH=L\"$(subst /,\\\\,$(GAIA_PATH))\" \
|
||||
$(NULL)
|
||||
GAIA_MAKE=make
|
||||
else # Non-windows machines use the same wrapper program
|
||||
CSRCS = run-b2g.c
|
||||
DEFINES += \
|
||||
-DB2G_NAME=\"$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)\" \
|
||||
-DGAIA_PATH=\"$(GAIA_PATH)\" \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef .PYMAKE
|
||||
# For use of GNU make in pymake builds.
|
||||
GAIA_MAKE=$(GMAKE)
|
||||
else
|
||||
GAIA_MAKE=$(MAKE)
|
||||
endif
|
||||
|
||||
|
@ -27,6 +32,6 @@ GENERATED_DIRS += $(DIST)/bin/$(GAIA_PATH)
|
|||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
libs::
|
||||
$(GAIA_MAKE) -j1 -C $(GAIADIR) clean
|
||||
$(GAIA_MAKE) -j1 -C $(GAIADIR) profile
|
||||
+$(GAIA_MAKE) -j1 -C $(GAIADIR) clean
|
||||
+$(GAIA_MAKE) -j1 -C $(GAIADIR) profile
|
||||
(cd $(GAIADIR)/profile && tar $(TAR_CREATE_FLAGS) - .) | (cd $(abspath $(DIST))/bin/$(GAIA_PATH) && tar -xf -)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# 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/.
|
||||
|
||||
DISTROEXT = $(call core_abspath,$(FINAL_TARGET))/distribution/extensions
|
||||
DISTROEXT = $(abspath $(FINAL_TARGET))/distribution/extensions
|
||||
|
||||
include $(topsrcdir)/config/config.mk
|
||||
|
||||
|
@ -34,7 +34,7 @@ include $(topsrcdir)/config/rules.mk
|
|||
$(all_xpis): $(DISTROEXT)/%.xpi: $(call mkdir_deps,$(DISTROEXT)) libs-%
|
||||
cd $* && \
|
||||
$(ZIP) -r9XD $@ * -x \*.in -x \*.mkdir.done
|
||||
cd $(call core_abspath,$(srcdir)/$*) && \
|
||||
cd $(abspath $(srcdir)/$*) && \
|
||||
$(ZIP) -r9XD $@ * -x \*.in -x \*.mkdir.done
|
||||
|
||||
.PHONY: $(all_xpis:.xpi=)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
include $(topsrcdir)/config/config.mk
|
||||
|
||||
abs_srcdir = $(call core_abspath,$(srcdir))
|
||||
abs_srcdir = $(abspath $(srcdir))
|
||||
|
||||
CHROME_DEPS += $(abs_srcdir)/content/overrides/app-license.html
|
||||
|
||||
|
|
|
@ -155,6 +155,54 @@ function basicNotification() {
|
|||
}
|
||||
}
|
||||
|
||||
function errorNotification() {
|
||||
var self = this;
|
||||
this.browser = gBrowser.selectedBrowser;
|
||||
this.id = "test-notification-" + gTestIndex;
|
||||
this.message = "This is popup notification " + this.id + " from test " + gTestIndex;
|
||||
this.anchorID = null;
|
||||
this.mainAction = {
|
||||
label: "Main Action",
|
||||
accessKey: "M",
|
||||
callback: function () {
|
||||
self.mainActionClicked = true;
|
||||
throw new Error("Oops!");
|
||||
}
|
||||
};
|
||||
this.secondaryActions = [
|
||||
{
|
||||
label: "Secondary Action",
|
||||
accessKey: "S",
|
||||
callback: function () {
|
||||
self.secondaryActionClicked = true;
|
||||
throw new Error("Oops!");
|
||||
}
|
||||
}
|
||||
];
|
||||
this.options = {
|
||||
eventCallback: function (eventName) {
|
||||
switch (eventName) {
|
||||
case "dismissed":
|
||||
self.dismissalCallbackTriggered = true;
|
||||
break;
|
||||
case "showing":
|
||||
self.showingCallbackTriggered = true;
|
||||
break;
|
||||
case "shown":
|
||||
self.shownCallbackTriggered = true;
|
||||
break;
|
||||
case "removed":
|
||||
self.removedCallbackTriggered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
this.addOptions = function(options) {
|
||||
for (let [name, value] in Iterator(options))
|
||||
self.options[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
var wrongBrowserNotificationObject = new basicNotification();
|
||||
var wrongBrowserNotification;
|
||||
|
||||
|
@ -831,7 +879,69 @@ var tests = [
|
|||
});
|
||||
}
|
||||
},
|
||||
{ // Test #29 - Existing popup notification shouldn't disappear when adding a dismissed notification
|
||||
{ // Test #29 - Popup Notifications should catch exceptions from callbacks
|
||||
run: function () {
|
||||
let callbackCount = 0;
|
||||
this.testNotif1 = new basicNotification();
|
||||
this.testNotif1.message += " 1";
|
||||
showNotification(this.testNotif1);
|
||||
this.testNotif1.options.eventCallback = function (eventName) {
|
||||
info("notifyObj1.options.eventCallback: " + eventName);
|
||||
if (eventName == "dismissed") {
|
||||
throw new Error("Oops 1!");
|
||||
if (++callbackCount == 2) {
|
||||
executeSoon(goNext);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.testNotif2 = new basicNotification();
|
||||
this.testNotif2.message += " 2";
|
||||
this.testNotif2.id += "-2";
|
||||
this.testNotif2.options.eventCallback = function (eventName) {
|
||||
info("notifyObj2.options.eventCallback: " + eventName);
|
||||
if (eventName == "dismissed") {
|
||||
throw new Error("Oops 2!");
|
||||
if (++callbackCount == 2) {
|
||||
executeSoon(goNext);
|
||||
}
|
||||
}
|
||||
};
|
||||
showNotification(this.testNotif2);
|
||||
},
|
||||
onShown: function (popup) {
|
||||
is(popup.childNodes.length, 2, "two notifications are shown");
|
||||
dismissNotification(popup);
|
||||
},
|
||||
onHidden: function () {}
|
||||
},
|
||||
{ // Test #30 - Popup Notifications main actions should catch exceptions from callbacks
|
||||
run: function () {
|
||||
this.testNotif = new errorNotification();
|
||||
showNotification(this.testNotif);
|
||||
},
|
||||
onShown: function (popup) {
|
||||
checkPopup(popup, this.testNotif);
|
||||
triggerMainCommand(popup);
|
||||
},
|
||||
onHidden: function (popup) {
|
||||
ok(this.testNotif.mainActionClicked, "main action has been triggered");
|
||||
}
|
||||
},
|
||||
{ // Test #31 - Popup Notifications secondary actions should catch exceptions from callbacks
|
||||
run: function () {
|
||||
this.testNotif = new errorNotification();
|
||||
showNotification(this.testNotif);
|
||||
},
|
||||
onShown: function (popup) {
|
||||
checkPopup(popup, this.testNotif);
|
||||
triggerSecondaryCommand(popup, 0);
|
||||
},
|
||||
onHidden: function (popup) {
|
||||
ok(this.testNotif.secondaryActionClicked, "secondary action has been triggered");
|
||||
}
|
||||
},
|
||||
{ // Test #32 - Existing popup notification shouldn't disappear when adding a dismissed notification
|
||||
run: function () {
|
||||
this.notifyObj1 = new basicNotification();
|
||||
this.notifyObj1.id += "_1";
|
||||
|
@ -862,7 +972,7 @@ var tests = [
|
|||
this.notification2.remove();
|
||||
}
|
||||
},
|
||||
{ // Test #30 - Showing should be able to modify the popup data
|
||||
{ // Test #33 - Showing should be able to modify the popup data
|
||||
run: function() {
|
||||
this.notifyObj = new basicNotification();
|
||||
var normalCallback = this.notifyObj.options.eventCallback;
|
||||
|
|
|
@ -8,7 +8,7 @@ browser_path := \"$(browser_path)\"
|
|||
|
||||
_PROFILE_DIR = $(TARGET_DEPTH)/_profile/pgo
|
||||
|
||||
ABSOLUTE_TOPSRCDIR = $(call core_abspath,$(MOZILLA_DIR))
|
||||
ABSOLUTE_TOPSRCDIR = $(abspath $(MOZILLA_DIR))
|
||||
_CERTS_SRC_DIR = $(ABSOLUTE_TOPSRCDIR)/build/pgo/certs
|
||||
|
||||
AUTOMATION_PPARGS = \
|
||||
|
|
|
@ -76,8 +76,9 @@ def InvokeClWithDependencyGeneration(cmdline):
|
|||
# We can't handle pathes with spaces properly in mddepend.pl, but
|
||||
# we can assume that anything in a path with spaces is a system
|
||||
# header and throw it away.
|
||||
dep = normcase(dep)
|
||||
if ' ' not in dep:
|
||||
rule.add_dependencies([normcase(dep)])
|
||||
rule.add_dependencies([dep])
|
||||
else:
|
||||
# Make sure we preserve the relevant output from cl. mozprocess
|
||||
# swallows the newline delimiter, so we need to re-add it.
|
||||
|
|
|
@ -19,14 +19,12 @@ topsrcdir = $(TOPSRCDIR)
|
|||
DEPTH = $(OBJDIR)
|
||||
include $(OBJDIR)/config/autoconf.mk
|
||||
|
||||
core_abspath = $(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1))
|
||||
|
||||
DIST = $(OBJDIR)/dist
|
||||
|
||||
postflight_all:
|
||||
mkdir -p $(DIST_UNI)/$(MOZ_PKG_APPNAME)
|
||||
rm -f $(DIST_ARCH_2)/universal
|
||||
ln -s $(call core_abspath,$(DIST_UNI)) $(DIST_ARCH_2)/universal
|
||||
ln -s $(abspath $(DIST_UNI)) $(DIST_ARCH_2)/universal
|
||||
# Stage a package for buildsymbols to be happy. Doing so in OBJDIR_ARCH_1
|
||||
# actually does a universal staging with both OBJDIR_ARCH_1 and OBJDIR_ARCH_2.
|
||||
$(MAKE) -C $(OBJDIR_ARCH_1)/$(MOZ_BUILD_APP)/installer \
|
||||
|
|
|
@ -101,7 +101,6 @@ public class RedirOutputThread extends Thread
|
|||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// Toast.makeText(SUTAgentAndroid.me.getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,13 +43,11 @@ import android.os.PowerManager;
|
|||
import android.os.RemoteException;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.widget.Toast;
|
||||
import android.os.Environment;
|
||||
|
||||
public class WatcherService extends Service
|
||||
{
|
||||
private final String prgVersion = "Watcher Version 1.16";
|
||||
private final String prgVersion = "Watcher Version 1.17";
|
||||
private final String LOGTAG = "Watcher";
|
||||
|
||||
String sErrorPrefix = "##Installer Error## ";
|
||||
|
@ -135,16 +133,16 @@ public class WatcherService extends Service
|
|||
try {
|
||||
if (nStayOn != 0) {
|
||||
if (!Settings.System.putInt(getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB)) {
|
||||
doToast("Screen couldn't be set to Always On [stay on while plugged in]");
|
||||
Log.e(LOGTAG, "Screen couldn't be set to Always On [stay on while plugged in]");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
String sExcept = e.getMessage();
|
||||
doToast("Screen couldn't be set to Always On [exception " + sExcept + "]");
|
||||
Log.e(LOGTAG, "Screen couldn't be set to Always On [exception " + sExcept + "]");
|
||||
}
|
||||
|
||||
doToast("WatcherService created");
|
||||
Log.i(LOGTAG, "WatcherService created");
|
||||
}
|
||||
|
||||
public String GetIniData(String sSection, String sKey, String sFile, String sDefault)
|
||||
|
@ -228,7 +226,7 @@ public class WatcherService extends Service
|
|||
{
|
||||
if (!this.bStartedTimer)
|
||||
{
|
||||
doToast("WatcherService started");
|
||||
Log.i(LOGTAG, "WatcherService started");
|
||||
myTimer = new Timer();
|
||||
Date startSchedule = new Date(System.currentTimeMillis() + lDelay);
|
||||
myTimer.schedule(new MyTime(), startSchedule, lPeriod);
|
||||
|
@ -237,11 +235,11 @@ public class WatcherService extends Service
|
|||
}
|
||||
else
|
||||
{
|
||||
doToast("WatcherService unknown command");
|
||||
Log.w(LOGTAG, "WatcherService unknown command");
|
||||
}
|
||||
}
|
||||
else
|
||||
doToast("WatcherService created");
|
||||
Log.w(LOGTAG, "WatcherService intent had null command");
|
||||
}
|
||||
|
||||
public void writeVersion() {
|
||||
|
@ -280,7 +278,7 @@ public class WatcherService extends Service
|
|||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
doToast("WatcherService destroyed");
|
||||
Log.i(LOGTAG, "WatcherService destroyed");
|
||||
if (pwl != null)
|
||||
pwl.release();
|
||||
stopForegroundCompat(R.string.foreground_service_started);
|
||||
|
@ -447,14 +445,6 @@ public class WatcherService extends Service
|
|||
}
|
||||
}
|
||||
|
||||
public void doToast(String sMsg)
|
||||
{
|
||||
Log.i(LOGTAG, sMsg);
|
||||
Toast toast = Toast.makeText(this, sMsg, Toast.LENGTH_LONG);
|
||||
toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL, 0, 100);
|
||||
toast.show();
|
||||
}
|
||||
|
||||
public void CheckMem()
|
||||
{
|
||||
System.gc();
|
||||
|
|
|
@ -16,6 +16,8 @@ WRAP_LDFLAGS=
|
|||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
DEFINES += -DELFHACK_BUILD
|
||||
|
||||
test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX): %$(DLL_SUFFIX): %.$(OBJ_SUFFIX) elfhack $(filter inject/%,$(CSRCS:.c=.$(OBJ_SUFFIX)))
|
||||
$(MKSHLIB) $(LDFLAGS) $< -nostartfiles
|
||||
@echo ===
|
||||
|
|
|
@ -30,5 +30,7 @@ $(CSRCS): %.c: ../inject.c
|
|||
|
||||
GARBAGE += $(CSRCS)
|
||||
|
||||
DEFINES += -DELFHACK_BUILD
|
||||
|
||||
CFLAGS := -O2 -fno-stack-protector $(filter -m% -I%,$(CFLAGS))
|
||||
$(CPU)-noinit.$(OBJ_SUFFIX): DEFINES += -DNOINIT
|
||||
|
|
|
@ -128,7 +128,7 @@ GARBAGE += \
|
|||
ifndef CROSS_COMPILE
|
||||
ifdef USE_ELF_DYNSTR_GC
|
||||
elf-dynstr-gc: elf-dynstr-gc.c $(GLOBAL_DEPS) $(call mkdir_deps,$(MDDEPDIR))
|
||||
$(CC) $(COMPILE_CFLAGS) $(GLIB_CFLAGS) -o $@ $< $(LDFLAGS) $(GLIB_LIBS)
|
||||
$(CC) $(COMPILE_CFLAGS) $(GLIB_CFLAGS) -DELFDYNSTRGC_BUILD -o $@ $< $(LDFLAGS) $(GLIB_LIBS)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ ifndef JAVA_CLASSPATH
|
|||
endif
|
||||
|
||||
# DEBUG_JARSIGNER always debug signs.
|
||||
DEBUG_JARSIGNER=$(PYTHON) $(call core_abspath,$(topsrcdir)/mobile/android/debug_sign_tool.py) \
|
||||
DEBUG_JARSIGNER=$(PYTHON) $(abspath $(topsrcdir)/mobile/android/debug_sign_tool.py) \
|
||||
--keytool=$(KEYTOOL) \
|
||||
--jarsigner=$(JARSIGNER) \
|
||||
$(NULL)
|
||||
|
|
|
@ -632,7 +632,7 @@ endif
|
|||
PWD := $(CURDIR)
|
||||
endif
|
||||
|
||||
NSINSTALL_PY := $(PYTHON) $(call core_abspath,$(topsrcdir)/config/nsinstall.py)
|
||||
NSINSTALL_PY := $(PYTHON) $(abspath $(topsrcdir)/config/nsinstall.py)
|
||||
# For Pymake, wherever we use nsinstall.py we're also going to try to make it
|
||||
# a native command where possible. Since native commands can't be used outside
|
||||
# of single-line commands, we continue to provide INSTALL for general use.
|
||||
|
@ -691,7 +691,7 @@ else
|
|||
IS_LANGUAGE_REPACK = 1
|
||||
endif
|
||||
|
||||
EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/$(1)/en-US,$(call core_realpath,$(L10NBASEDIR))/$(AB_CD)/$(subst /locales,,$(1)))
|
||||
EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/$(1)/en-US,$(or $(realpath $(L10NBASEDIR)),$(abspath $(L10NBASEDIR)))/$(AB_CD)/$(subst /locales,,$(1)))
|
||||
|
||||
ifdef relativesrcdir
|
||||
LOCALE_SRCDIR ?= $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir))
|
||||
|
@ -742,7 +742,7 @@ ifdef MOZ_DEBUG
|
|||
JAVAC_FLAGS += -g
|
||||
endif
|
||||
|
||||
CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py)
|
||||
CREATE_PRECOMPLETE_CMD = $(PYTHON) $(abspath $(topsrcdir)/config/createprecomplete.py)
|
||||
|
||||
# MDDEPDIR is the subdirectory where dependency files are stored
|
||||
MDDEPDIR := .deps
|
||||
|
|
|
@ -16,7 +16,7 @@ $(error Do not include functions.mk twice!)
|
|||
endif
|
||||
INCLUDED_FUNCTIONS_MK = 1
|
||||
|
||||
core_abspath = $(if $(findstring :,$(1)),$(1),$(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1)))
|
||||
core_realpath = $(if $(realpath $(1)),$(realpath $(1)),$(call core_abspath,$(1)))
|
||||
core_abspath = $(error core_abspath is unsupported, use $$(abspath) instead)
|
||||
core_realpath = $(error core_realpath is unsupported)
|
||||
|
||||
core_winabspath = $(firstword $(subst /, ,$(call core_abspath,$(1)))):$(subst $(space),,$(patsubst %,\\%,$(wordlist 2,$(words $(subst /, ,$(call core_abspath,$(1)))), $(strip $(subst /, ,$(call core_abspath,$(1)))))))
|
||||
core_winabspath = $(error core_winabspath is unsupported)
|
||||
|
|
|
@ -13,7 +13,7 @@ $(error config/nspr/Makefile.in is not compatible with MOZ_NATIVE_NSPR)
|
|||
endif
|
||||
|
||||
# Copy NSPR to the SDK
|
||||
ABS_DIST = $(call core_abspath,$(DIST))
|
||||
ABS_DIST = $(abspath $(DIST))
|
||||
|
||||
ifdef MOZ_FOLD_LIBS
|
||||
# Trick the nspr build system into not building shared libraries.
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script>
|
||||
function start() {
|
||||
var doc = document.getElementsByTagName("iframe")[0].contentDocument;
|
||||
var vid = doc.getElementsByTagName("video")[0];
|
||||
|
||||
function runnable() {
|
||||
// The doc.write forces us to recreate doc's body.
|
||||
doc.write("Hello, world");
|
||||
doc.body.appendChild(vid);
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
|
||||
doc.open();
|
||||
setTimeout(runnable, 0);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload='start()'>
|
||||
<iframe src="data:text/html,<meta charset=UTF-8><body><video src=http://localhost:8080/ controls=true loop=true autoplay=true autobuffer=false></video>"></iframe>
|
||||
</body>
|
||||
</html>
|
|
@ -133,5 +133,6 @@ load 841205.html
|
|||
load 844404.html
|
||||
load 847127.html
|
||||
load 849601.html
|
||||
skip-if(Android) load 851353-1.html
|
||||
load 863950.html
|
||||
load 864448.html
|
||||
|
|
|
@ -1173,7 +1173,9 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
nsIDocument::UnlockPointer();
|
||||
}
|
||||
if (GetParent()) {
|
||||
NS_RELEASE(mParent);
|
||||
nsINode* p = mParent;
|
||||
mParent = nullptr;
|
||||
NS_RELEASE(p);
|
||||
} else {
|
||||
mParent = nullptr;
|
||||
}
|
||||
|
|
|
@ -506,6 +506,7 @@ support-files =
|
|||
[test_bug814576.html]
|
||||
[test_bug819051.html]
|
||||
[test_bug820909.html]
|
||||
[test_bug840098.html]
|
||||
[test_bug868999.html]
|
||||
[test_bug869000.html]
|
||||
[test_bug869002.html]
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=840098
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 840098</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=840098">Mozilla Bug 840098</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<div id="foo"></div>
|
||||
</div>
|
||||
<marquee id="m">Hello</marquee>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 840098 **/
|
||||
var anonymousNode = document.getElementById("m").outerDiv;
|
||||
try {
|
||||
document.implementation.createDocument("", "", null).adoptNode(anonymousNode);
|
||||
ok(false, "shouldn't be able to adopt the root of an anonymous subtree");
|
||||
} catch (e) {
|
||||
is(e.name, "NotSupportedError", "threw the correct type of error");
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,13 @@
|
|||
<script>
|
||||
o0 = document.createElement('canvas');
|
||||
(document.body || document.documentElement).appendChild(o0);
|
||||
o1 = o0.getContext('2d');
|
||||
o2 = document.createElement('img');
|
||||
//o2.src = "image2.png";
|
||||
o3 = o1.createImageData(0.7409945472006207, 0.8815588599260801);
|
||||
o1.save();
|
||||
o1.mozCurrentTransform = [0.18777365986904448, 4, 4, -2048, 3, 32];
|
||||
o0.width = 0.52;
|
||||
o1.putImageData(o3, -32, -0.16596290333335356);
|
||||
o0.toBlob(function() {}, "video/mp4", 16);
|
||||
</script>
|
|
@ -17,3 +17,4 @@ skip-if(Android||B2G) load 780392-1.html # bug 833371 for B2G
|
|||
skip-if(Android||B2G) load 789933-1.html # bug 833371
|
||||
load 794463-1.html
|
||||
load 802926-1.html
|
||||
load 916128-1.html
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
#include "mozilla/RefPtr.h"
|
||||
|
||||
#define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
|
||||
{ 0x8b8da863, 0xd151, 0x4014, \
|
||||
{ 0x8b, 0xdc, 0x62, 0xb5, 0x0d, 0xc0, 0x2b, 0x62 } }
|
||||
{ 0x9a6a5bdf, 0x1261, 0x4057, \
|
||||
{ 0x85, 0xcc, 0xaf, 0x97, 0x6c, 0x36, 0x99, 0xa9 } }
|
||||
|
||||
class gfxContext;
|
||||
class gfxASurface;
|
||||
|
@ -71,6 +71,9 @@ public:
|
|||
GraphicsFilter aFilter,
|
||||
uint32_t aFlags = RenderFlagPremultAlpha) = 0;
|
||||
|
||||
// Creates an image buffer. Returns null on failure.
|
||||
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) = 0;
|
||||
|
||||
// Gives you a stream containing the image represented by this context.
|
||||
// The format is given in aMimeTime, for example "image/png".
|
||||
//
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include "imgIEncoder.h"
|
||||
#include "ImageEncoder.h"
|
||||
|
||||
#include "gfxContext.h"
|
||||
#include "gfxASurface.h"
|
||||
|
@ -928,16 +928,18 @@ CanvasRenderingContext2D::SetDimensions(int32_t width, int32_t height)
|
|||
{
|
||||
ClearTarget();
|
||||
|
||||
// Zero sized surfaces cause issues, so just go with 1x1.
|
||||
if (height == 0 || width == 0) {
|
||||
mZero = true;
|
||||
mWidth = 1;
|
||||
mHeight = 1;
|
||||
} else {
|
||||
// Zero sized surfaces can cause problems.
|
||||
mZero = false;
|
||||
if (height == 0) {
|
||||
height = 1;
|
||||
mZero = true;
|
||||
}
|
||||
if (width == 0) {
|
||||
width = 1;
|
||||
mZero = true;
|
||||
}
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1050,71 +1052,71 @@ CanvasRenderingContext2D::Render(gfxContext *ctx, GraphicsFilter aFilter, uint32
|
|||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
|
||||
const PRUnichar *aEncoderOptions,
|
||||
nsIInputStream **aStream)
|
||||
void
|
||||
CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer,
|
||||
int32_t* aFormat)
|
||||
{
|
||||
EnsureTarget();
|
||||
if (!IsTargetValid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
*aImageBuffer = nullptr;
|
||||
*aFormat = 0;
|
||||
|
||||
nsRefPtr<gfxASurface> surface;
|
||||
|
||||
if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
nsresult rv = GetThebesSurface(getter_AddRefs(surface));
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
|
||||
static const fallible_t fallible = fallible_t();
|
||||
nsAutoArrayPtr<char> conid(new (fallible) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
|
||||
|
||||
if (!conid) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
strcpy(conid, encoderPrefix);
|
||||
strcat(conid, aMimeType);
|
||||
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
|
||||
if (!encoder) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsAutoArrayPtr<uint8_t> imageBuffer(new (fallible) uint8_t[mWidth * mHeight * 4]);
|
||||
uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
|
||||
if (!imageBuffer) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxImageSurface> imgsurf =
|
||||
new gfxImageSurface(imageBuffer.get(),
|
||||
new gfxImageSurface(imageBuffer,
|
||||
gfxIntSize(mWidth, mHeight),
|
||||
mWidth * 4,
|
||||
gfxImageFormatARGB32);
|
||||
|
||||
if (!imgsurf || imgsurf->CairoStatus()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
delete[] imageBuffer;
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
|
||||
|
||||
if (!ctx || ctx->HasError()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
delete[] imageBuffer;
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
ctx->SetSource(surface, gfxPoint(0, 0));
|
||||
ctx->Paint();
|
||||
|
||||
rv = encoder->InitFromData(imageBuffer.get(),
|
||||
mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
|
||||
imgIEncoder::INPUT_FORMAT_HOSTARGB,
|
||||
nsDependentString(aEncoderOptions));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
*aImageBuffer = imageBuffer;
|
||||
*aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
|
||||
}
|
||||
|
||||
return CallQueryInterface(encoder, aStream);
|
||||
NS_IMETHODIMP
|
||||
CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
|
||||
const PRUnichar *aEncoderOptions,
|
||||
nsIInputStream **aStream)
|
||||
{
|
||||
uint8_t* imageBuffer = nullptr;
|
||||
int32_t format = 0;
|
||||
GetImageBuffer(&imageBuffer, &format);
|
||||
if (!imageBuffer) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCString enccid("@mozilla.org/image/encoder;2?type=");
|
||||
enccid += aMimeType;
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
|
||||
if (!encoder) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
|
||||
encoder, aEncoderOptions, aStream);
|
||||
}
|
||||
|
||||
SurfaceFormat
|
||||
|
@ -3815,6 +3817,9 @@ NS_IMETHODIMP
|
|||
CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
|
||||
{
|
||||
EnsureTarget();
|
||||
if (!IsTargetValid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxASurface> thebesSurface =
|
||||
gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "imgIEncoder.h"
|
||||
|
||||
class nsXULElement;
|
||||
|
||||
|
@ -460,6 +461,8 @@ public:
|
|||
|
||||
friend class CanvasRenderingContext2DUserData;
|
||||
|
||||
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
|
||||
|
||||
protected:
|
||||
nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
|
||||
uint32_t aWidth, uint32_t aHeight,
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gfxImageSurface.h"
|
||||
#include "ImageEncoder.h"
|
||||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class EncodingCompleteEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
EncodingCompleteEvent(nsIScriptContext* aScriptContext,
|
||||
nsIThread* aEncoderThread,
|
||||
FileCallback& aCallback)
|
||||
: mImgSize(0)
|
||||
, mType()
|
||||
, mImgData(nullptr)
|
||||
, mScriptContext(aScriptContext)
|
||||
, mEncoderThread(aEncoderThread)
|
||||
, mCallback(&aCallback)
|
||||
, mFailed(false)
|
||||
{}
|
||||
virtual ~EncodingCompleteEvent() {}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mozilla::ErrorResult rv;
|
||||
|
||||
if (!mFailed) {
|
||||
nsRefPtr<nsDOMMemoryFile> blob =
|
||||
new nsDOMMemoryFile(mImgData, mImgSize, mType);
|
||||
|
||||
if (mScriptContext) {
|
||||
JSContext* jsContext = mScriptContext->GetNativeContext();
|
||||
if (jsContext) {
|
||||
JS_updateMallocCounter(jsContext, mImgSize);
|
||||
}
|
||||
}
|
||||
|
||||
mCallback->Call(blob, rv);
|
||||
}
|
||||
|
||||
// These members aren't thread-safe. We're making sure that they're being
|
||||
// released on the main thread here. Otherwise, they could be getting
|
||||
// released by EncodingRunnable's destructor on the encoding thread
|
||||
// (bug 916128).
|
||||
mScriptContext = nullptr;
|
||||
mCallback = nullptr;
|
||||
|
||||
mEncoderThread->Shutdown();
|
||||
return rv.ErrorCode();
|
||||
}
|
||||
|
||||
void SetMembers(void* aImgData, uint64_t aImgSize, const nsAutoString& aType)
|
||||
{
|
||||
mImgData = aImgData;
|
||||
mImgSize = aImgSize;
|
||||
mType = aType;
|
||||
}
|
||||
|
||||
void SetFailed()
|
||||
{
|
||||
mFailed = true;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t mImgSize;
|
||||
nsAutoString mType;
|
||||
void* mImgData;
|
||||
nsCOMPtr<nsIScriptContext> mScriptContext;
|
||||
nsCOMPtr<nsIThread> mEncoderThread;
|
||||
nsRefPtr<FileCallback> mCallback;
|
||||
bool mFailed;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(EncodingCompleteEvent, nsIRunnable);
|
||||
|
||||
class EncodingRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
EncodingRunnable(const nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
uint8_t* aImageBuffer,
|
||||
imgIEncoder* aEncoder,
|
||||
EncodingCompleteEvent* aEncodingCompleteEvent,
|
||||
int32_t aFormat,
|
||||
const nsIntSize aSize,
|
||||
bool aUsingCustomOptions)
|
||||
: mType(aType)
|
||||
, mOptions(aOptions)
|
||||
, mImageBuffer(aImageBuffer)
|
||||
, mEncoder(aEncoder)
|
||||
, mEncodingCompleteEvent(aEncodingCompleteEvent)
|
||||
, mFormat(aFormat)
|
||||
, mSize(aSize)
|
||||
, mUsingCustomOptions(aUsingCustomOptions)
|
||||
{}
|
||||
virtual ~EncodingRunnable() {}
|
||||
|
||||
nsresult ProcessImageData(uint64_t* aImgSize, void** aImgData)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
nsresult rv = ImageEncoder::ExtractDataInternal(mType,
|
||||
mOptions,
|
||||
mImageBuffer,
|
||||
mFormat,
|
||||
mSize,
|
||||
nullptr,
|
||||
getter_AddRefs(stream),
|
||||
mEncoder);
|
||||
|
||||
// If there are unrecognized custom parse options, we should fall back to
|
||||
// the default values for the encoder without any options at all.
|
||||
if (rv == NS_ERROR_INVALID_ARG && mUsingCustomOptions) {
|
||||
rv = ImageEncoder::ExtractDataInternal(mType,
|
||||
EmptyString(),
|
||||
mImageBuffer,
|
||||
mFormat,
|
||||
mSize,
|
||||
nullptr,
|
||||
getter_AddRefs(stream),
|
||||
mEncoder);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stream->Available(aImgSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(*aImgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
|
||||
|
||||
rv = NS_ReadInputStreamToBuffer(stream, aImgData, *aImgSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
uint64_t imgSize;
|
||||
void* imgData = nullptr;
|
||||
|
||||
nsresult rv = ProcessImageData(&imgSize, &imgData);
|
||||
if (NS_FAILED(rv)) {
|
||||
mEncodingCompleteEvent->SetFailed();
|
||||
} else {
|
||||
mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
|
||||
}
|
||||
rv = NS_DispatchToMainThread(mEncodingCompleteEvent, NS_DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Better to leak than to crash.
|
||||
mEncodingCompleteEvent.forget();
|
||||
return rv;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
private:
|
||||
nsAutoString mType;
|
||||
nsAutoString mOptions;
|
||||
nsAutoArrayPtr<uint8_t> mImageBuffer;
|
||||
nsCOMPtr<imgIEncoder> mEncoder;
|
||||
nsRefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
|
||||
int32_t mFormat;
|
||||
const nsIntSize mSize;
|
||||
bool mUsingCustomOptions;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(EncodingRunnable, nsIRunnable)
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
ImageEncoder::ExtractData(nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
nsIInputStream** aStream)
|
||||
{
|
||||
nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
|
||||
if (!encoder) {
|
||||
return NS_IMAGELIB_ERROR_NO_ENCODER;
|
||||
}
|
||||
|
||||
return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize, aContext,
|
||||
aStream, encoder);
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
ImageEncoder::ExtractDataAsync(nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
bool aUsingCustomOptions,
|
||||
uint8_t* aImageBuffer,
|
||||
int32_t aFormat,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
nsIScriptContext* aScriptContext,
|
||||
FileCallback& aCallback)
|
||||
{
|
||||
nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
|
||||
if (!encoder) {
|
||||
return NS_IMAGELIB_ERROR_NO_ENCODER;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread> encoderThread;
|
||||
nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsRefPtr<EncodingCompleteEvent> completeEvent =
|
||||
new EncodingCompleteEvent(aScriptContext, encoderThread, aCallback);
|
||||
|
||||
nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
|
||||
aOptions,
|
||||
aImageBuffer,
|
||||
encoder,
|
||||
completeEvent,
|
||||
aFormat,
|
||||
aSize,
|
||||
aUsingCustomOptions);
|
||||
return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
/*static*/ nsresult
|
||||
ImageEncoder::GetInputStream(int32_t aWidth,
|
||||
int32_t aHeight,
|
||||
uint8_t* aImageBuffer,
|
||||
int32_t aFormat,
|
||||
imgIEncoder* aEncoder,
|
||||
const PRUnichar* aEncoderOptions,
|
||||
nsIInputStream** aStream)
|
||||
{
|
||||
nsresult rv =
|
||||
aEncoder->InitFromData(aImageBuffer,
|
||||
aWidth * aHeight * 4, aWidth, aHeight, aWidth * 4,
|
||||
aFormat,
|
||||
nsDependentString(aEncoderOptions));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return CallQueryInterface(aEncoder, aStream);
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
ImageEncoder::ExtractDataInternal(const nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
uint8_t* aImageBuffer,
|
||||
int32_t aFormat,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
nsIInputStream** aStream,
|
||||
imgIEncoder* aEncoder)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> imgStream;
|
||||
|
||||
// get image bytes
|
||||
nsresult rv;
|
||||
if (aImageBuffer) {
|
||||
rv = ImageEncoder::GetInputStream(
|
||||
aSize.width,
|
||||
aSize.height,
|
||||
aImageBuffer,
|
||||
aFormat,
|
||||
aEncoder,
|
||||
nsPromiseFlatString(aOptions).get(),
|
||||
getter_AddRefs(imgStream));
|
||||
} else if (aContext) {
|
||||
NS_ConvertUTF16toUTF8 encoderType(aType);
|
||||
rv = aContext->GetInputStream(encoderType.get(),
|
||||
nsPromiseFlatString(aOptions).get(),
|
||||
getter_AddRefs(imgStream));
|
||||
} else {
|
||||
// no context, so we have to encode an empty image
|
||||
// note that if we didn't have a current context, the spec says we're
|
||||
// supposed to just return transparent black pixels of the canvas
|
||||
// dimensions.
|
||||
nsRefPtr<gfxImageSurface> emptyCanvas =
|
||||
new gfxImageSurface(gfxIntSize(aSize.width, aSize.height),
|
||||
gfxImageFormatARGB32);
|
||||
if (emptyCanvas->CairoStatus()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
rv = aEncoder->InitFromData(emptyCanvas->Data(),
|
||||
aSize.width * aSize.height * 4,
|
||||
aSize.width,
|
||||
aSize.height,
|
||||
aSize.width * 4,
|
||||
imgIEncoder::INPUT_FORMAT_HOSTARGB,
|
||||
aOptions);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
imgStream = do_QueryInterface(aEncoder);
|
||||
}
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
imgStream.forget(aStream);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<imgIEncoder>
|
||||
ImageEncoder::GetImageEncoder(nsAString& aType)
|
||||
{
|
||||
// Get an image encoder for the media type.
|
||||
nsCString encoderCID("@mozilla.org/image/encoder;2?type=");
|
||||
NS_ConvertUTF16toUTF8 encoderType(aType);
|
||||
encoderCID += encoderType;
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
|
||||
|
||||
if (!encoder && aType != NS_LITERAL_STRING("image/png")) {
|
||||
// Unable to create an encoder instance of the specified type. Falling back
|
||||
// to PNG.
|
||||
aType.AssignLiteral("image/png");
|
||||
nsCString PNGEncoderCID("@mozilla.org/image/encoder;2?type=image/png");
|
||||
encoder = do_CreateInstance(PNGEncoderCID.get());
|
||||
}
|
||||
|
||||
return encoder.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,92 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef ImageEncoder_h
|
||||
#define ImageEncoder_h
|
||||
|
||||
#include "imgIEncoder.h"
|
||||
#include "nsDOMFile.h"
|
||||
#include "nsError.h"
|
||||
#include "mozilla/dom/HTMLCanvasElementBinding.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsSize.h"
|
||||
|
||||
class nsICanvasRenderingContextInternal;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class EncodingRunnable;
|
||||
|
||||
class ImageEncoder
|
||||
{
|
||||
public:
|
||||
// Extracts data synchronously and gives you a stream containing the image
|
||||
// represented by aContext. aType may change to "image/png" if we had to fall
|
||||
// back to a PNG encoder. A return value of NS_OK implies successful data
|
||||
// extraction. If there are any unrecognized custom parse options in
|
||||
// aOptions, NS_ERROR_INVALID_ARG will be returned. When encountering this
|
||||
// error it is usual to call this function again without any options at all.
|
||||
static nsresult ExtractData(nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
nsIInputStream** aStream);
|
||||
|
||||
// Extracts data asynchronously. aType may change to "image/png" if we had to
|
||||
// fall back to a PNG encoder. aOptions are the options to be passed to the
|
||||
// encoder and aUsingCustomOptions specifies whether custom parse options were
|
||||
// used (i.e. by using -moz-parse-options). If there are any unrecognized
|
||||
// custom parse options, we fall back to the default values for the encoder
|
||||
// without any options at all. A return value of NS_OK only implies
|
||||
// successful dispatching of the extraction step to the encoding thread.
|
||||
static nsresult ExtractDataAsync(nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
bool aUsingCustomOptions,
|
||||
uint8_t* aImageBuffer,
|
||||
int32_t aFormat,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
nsIScriptContext* aScriptContext,
|
||||
FileCallback& aCallback);
|
||||
|
||||
// Gives you a stream containing the image represented by aImageBuffer.
|
||||
// The format is given in aFormat, for example
|
||||
// imgIEncoder::INPUT_FORMAT_HOSTARGB.
|
||||
static nsresult GetInputStream(int32_t aWidth,
|
||||
int32_t aHeight,
|
||||
uint8_t* aImageBuffer,
|
||||
int32_t aFormat,
|
||||
imgIEncoder* aEncoder,
|
||||
const PRUnichar* aEncoderOptions,
|
||||
nsIInputStream** aStream);
|
||||
|
||||
private:
|
||||
// When called asynchronously, aContext is null.
|
||||
static nsresult
|
||||
ExtractDataInternal(const nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
uint8_t* aImageBuffer,
|
||||
int32_t aFormat,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
nsIInputStream** aStream,
|
||||
imgIEncoder* aEncoder);
|
||||
|
||||
// Creates and returns an encoder instance of the type specified in aType.
|
||||
// aType may change to "image/png" if no instance of the original type could
|
||||
// be created and we had to fall back to a PNG encoder. A return value of
|
||||
// NULL should be interpreted as NS_IMAGELIB_ERROR_NO_ENCODER and aType is
|
||||
// undefined in this case.
|
||||
static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
|
||||
|
||||
friend class EncodingRunnable;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ImageEncoder_h
|
|
@ -22,5 +22,6 @@ INCLUDES += \
|
|||
-I$(srcdir)/../../html/content/src \
|
||||
-I$(srcdir)/../../../js/xpconnect/src \
|
||||
-I$(srcdir)/../../../dom/base \
|
||||
-I$(srcdir)/../../../image/src \
|
||||
-I$(topsrcdir)/content/xul/content/src \
|
||||
$(NULL)
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
#include "nsIVariant.h"
|
||||
|
||||
#include "imgIEncoder.h"
|
||||
#include "ImageEncoder.h"
|
||||
|
||||
#include "gfxContext.h"
|
||||
#include "gfxPattern.h"
|
||||
|
@ -381,8 +381,10 @@ WebGLContext::SetDimensions(int32_t width, int32_t height)
|
|||
return NS_OK;
|
||||
|
||||
// Zero-sized surfaces can cause problems.
|
||||
if (width == 0 || height == 0) {
|
||||
if (width == 0) {
|
||||
width = 1;
|
||||
}
|
||||
if (height == 0) {
|
||||
height = 1;
|
||||
}
|
||||
|
||||
|
@ -733,6 +735,54 @@ void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
|
||||
{
|
||||
*aImageBuffer = nullptr;
|
||||
*aFormat = 0;
|
||||
|
||||
nsRefPtr<gfxImageSurface> imgsurf =
|
||||
new gfxImageSurface(gfxIntSize(mWidth, mHeight),
|
||||
gfxImageFormatARGB32);
|
||||
|
||||
if (!imgsurf || imgsurf->CairoStatus()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
|
||||
if (!ctx || ctx->HasError()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use Render() to make sure that appropriate y-flip gets applied
|
||||
uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
|
||||
nsresult rv = Render(ctx, GraphicsFilter::FILTER_NEAREST, flags);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
|
||||
if (!mOptions.premultipliedAlpha) {
|
||||
// We need to convert to INPUT_FORMAT_RGBA, otherwise
|
||||
// we are automatically considered premult, and unpremult'd.
|
||||
// Yes, it is THAT silly.
|
||||
// Except for different lossy conversions by color,
|
||||
// we could probably just change the label, and not change the data.
|
||||
gfxUtils::ConvertBGRAtoRGBA(imgsurf);
|
||||
format = imgIEncoder::INPUT_FORMAT_RGBA;
|
||||
}
|
||||
|
||||
static const fallible_t fallible = fallible_t();
|
||||
uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
|
||||
if (!imageBuffer) {
|
||||
return;
|
||||
}
|
||||
memcpy(imageBuffer, imgsurf->Data(), mWidth * mHeight * 4);
|
||||
|
||||
*aImageBuffer = imageBuffer;
|
||||
*aFormat = format;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLContext::GetInputStream(const char* aMimeType,
|
||||
const PRUnichar* aEncoderOptions,
|
||||
|
@ -742,48 +792,22 @@ WebGLContext::GetInputStream(const char* aMimeType,
|
|||
if (!gl)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
|
||||
gfxImageFormatARGB32);
|
||||
if (surf->CairoStatus() != 0)
|
||||
uint8_t* imageBuffer = nullptr;
|
||||
int32_t format = 0;
|
||||
GetImageBuffer(&imageBuffer, &format);
|
||||
if (!imageBuffer) {
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsRefPtr<gfxContext> tmpcx = new gfxContext(surf);
|
||||
// Use Render() to make sure that appropriate y-flip gets applied
|
||||
uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
|
||||
nsresult rv = Render(tmpcx, GraphicsFilter::FILTER_NEAREST, flags);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
|
||||
nsAutoArrayPtr<char> conid(new char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
|
||||
|
||||
strcpy(conid, encoderPrefix);
|
||||
strcat(conid, aMimeType);
|
||||
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
|
||||
if (!encoder)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
int format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
|
||||
if (!mOptions.premultipliedAlpha) {
|
||||
// We need to convert to INPUT_FORMAT_RGBA, otherwise
|
||||
// we are automatically considered premult, and unpremult'd.
|
||||
// Yes, it is THAT silly.
|
||||
// Except for different lossy conversions by color,
|
||||
// we could probably just change the label, and not change the data.
|
||||
gfxUtils::ConvertBGRAtoRGBA(surf);
|
||||
format = imgIEncoder::INPUT_FORMAT_RGBA;
|
||||
}
|
||||
|
||||
rv = encoder->InitFromData(surf->Data(),
|
||||
mWidth * mHeight * 4,
|
||||
mWidth, mHeight,
|
||||
surf->Stride(),
|
||||
format,
|
||||
nsDependentString(aEncoderOptions));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCString enccid("@mozilla.org/image/encoder;2?type=");
|
||||
enccid += aMimeType;
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
|
||||
if (!encoder) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return CallQueryInterface(encoder, aStream);
|
||||
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
|
||||
encoder, aEncoderOptions, aStream);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -167,6 +167,7 @@ public:
|
|||
NS_IMETHOD Render(gfxContext *ctx,
|
||||
GraphicsFilter f,
|
||||
uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
|
||||
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
|
||||
NS_IMETHOD GetInputStream(const char* aMimeType,
|
||||
const PRUnichar* aEncoderOptions,
|
||||
nsIInputStream **aStream) MOZ_OVERRIDE;
|
||||
|
|
|
@ -22,6 +22,7 @@ CPP_SOURCES += [
|
|||
'DocumentRendererChild.cpp',
|
||||
'DocumentRendererParent.cpp',
|
||||
'ImageData.cpp',
|
||||
'ImageEncoder.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WEBGL']:
|
||||
|
|
|
@ -7,42 +7,43 @@
|
|||
<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
|
||||
<script>
|
||||
|
||||
var gCompares = 0;
|
||||
|
||||
function compareAsync(file, canvas, type)
|
||||
function compareAsync(file, canvas, type, callback)
|
||||
{
|
||||
++gCompares;
|
||||
|
||||
var reader = new FileReader();
|
||||
reader.onload =
|
||||
function(e) {
|
||||
is(e.target.result, canvas.toDataURL(type),
|
||||
"<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
|
||||
if (--gCompares == 0) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
callback(canvas);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
function test1(canvas)
|
||||
{
|
||||
var pngfile = canvas.mozGetAsFile("foo.png");
|
||||
is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
|
||||
compareAsync(pngfile, canvas, "image/png", test2);
|
||||
is(pngfile.name, "foo.png", "File name should be what we passed in");
|
||||
}
|
||||
|
||||
function test2(canvas)
|
||||
{
|
||||
var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
|
||||
is(jpegfile.type, "image/jpeg",
|
||||
"When a valid type is specified that should be returned");
|
||||
compareAsync(jpegfile, canvas, "image/jpeg", SimpleTest.finish);
|
||||
is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function () {
|
||||
|
||||
var canvas = document.getElementById('c');
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
|
||||
|
||||
var pngfile = canvas.mozGetAsFile("foo.png");
|
||||
is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
|
||||
compareAsync(pngfile, canvas, "image/png");
|
||||
is(pngfile.name, "foo.png", "File name should be what we passed in");
|
||||
|
||||
var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
|
||||
is(jpegfile.type, "image/jpeg",
|
||||
"When a valid type is specified that should be returned");
|
||||
compareAsync(jpegfile, canvas, "image/jpeg");
|
||||
is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
|
||||
test1(canvas);
|
||||
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE HTML>
|
||||
<title>Canvas test: mozGetAsFile</title>
|
||||
<title>Canvas test: toBlob</title>
|
||||
<script src="/MochiKit/MochiKit.js"></script>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
|
@ -7,9 +7,7 @@
|
|||
<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
|
||||
<script>
|
||||
|
||||
var gCompares = 2;
|
||||
|
||||
function BlobListener(type, canvas, file)
|
||||
function BlobListener(type, canvas, callback, file)
|
||||
{
|
||||
is(file.type, type,
|
||||
"When a valid type is specified that should be returned");
|
||||
|
@ -18,23 +16,31 @@ function BlobListener(type, canvas, file)
|
|||
function(e) {
|
||||
is(e.target.result, canvas.toDataURL(type),
|
||||
"<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
|
||||
if (--gCompares == 0) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
callback(canvas);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
function test1(canvas)
|
||||
{
|
||||
canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas, test2));
|
||||
}
|
||||
|
||||
function test2(canvas)
|
||||
{
|
||||
canvas.toBlob(
|
||||
BlobListener.bind(undefined, "image/jpeg", canvas, SimpleTest.finish),
|
||||
"image/jpeg");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function () {
|
||||
|
||||
var canvas = document.getElementById('c');
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
|
||||
|
||||
canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas));
|
||||
canvas.toBlob(BlobListener.bind(undefined, "image/jpeg", canvas), "image/jpeg");
|
||||
test1(canvas);
|
||||
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -227,10 +227,9 @@ protected:
|
|||
const JS::Value& aEncoderOptions,
|
||||
nsAString& aParams,
|
||||
bool* usingCustomParseOptions);
|
||||
nsresult ExtractData(const nsAString& aType,
|
||||
nsresult ExtractData(nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
nsIInputStream** aStream,
|
||||
bool& aFellBackToPNG);
|
||||
nsIInputStream** aStream);
|
||||
nsresult ToDataURLImpl(JSContext* aCx,
|
||||
const nsAString& aMimeType,
|
||||
const JS::Value& aEncoderOptions,
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
|
||||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
|
||||
#include "Layers.h"
|
||||
#include "imgIEncoder.h"
|
||||
#include "ImageEncoder.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "gfxImageSurface.h"
|
||||
#include "Layers.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||
|
@ -23,6 +23,7 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsDisplayList.h"
|
||||
#include "nsDOMFile.h"
|
||||
#include "nsDOMJSUtils.h"
|
||||
#include "nsFrameManager.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsITimer.h"
|
||||
|
@ -46,29 +47,6 @@ namespace {
|
|||
typedef mozilla::dom::HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement
|
||||
HTMLImageOrCanvasOrVideoElement;
|
||||
|
||||
class ToBlobRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
ToBlobRunnable(mozilla::dom::FileCallback& aCallback,
|
||||
nsIDOMBlob* aBlob)
|
||||
: mCallback(&aCallback),
|
||||
mBlob(aBlob)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
mozilla::ErrorResult rv;
|
||||
mCallback->Call(mBlob, rv);
|
||||
return rv.ErrorCode();
|
||||
}
|
||||
private:
|
||||
nsRefPtr<mozilla::dom::FileCallback> mCallback;
|
||||
nsCOMPtr<nsIDOMBlob> mBlob;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -369,10 +347,10 @@ HTMLCanvasElement::MozFetchAsStream(nsIInputStreamCallback *aCallback,
|
|||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsresult rv;
|
||||
bool fellBackToPNG = false;
|
||||
nsCOMPtr<nsIInputStream> inputData;
|
||||
|
||||
rv = ExtractData(aType, EmptyString(), getter_AddRefs(inputData), fellBackToPNG);
|
||||
nsAutoString type(aType);
|
||||
rv = ExtractData(type, EmptyString(), getter_AddRefs(inputData));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> asyncData = do_QueryInterface(inputData, &rv);
|
||||
|
@ -404,68 +382,15 @@ HTMLCanvasElement::GetMozPrintCallback() const
|
|||
}
|
||||
|
||||
nsresult
|
||||
HTMLCanvasElement::ExtractData(const nsAString& aType,
|
||||
HTMLCanvasElement::ExtractData(nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
nsIInputStream** aStream,
|
||||
bool& aFellBackToPNG)
|
||||
nsIInputStream** aStream)
|
||||
{
|
||||
// note that if we don't have a current context, the spec says we're
|
||||
// supposed to just return transparent black pixels of the canvas
|
||||
// dimensions.
|
||||
nsRefPtr<gfxImageSurface> emptyCanvas;
|
||||
nsIntSize size = GetWidthHeight();
|
||||
if (!mCurrentContext) {
|
||||
emptyCanvas = new gfxImageSurface(gfxIntSize(size.width, size.height), gfxImageFormatARGB32);
|
||||
if (emptyCanvas->CairoStatus()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// get image bytes
|
||||
nsCOMPtr<nsIInputStream> imgStream;
|
||||
NS_ConvertUTF16toUTF8 encoderType(aType);
|
||||
|
||||
try_again:
|
||||
if (mCurrentContext) {
|
||||
rv = mCurrentContext->GetInputStream(encoderType.get(),
|
||||
nsPromiseFlatString(aOptions).get(),
|
||||
getter_AddRefs(imgStream));
|
||||
} else {
|
||||
// no context, so we have to encode the empty image we created above
|
||||
nsCString enccid("@mozilla.org/image/encoder;2?type=");
|
||||
enccid += encoderType;
|
||||
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get(), &rv);
|
||||
if (NS_SUCCEEDED(rv) && encoder) {
|
||||
rv = encoder->InitFromData(emptyCanvas->Data(),
|
||||
size.width * size.height * 4,
|
||||
size.width,
|
||||
size.height,
|
||||
size.width * 4,
|
||||
imgIEncoder::INPUT_FORMAT_HOSTARGB,
|
||||
aOptions);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
imgStream = do_QueryInterface(encoder);
|
||||
}
|
||||
} else {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv) && !aFellBackToPNG) {
|
||||
// Try image/png instead.
|
||||
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
|
||||
aFellBackToPNG = true;
|
||||
encoderType.AssignLiteral("image/png");
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
imgStream.forget(aStream);
|
||||
return NS_OK;
|
||||
return ImageEncoder::ExtractData(aType,
|
||||
aOptions,
|
||||
GetSize(),
|
||||
mCurrentContext,
|
||||
aStream);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -516,8 +441,6 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
|
|||
const JS::Value& aEncoderOptions,
|
||||
nsAString& aDataURL)
|
||||
{
|
||||
bool fallbackToPNG = false;
|
||||
|
||||
nsIntSize size = GetWidthHeight();
|
||||
if (size.height == 0 || size.width == 0) {
|
||||
aDataURL = NS_LITERAL_STRING("data:,");
|
||||
|
@ -538,23 +461,18 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
|
|||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
rv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
|
||||
rv = ExtractData(type, params, getter_AddRefs(stream));
|
||||
|
||||
// If there are unrecognized custom parse options, we should fall back to
|
||||
// the default values for the encoder without any options at all.
|
||||
if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
|
||||
fallbackToPNG = false;
|
||||
rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
|
||||
rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// build data URL string
|
||||
if (fallbackToPNG)
|
||||
aDataURL = NS_LITERAL_STRING("data:image/png;base64,");
|
||||
else
|
||||
aDataURL = NS_LITERAL_STRING("data:") + type +
|
||||
NS_LITERAL_STRING(";base64,");
|
||||
aDataURL = NS_LITERAL_STRING("data:") + type + NS_LITERAL_STRING(";base64,");
|
||||
|
||||
uint64_t count;
|
||||
rv = stream->Available(&count);
|
||||
|
@ -564,7 +482,6 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
|
|||
return Base64EncodeInputStream(stream, aDataURL, (uint32_t)count, aDataURL.Length());
|
||||
}
|
||||
|
||||
// XXXkhuey the encoding should be off the main thread, but we're lazy.
|
||||
void
|
||||
HTMLCanvasElement::ToBlob(JSContext* aCx,
|
||||
FileCallback& aCallback,
|
||||
|
@ -597,58 +514,35 @@ HTMLCanvasElement::ToBlob(JSContext* aCx,
|
|||
|
||||
#ifdef DEBUG
|
||||
if (mCurrentContext) {
|
||||
// We disallow canvases of width or height zero, and set them to 1, so
|
||||
// we will have a discrepancy with the sizes of the canvas and the context.
|
||||
// That discrepancy is OK, the rest are not.
|
||||
nsIntSize elementSize = GetWidthHeight();
|
||||
MOZ_ASSERT(elementSize.width == mCurrentContext->GetWidth());
|
||||
MOZ_ASSERT(elementSize.height == mCurrentContext->GetHeight());
|
||||
MOZ_ASSERT(elementSize.width == mCurrentContext->GetWidth() ||
|
||||
(elementSize.width == 0 && mCurrentContext->GetWidth() == 1));
|
||||
MOZ_ASSERT(elementSize.height == mCurrentContext->GetHeight() ||
|
||||
(elementSize.height == 0 && mCurrentContext->GetHeight() == 1));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool fallbackToPNG = false;
|
||||
nsCOMPtr<nsIScriptContext> scriptContext =
|
||||
GetScriptContextFromJSContext(nsContentUtils::GetCurrentJSContext());
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
aRv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
|
||||
// If there are unrecognized custom parse options, we should fall back to
|
||||
// the default values for the encoder without any options at all.
|
||||
if (aRv.ErrorCode() == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
|
||||
fallbackToPNG = false;
|
||||
aRv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
|
||||
uint8_t* imageBuffer = nullptr;
|
||||
int32_t format = 0;
|
||||
if (mCurrentContext) {
|
||||
mCurrentContext->GetImageBuffer(&imageBuffer, &format);
|
||||
}
|
||||
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fallbackToPNG) {
|
||||
type.AssignLiteral("image/png");
|
||||
}
|
||||
|
||||
uint64_t imgSize;
|
||||
aRv = stream->Available(&imgSize);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
if (imgSize > UINT32_MAX) {
|
||||
aRv.Throw(NS_ERROR_FILE_TOO_BIG);
|
||||
return;
|
||||
}
|
||||
|
||||
void* imgData = nullptr;
|
||||
aRv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The DOMFile takes ownership of the buffer
|
||||
nsRefPtr<nsDOMMemoryFile> blob =
|
||||
new nsDOMMemoryFile(imgData, imgSize, type);
|
||||
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContext();
|
||||
if (cx) {
|
||||
JS_updateMallocCounter(cx, imgSize);
|
||||
}
|
||||
|
||||
nsRefPtr<ToBlobRunnable> runnable = new ToBlobRunnable(aCallback, blob);
|
||||
aRv = NS_DispatchToCurrentThread(runnable);
|
||||
aRv = ImageEncoder::ExtractDataAsync(type,
|
||||
params,
|
||||
usingCustomParseOptions,
|
||||
imageBuffer,
|
||||
format,
|
||||
GetSize(),
|
||||
mCurrentContext,
|
||||
scriptContext,
|
||||
aCallback);
|
||||
}
|
||||
|
||||
already_AddRefed<nsIDOMFile>
|
||||
|
@ -682,17 +576,10 @@ HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
|
|||
const nsAString& aType,
|
||||
nsIDOMFile** aResult)
|
||||
{
|
||||
bool fallbackToPNG = false;
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
nsresult rv = ExtractData(aType, EmptyString(), getter_AddRefs(stream),
|
||||
fallbackToPNG);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoString type(aType);
|
||||
if (fallbackToPNG) {
|
||||
type.AssignLiteral("image/png");
|
||||
}
|
||||
nsresult rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uint64_t imgSize;
|
||||
rv = stream->Available(&imgSize);
|
||||
|
|
|
@ -22,6 +22,7 @@ INCLUDES += \
|
|||
-I$(srcdir)/../../../../editor/libeditor/text \
|
||||
-I$(srcdir)/../../../../editor/txmgr/src \
|
||||
-I$(srcdir)/../../../../netwerk/base/src \
|
||||
-I$(srcdir)/../../../../content/canvas/src \
|
||||
-I$(srcdir) \
|
||||
-I$(topsrcdir)/xpcom/ds \
|
||||
-I$(topsrcdir)/content/media/ \
|
||||
|
|
|
@ -16,25 +16,20 @@
|
|||
|
||||
const int32_t txExecutionState::kMaxRecursionDepth = 20000;
|
||||
|
||||
nsresult txLoadedDocumentsHash::init(txXPathNode* aSourceDocument)
|
||||
void
|
||||
txLoadedDocumentsHash::init(txXPathNode* aSourceDocument)
|
||||
{
|
||||
mSourceDocument = aSourceDocument;
|
||||
|
||||
nsAutoString baseURI;
|
||||
txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
|
||||
|
||||
txLoadedDocumentEntry* entry = PutEntry(baseURI);
|
||||
if (!entry) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
entry->mDocument = mSourceDocument;
|
||||
|
||||
return NS_OK;
|
||||
PutEntry(baseURI)->mDocument = mSourceDocument;
|
||||
}
|
||||
|
||||
txLoadedDocumentsHash::~txLoadedDocumentsHash()
|
||||
{
|
||||
if (mSourceDocument) {
|
||||
nsAutoString baseURI;
|
||||
txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
|
||||
|
||||
|
@ -43,6 +38,7 @@ txLoadedDocumentsHash::~txLoadedDocumentsHash()
|
|||
delete entry->mDocument.forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
txExecutionState::txExecutionState(txStylesheet* aStylesheet,
|
||||
bool aDisableLoads)
|
||||
|
@ -118,14 +114,7 @@ txExecutionState::init(const txXPathNode& aNode,
|
|||
mOutputHandler->startDocument();
|
||||
|
||||
// Set up loaded-documents-hash
|
||||
nsAutoPtr<txXPathNode> document(txXPathNodeUtils::getOwnerDocument(aNode));
|
||||
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
|
||||
|
||||
rv = mLoadedDocuments.init(document);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// loaded-documents-hash owns this now
|
||||
document.forget();
|
||||
mLoadedDocuments.init(txXPathNodeUtils::getOwnerDocument(aNode));
|
||||
|
||||
// Init members
|
||||
rv = mKeyHash.init();
|
||||
|
|
|
@ -57,11 +57,12 @@ class txLoadedDocumentsHash : public nsTHashtable<txLoadedDocumentEntry>
|
|||
{
|
||||
public:
|
||||
txLoadedDocumentsHash()
|
||||
: nsTHashtable<txLoadedDocumentEntry>(8)
|
||||
: nsTHashtable<txLoadedDocumentEntry>(8),
|
||||
mSourceDocument(nullptr)
|
||||
{
|
||||
}
|
||||
~txLoadedDocumentsHash();
|
||||
nsresult init(txXPathNode* aSourceDocument);
|
||||
void init(txXPathNode* aSourceDocument);
|
||||
|
||||
private:
|
||||
friend class txExecutionState;
|
||||
|
|
|
@ -101,10 +101,12 @@ LOCAL_INCLUDES += \
|
|||
$(NULL)
|
||||
endif
|
||||
|
||||
ABS_DIST := $(abspath $(DIST))
|
||||
|
||||
EXTRA_EXPORT_MDDEPEND_FILES := $(addsuffix .pp,$(binding_dependency_trackers))
|
||||
|
||||
EXPORTS_GENERATED_FILES := $(exported_binding_headers) $(exported_generated_events_headers)
|
||||
EXPORTS_GENERATED_DEST := $(DIST)/include/$(binding_include_path)
|
||||
EXPORTS_GENERATED_DEST := $(ABS_DIST)/include/$(binding_include_path)
|
||||
EXPORTS_GENERATED_TARGET := export
|
||||
INSTALL_TARGETS += EXPORTS_GENERATED
|
||||
|
||||
|
@ -118,7 +120,7 @@ globalgen_headers_FILES := \
|
|||
UnionConversions.h \
|
||||
UnionTypes.h \
|
||||
$(NULL)
|
||||
globalgen_headers_DEST = $(DIST)/include/mozilla/dom
|
||||
globalgen_headers_DEST = $(ABS_DIST)/include/mozilla/dom
|
||||
globalgen_headers_TARGET := export
|
||||
INSTALL_TARGETS += globalgen_headers
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIDOMMozNetworkStatsInterface;
|
||||
|
||||
[scriptable, builtinclass, uuid(3b16fe17-5583-483a-b486-b64a3243221c)]
|
||||
interface nsIDOMMozNetworkStatsData : nsISupports
|
||||
{
|
||||
|
@ -14,7 +12,7 @@ interface nsIDOMMozNetworkStatsData : nsISupports
|
|||
readonly attribute jsval date; // Date.
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(b6fc4b14-628d-4c99-bf4e-e4ed56916cbe)]
|
||||
[scriptable, builtinclass, uuid(6613ea55-b99c-44f9-91bf-d07da10b9b74)]
|
||||
interface nsIDOMMozNetworkStats : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -24,12 +22,13 @@ interface nsIDOMMozNetworkStats : nsISupports
|
|||
readonly attribute DOMString manifestURL;
|
||||
|
||||
/**
|
||||
* Network the returned data belongs to.
|
||||
* Can be 'mobile', 'wifi' or null.
|
||||
* If null, stats for both mobile and wifi are returned.
|
||||
*/
|
||||
readonly attribute nsIDOMMozNetworkStatsInterface network;
|
||||
readonly attribute DOMString connectionType;
|
||||
|
||||
/**
|
||||
* Stats for a network.
|
||||
* Stats for connectionType
|
||||
*/
|
||||
readonly attribute jsval data; // array of NetworkStatsData.
|
||||
// one element per day.
|
||||
|
|
|
@ -6,65 +6,57 @@
|
|||
|
||||
interface nsIDOMDOMRequest;
|
||||
|
||||
/**
|
||||
* Represents a data interface for which the manager is recording statistics.
|
||||
*/
|
||||
[scriptable, uuid(f540615b-d803-43ff-8200-2a9d145a5645)]
|
||||
interface nsIDOMMozNetworkStatsInterface : nsISupports
|
||||
dictionary NetworkStatsOptions
|
||||
{
|
||||
readonly attribute long type;
|
||||
|
||||
/**
|
||||
* Id value is '0' for wifi or the iccid for mobile (SIM).
|
||||
* Connection type used to filter which network stats will be returned:
|
||||
* 'mobile', 'wifi' or null.
|
||||
* If null, stats for both mobile and wifi are returned.
|
||||
*
|
||||
* Manifest URL used to retrieve network stats per app.
|
||||
* If null, system stats (regardless of the app) are returned.
|
||||
*/
|
||||
readonly attribute DOMString id;
|
||||
DOMString connectionType;
|
||||
DOMString manifestURL;
|
||||
jsval start; // date
|
||||
jsval end; // date
|
||||
};
|
||||
|
||||
[scriptable, uuid(5fbdcae6-a2cd-47b3-929f-83ac75bd4881)]
|
||||
[scriptable, uuid(87529a6c-aef6-11e1-a595-4f034275cfa6)]
|
||||
interface nsIDOMMozNetworkStatsManager : nsISupports
|
||||
{
|
||||
/**
|
||||
* Constants for known interface types.
|
||||
*/
|
||||
const long WIFI = 0;
|
||||
const long MOBILE = 1;
|
||||
|
||||
/**
|
||||
* Find samples between two dates start and end, both included.
|
||||
* Query network statistics.
|
||||
*
|
||||
* If manifestURL is provided, per-app usage is retrieved,
|
||||
* otherwise the target will be system usage.
|
||||
* If options.connectionType is not provided, return statistics for all known
|
||||
* network interfaces.
|
||||
*
|
||||
* If success, the request result will be an nsIDOMMozNetworkStats object.
|
||||
* If options.manifestURL is not provided, return statistics regardless of the app.
|
||||
*
|
||||
* If successful, the request result will be an nsIDOMMozNetworkStats object.
|
||||
*
|
||||
* If network stats are not available for some dates, then rxBytes &
|
||||
* txBytes are undefined for those dates.
|
||||
*/
|
||||
nsIDOMDOMRequest getSamples(in nsIDOMMozNetworkStatsInterface network,
|
||||
in jsval start,
|
||||
in jsval end,
|
||||
[optional] in DOMString manifestURL);
|
||||
nsIDOMDOMRequest getNetworkStats(in jsval options);
|
||||
|
||||
/**
|
||||
* Remove all stats related with the provided network from DB.
|
||||
* Return available connection types.
|
||||
*/
|
||||
nsIDOMDOMRequest clearStats(in nsIDOMMozNetworkStatsInterface network);
|
||||
readonly attribute jsval connectionTypes; // array of DOMStrings.
|
||||
|
||||
/**
|
||||
* Remove all stats in the database.
|
||||
* Clear all stats from DB.
|
||||
*/
|
||||
nsIDOMDOMRequest clearAllStats();
|
||||
nsIDOMDOMRequest clearAllData();
|
||||
|
||||
/**
|
||||
* Return currently available networks.
|
||||
*/
|
||||
readonly attribute jsval availableNetworks; // array of nsIDOMMozNetworkStatsInterface.
|
||||
|
||||
/**
|
||||
* Minimum time in milliseconds between samples stored in the database.
|
||||
* Time in seconds between samples stored in database.
|
||||
*/
|
||||
readonly attribute long sampleRate;
|
||||
|
||||
/**
|
||||
* Time in milliseconds recorded by the API until present time. All samples
|
||||
* older than maxStorageAge from now are deleted.
|
||||
* Maximum number of samples stored in the database per connection type.
|
||||
*/
|
||||
readonly attribute long long maxStorageAge;
|
||||
readonly attribute long maxStorageSamples;
|
||||
};
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsINetworkInterface;
|
||||
|
||||
[scriptable, function, uuid(5f821529-1d80-4ab5-a933-4e1b3585b6bc)]
|
||||
interface nsINetworkStatsServiceProxyCallback : nsISupports
|
||||
{
|
||||
|
@ -16,20 +14,20 @@ interface nsINetworkStatsServiceProxyCallback : nsISupports
|
|||
void notify(in boolean aResult, in jsval aMessage);
|
||||
};
|
||||
|
||||
[scriptable, uuid(facef032-3fd9-4509-a396-83d94c1a11ae)]
|
||||
[scriptable, uuid(8fbd115d-f590-474c-96dc-e2b6803ca975)]
|
||||
interface nsINetworkStatsServiceProxy : nsISupports
|
||||
{
|
||||
/*
|
||||
* An interface used to record per-app traffic data.
|
||||
* @param aAppId app id
|
||||
* @param aNetworkInterface network
|
||||
* @param aConnectionType network connection type (0 for wifi, 1 for mobile)
|
||||
* @param aTimeStamp time stamp
|
||||
* @param aRxBytes received data amount
|
||||
* @param aTxBytes transmitted data amount
|
||||
* @param aCallback an optional callback
|
||||
*/
|
||||
void saveAppStats(in unsigned long aAppId,
|
||||
in nsINetworkInterface aNetwork,
|
||||
in long aConnectionType,
|
||||
in unsigned long long aTimeStamp,
|
||||
in unsigned long long aRxBytes,
|
||||
in unsigned long long aTxBytes,
|
||||
|
|
|
@ -16,7 +16,8 @@ Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
|
|||
|
||||
const DB_NAME = "net_stats";
|
||||
const DB_VERSION = 2;
|
||||
const STORE_NAME = "net_stats";
|
||||
const STORE_NAME = "net_stats"; // Deprecated. Use "net_stats_v2" instead.
|
||||
const STORE_NAME_V2 = "net_stats_v2";
|
||||
|
||||
// Constant defining the maximum values allowed per interface. If more, older
|
||||
// will be erased.
|
||||
|
@ -25,11 +26,12 @@ const VALUES_MAX_LENGTH = 6 * 30;
|
|||
// Constant defining the rate of the samples. Daily.
|
||||
const SAMPLE_RATE = 1000 * 60 * 60 * 24;
|
||||
|
||||
this.NetworkStatsDB = function NetworkStatsDB() {
|
||||
this.NetworkStatsDB = function NetworkStatsDB(aConnectionTypes) {
|
||||
if (DEBUG) {
|
||||
debug("Constructor");
|
||||
}
|
||||
this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME]);
|
||||
this._connectionTypes = aConnectionTypes;
|
||||
this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME_V2]);
|
||||
}
|
||||
|
||||
NetworkStatsDB.prototype = {
|
||||
|
@ -42,7 +44,7 @@ NetworkStatsDB.prototype = {
|
|||
function errorCb(error) {
|
||||
txnCb(error, null);
|
||||
}
|
||||
return this.newTxn(txn_type, STORE_NAME, callback, successCb, errorCb);
|
||||
return this.newTxn(txn_type, STORE_NAME_V2, callback, successCb, errorCb);
|
||||
},
|
||||
|
||||
upgradeSchema: function upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) {
|
||||
|
@ -67,57 +69,69 @@ NetworkStatsDB.prototype = {
|
|||
if (DEBUG) {
|
||||
debug("Created object stores and indexes");
|
||||
}
|
||||
|
||||
// There could be a time delay between the point when the network
|
||||
// interface comes up and the point when the database is initialized.
|
||||
// In this short interval some traffic data are generated but are not
|
||||
// registered by the first sample. The initialization of the database
|
||||
// should make up the missing sample.
|
||||
let stats = [];
|
||||
for (let connection in this._connectionTypes) {
|
||||
let connectionType = this._connectionTypes[connection].name;
|
||||
let timestamp = this.normalizeDate(new Date());
|
||||
stats.push({ connectionType: connectionType,
|
||||
timestamp: timestamp,
|
||||
rxBytes: 0,
|
||||
txBytes: 0,
|
||||
rxTotalBytes: 0,
|
||||
txTotalBytes: 0 });
|
||||
}
|
||||
this._saveStats(aTransaction, objectStore, stats);
|
||||
if (DEBUG) {
|
||||
debug("Database initialized");
|
||||
}
|
||||
} else if (currVersion == 1) {
|
||||
// In order to support per-app traffic data storage, the original
|
||||
// objectStore needs to be replaced by a new objectStore with new
|
||||
// key path ("appId") and new index ("appId").
|
||||
// Also, since now networks are identified by their
|
||||
// [networkId, networkType] not just by their connectionType,
|
||||
// to modify the keyPath is mandatory to delete the object store
|
||||
// and create it again. Old data is going to be deleted because the
|
||||
// networkId for each sample can not be set.
|
||||
let newObjectStore;
|
||||
newObjectStore = db.createObjectStore(STORE_NAME_V2, { keyPath: ["appId", "connectionType", "timestamp"] });
|
||||
newObjectStore.createIndex("appId", "appId", { unique: false });
|
||||
newObjectStore.createIndex("connectionType", "connectionType", { unique: false });
|
||||
newObjectStore.createIndex("timestamp", "timestamp", { unique: false });
|
||||
newObjectStore.createIndex("rxBytes", "rxBytes", { unique: false });
|
||||
newObjectStore.createIndex("txBytes", "txBytes", { unique: false });
|
||||
newObjectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false });
|
||||
newObjectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false });
|
||||
if (DEBUG) {
|
||||
debug("Created new object stores and indexes");
|
||||
}
|
||||
|
||||
// Copy the data from the original objectStore to the new objectStore.
|
||||
objectStore = aTransaction.objectStore(STORE_NAME);
|
||||
objectStore.openCursor().onsuccess = function(event) {
|
||||
let cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
// Delete the original object store.
|
||||
db.deleteObjectStore(STORE_NAME);
|
||||
return;
|
||||
}
|
||||
|
||||
objectStore = db.createObjectStore(STORE_NAME, { keyPath: ["appId", "network", "timestamp"] });
|
||||
objectStore.createIndex("appId", "appId", { unique: false });
|
||||
objectStore.createIndex("network", "network", { unique: false });
|
||||
objectStore.createIndex("networkType", "networkType", { unique: false });
|
||||
objectStore.createIndex("timestamp", "timestamp", { unique: false });
|
||||
objectStore.createIndex("rxBytes", "rxBytes", { unique: false });
|
||||
objectStore.createIndex("txBytes", "txBytes", { unique: false });
|
||||
objectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false });
|
||||
objectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false });
|
||||
|
||||
debug("Created object stores and indexes for version 2");
|
||||
let oldStats = cursor.value;
|
||||
let newStats = { appId: 0,
|
||||
connectionType: oldStats.connectionType,
|
||||
timestamp: oldStats.timestamp,
|
||||
rxBytes: oldStats.rxBytes,
|
||||
txBytes: oldStats.txBytes,
|
||||
rxTotalBytes: oldStats.rxTotalBytes,
|
||||
txTotalBytes: oldStats.txTotalBytes };
|
||||
this._saveStats(aTransaction, newObjectStore, newStats);
|
||||
cursor.continue();
|
||||
}.bind(this);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
importData: function importData(aStats) {
|
||||
let stats = { appId: aStats.appId,
|
||||
network: [aStats.networkId, aStats.networkType],
|
||||
timestamp: aStats.timestamp,
|
||||
rxBytes: aStats.rxBytes,
|
||||
txBytes: aStats.txBytes,
|
||||
rxTotalBytes: aStats.rxTotalBytes,
|
||||
txTotalBytes: aStats.txTotalBytes };
|
||||
|
||||
return stats;
|
||||
},
|
||||
|
||||
exportData: function exportData(aStats) {
|
||||
let stats = { appId: aStats.appId,
|
||||
networkId: aStats.network[0],
|
||||
networkType: aStats.network[1],
|
||||
timestamp: aStats.timestamp,
|
||||
rxBytes: aStats.rxBytes,
|
||||
txBytes: aStats.txBytes,
|
||||
rxTotalBytes: aStats.rxTotalBytes,
|
||||
txTotalBytes: aStats.txTotalBytes };
|
||||
|
||||
return stats;
|
||||
},
|
||||
|
||||
normalizeDate: function normalizeDate(aDate) {
|
||||
// Convert to UTC according to timezone and
|
||||
// filter timestamp to get SAMPLE_RATE precission
|
||||
|
@ -126,42 +140,29 @@ NetworkStatsDB.prototype = {
|
|||
return timestamp;
|
||||
},
|
||||
|
||||
saveStats: function saveStats(aStats, aResultCb) {
|
||||
let timestamp = this.normalizeDate(aStats.date);
|
||||
saveStats: function saveStats(stats, aResultCb) {
|
||||
let timestamp = this.normalizeDate(stats.date);
|
||||
|
||||
let stats = { appId: aStats.appId,
|
||||
networkId: aStats.networkId,
|
||||
networkType: aStats.networkType,
|
||||
stats = { appId: stats.appId,
|
||||
connectionType: stats.connectionType,
|
||||
timestamp: timestamp,
|
||||
rxBytes: (aStats.appId == 0) ? 0 : aStats.rxBytes,
|
||||
txBytes: (aStats.appId == 0) ? 0 : aStats.txBytes,
|
||||
rxTotalBytes: (aStats.appId == 0) ? aStats.rxBytes : 0,
|
||||
txTotalBytes: (aStats.appId == 0) ? aStats.txBytes : 0 };
|
||||
rxBytes: (stats.appId == 0) ? 0 : stats.rxBytes,
|
||||
txBytes: (stats.appId == 0) ? 0 : stats.txBytes,
|
||||
rxTotalBytes: (stats.appId == 0) ? stats.rxBytes : 0,
|
||||
txTotalBytes: (stats.appId == 0) ? stats.txBytes : 0 };
|
||||
|
||||
stats = this.importData(stats);
|
||||
|
||||
this.dbNewTxn("readwrite", function(aTxn, aStore) {
|
||||
this.dbNewTxn("readwrite", function(txn, store) {
|
||||
if (DEBUG) {
|
||||
debug("Filtered time: " + new Date(timestamp));
|
||||
debug("New stats: " + JSON.stringify(stats));
|
||||
}
|
||||
|
||||
let request = aStore.index("network").openCursor(stats.network, "prev");
|
||||
let request = store.index("connectionType").openCursor(stats.connectionType, "prev");
|
||||
request.onsuccess = function onsuccess(event) {
|
||||
let cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
// Empty, so save first element.
|
||||
|
||||
// There could be a time delay between the point when the network
|
||||
// interface comes up and the point when the database is initialized.
|
||||
// In this short interval some traffic data are generated but are not
|
||||
// registered by the first sample.
|
||||
if (stats.appId == 0) {
|
||||
stats.rxBytes = stats.rxTotalBytes;
|
||||
stats.txBytes = stats.txTotalBytes;
|
||||
}
|
||||
|
||||
this._saveStats(aTxn, aStore, stats);
|
||||
this._saveStats(txn, store, stats);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -176,10 +177,10 @@ NetworkStatsDB.prototype = {
|
|||
}
|
||||
|
||||
// Remove stats previous to now - VALUE_MAX_LENGTH
|
||||
this._removeOldStats(aTxn, aStore, stats.appId, stats.network, stats.timestamp);
|
||||
this._removeOldStats(txn, store, stats.appId, stats.connectionType, stats.timestamp);
|
||||
|
||||
// Process stats before save
|
||||
this._processSamplesDiff(aTxn, aStore, cursor, stats);
|
||||
this._processSamplesDiff(txn, store, cursor, stats);
|
||||
}.bind(this);
|
||||
}.bind(this), aResultCb);
|
||||
},
|
||||
|
@ -188,21 +189,20 @@ NetworkStatsDB.prototype = {
|
|||
* This function check that stats are saved in the database following the sample rate.
|
||||
* In this way is easier to find elements when stats are requested.
|
||||
*/
|
||||
_processSamplesDiff: function _processSamplesDiff(aTxn, aStore, aLastSampleCursor, aNewSample) {
|
||||
let lastSample = aLastSampleCursor.value;
|
||||
_processSamplesDiff: function _processSamplesDiff(txn, store, lastSampleCursor, newSample) {
|
||||
let lastSample = lastSampleCursor.value;
|
||||
|
||||
// Get difference between last and new sample.
|
||||
let diff = (aNewSample.timestamp - lastSample.timestamp) / SAMPLE_RATE;
|
||||
let diff = (newSample.timestamp - lastSample.timestamp) / SAMPLE_RATE;
|
||||
if (diff % 1) {
|
||||
// diff is decimal, so some error happened because samples are stored as a multiple
|
||||
// of SAMPLE_RATE
|
||||
aTxn.abort();
|
||||
txn.abort();
|
||||
throw new Error("Error processing samples");
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("New: " + aNewSample.timestamp + " - Last: " +
|
||||
lastSample.timestamp + " - diff: " + diff);
|
||||
debug("New: " + newSample.timestamp + " - Last: " + lastSample.timestamp + " - diff: " + diff);
|
||||
}
|
||||
|
||||
// If the incoming data is obtained from netd (|newSample.appId| is 0),
|
||||
|
@ -210,15 +210,15 @@ NetworkStatsDB.prototype = {
|
|||
// |txTotalBytes|/|rxTotalBytes| and the last |txTotalBytes|/|rxTotalBytes|.
|
||||
// Else, the incoming data is per-app data (|newSample.appId| is not 0),
|
||||
// the |txBytes|/|rxBytes| is directly the new |txBytes|/|rxBytes|.
|
||||
if (aNewSample.appId == 0) {
|
||||
let rxDiff = aNewSample.rxTotalBytes - lastSample.rxTotalBytes;
|
||||
let txDiff = aNewSample.txTotalBytes - lastSample.txTotalBytes;
|
||||
if (newSample.appId == 0) {
|
||||
let rxDiff = newSample.rxTotalBytes - lastSample.rxTotalBytes;
|
||||
let txDiff = newSample.txTotalBytes - lastSample.txTotalBytes;
|
||||
if (rxDiff < 0 || txDiff < 0) {
|
||||
rxDiff = aNewSample.rxTotalBytes;
|
||||
txDiff = aNewSample.txTotalBytes;
|
||||
rxDiff = newSample.rxTotalBytes;
|
||||
txDiff = newSample.txTotalBytes;
|
||||
}
|
||||
aNewSample.rxBytes = rxDiff;
|
||||
aNewSample.txBytes = txDiff;
|
||||
newSample.rxBytes = rxDiff;
|
||||
newSample.txBytes = txDiff;
|
||||
}
|
||||
|
||||
if (diff == 1) {
|
||||
|
@ -227,12 +227,11 @@ NetworkStatsDB.prototype = {
|
|||
// If the incoming data is per-data data, new |rxTotalBytes|/|txTotalBytes|
|
||||
// needs to be obtained by adding new |rxBytes|/|txBytes| to last
|
||||
// |rxTotalBytes|/|txTotalBytes|.
|
||||
if (aNewSample.appId != 0) {
|
||||
aNewSample.rxTotalBytes = aNewSample.rxBytes + lastSample.rxTotalBytes;
|
||||
aNewSample.txTotalBytes = aNewSample.txBytes + lastSample.txTotalBytes;
|
||||
if (newSample.appId != 0) {
|
||||
newSample.rxTotalBytes = newSample.rxBytes + lastSample.rxTotalBytes;
|
||||
newSample.txTotalBytes = newSample.txBytes + lastSample.txTotalBytes;
|
||||
}
|
||||
|
||||
this._saveStats(aTxn, aStore, aNewSample);
|
||||
this._saveStats(txn, store, newSample);
|
||||
return;
|
||||
}
|
||||
if (diff > 1) {
|
||||
|
@ -245,20 +244,19 @@ NetworkStatsDB.prototype = {
|
|||
|
||||
let data = [];
|
||||
for (let i = diff - 2; i >= 0; i--) {
|
||||
let time = aNewSample.timestamp - SAMPLE_RATE * (i + 1);
|
||||
let sample = { appId: aNewSample.appId,
|
||||
network: aNewSample.network,
|
||||
let time = newSample.timestamp - SAMPLE_RATE * (i + 1);
|
||||
let sample = {appId: newSample.appId,
|
||||
connectionType: newSample.connectionType,
|
||||
timestamp: time,
|
||||
rxBytes: 0,
|
||||
txBytes: 0,
|
||||
rxTotalBytes: lastSample.rxTotalBytes,
|
||||
txTotalBytes: lastSample.txTotalBytes};
|
||||
|
||||
data.push(sample);
|
||||
}
|
||||
|
||||
data.push(aNewSample);
|
||||
this._saveStats(aTxn, aStore, data);
|
||||
data.push(newSample);
|
||||
this._saveStats(txn, store, data);
|
||||
return;
|
||||
}
|
||||
if (diff == 0 || diff < 0) {
|
||||
|
@ -268,163 +266,91 @@ NetworkStatsDB.prototype = {
|
|||
|
||||
// If diff < 0, clock or timezone changed back. Place data in the last sample.
|
||||
|
||||
lastSample.rxBytes += aNewSample.rxBytes;
|
||||
lastSample.txBytes += aNewSample.txBytes;
|
||||
lastSample.rxBytes += newSample.rxBytes;
|
||||
lastSample.txBytes += newSample.txBytes;
|
||||
|
||||
// If incoming data is obtained from netd, last |rxTotalBytes|/|txTotalBytes|
|
||||
// needs to get updated by replacing the new |rxTotalBytes|/|txTotalBytes|.
|
||||
if (aNewSample.appId == 0) {
|
||||
lastSample.rxTotalBytes = aNewSample.rxTotalBytes;
|
||||
lastSample.txTotalBytes = aNewSample.txTotalBytes;
|
||||
if (newSample.appId == 0) {
|
||||
lastSample.rxTotalBytes = newSample.rxTotalBytes;
|
||||
lastSample.txTotalBytes = newSample.txTotalBytes;
|
||||
} else {
|
||||
// Else, the incoming data is per-app data, old |rxTotalBytes|/
|
||||
// |txTotalBytes| needs to get updated by adding the new
|
||||
// |rxBytes|/|txBytes| to last |rxTotalBytes|/|txTotalBytes|.
|
||||
lastSample.rxTotalBytes += aNewSample.rxBytes;
|
||||
lastSample.txTotalBytes += aNewSample.txBytes;
|
||||
lastSample.rxTotalBytes += newSample.rxBytes;
|
||||
lastSample.txTotalBytes += newSample.txBytes;
|
||||
}
|
||||
if (DEBUG) {
|
||||
debug("Update: " + JSON.stringify(lastSample));
|
||||
}
|
||||
let req = aLastSampleCursor.update(lastSample);
|
||||
let req = lastSampleCursor.update(lastSample);
|
||||
}
|
||||
},
|
||||
|
||||
_saveStats: function _saveStats(aTxn, aStore, aNetworkStats) {
|
||||
_saveStats: function _saveStats(txn, store, networkStats) {
|
||||
if (DEBUG) {
|
||||
debug("_saveStats: " + JSON.stringify(aNetworkStats));
|
||||
debug("_saveStats: " + JSON.stringify(networkStats));
|
||||
}
|
||||
|
||||
if (Array.isArray(aNetworkStats)) {
|
||||
let len = aNetworkStats.length - 1;
|
||||
if (Array.isArray(networkStats)) {
|
||||
let len = networkStats.length - 1;
|
||||
for (let i = 0; i <= len; i++) {
|
||||
aStore.put(aNetworkStats[i]);
|
||||
store.put(networkStats[i]);
|
||||
}
|
||||
} else {
|
||||
aStore.put(aNetworkStats);
|
||||
store.put(networkStats);
|
||||
}
|
||||
},
|
||||
|
||||
_removeOldStats: function _removeOldStats(aTxn, aStore, aAppId, aNetwork, aDate) {
|
||||
_removeOldStats: function _removeOldStats(txn, store, appId, connType, date) {
|
||||
// Callback function to remove old items when new ones are added.
|
||||
let filterDate = aDate - (SAMPLE_RATE * VALUES_MAX_LENGTH - 1);
|
||||
let lowerFilter = [aAppId, aNetwork, 0];
|
||||
let upperFilter = [aAppId, aNetwork, filterDate];
|
||||
let filterDate = date - (SAMPLE_RATE * VALUES_MAX_LENGTH - 1);
|
||||
let lowerFilter = [appId, connType, 0];
|
||||
let upperFilter = [appId, connType, filterDate];
|
||||
let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
|
||||
let lastSample = null;
|
||||
let self = this;
|
||||
|
||||
aStore.openCursor(range).onsuccess = function(event) {
|
||||
store.openCursor(range).onsuccess = function(event) {
|
||||
var cursor = event.target.result;
|
||||
if (cursor) {
|
||||
lastSample = cursor.value;
|
||||
cursor.delete();
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
// If all samples for a network are removed, an empty sample
|
||||
// has to be saved to keep the totalBytes in order to compute
|
||||
// future samples because system counters are not set to 0.
|
||||
// Thus, if there are no samples left, the last sample removed
|
||||
// will be saved again after setting its bytes to 0.
|
||||
let request = aStore.index("network").openCursor(aNetwork);
|
||||
request.onsuccess = function onsuccess(event) {
|
||||
let cursor = event.target.result;
|
||||
if (!cursor && lastSample != null) {
|
||||
let timestamp = new Date();
|
||||
timestamp = self.normalizeDate(timestamp);
|
||||
lastSample.timestamp = timestamp;
|
||||
lastSample.rxBytes = 0;
|
||||
lastSample.txBytes = 0;
|
||||
self._saveStats(aTxn, aStore, lastSample);
|
||||
}
|
||||
};
|
||||
};
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
clearInterfaceStats: function clearInterfaceStats(aNetwork, aResultCb) {
|
||||
let network = [aNetwork.id, aNetwork.type];
|
||||
let self = this;
|
||||
|
||||
// Clear and save an empty sample to keep sync with system counters
|
||||
this.dbNewTxn("readwrite", function(aTxn, aStore) {
|
||||
let sample = null;
|
||||
let request = aStore.index("network").openCursor(network, "prev");
|
||||
request.onsuccess = function onsuccess(event) {
|
||||
let cursor = event.target.result;
|
||||
if (cursor) {
|
||||
if (!sample) {
|
||||
sample = cursor.value;
|
||||
clear: function clear(aResultCb) {
|
||||
this.dbNewTxn("readwrite", function(txn, store) {
|
||||
if (DEBUG) {
|
||||
debug("Going to clear all!");
|
||||
}
|
||||
|
||||
cursor.delete();
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
if (sample) {
|
||||
let timestamp = new Date();
|
||||
timestamp = self.normalizeDate(timestamp);
|
||||
sample.timestamp = timestamp;
|
||||
sample.appId = 0;
|
||||
sample.rxBytes = 0;
|
||||
sample.txBytes = 0;
|
||||
|
||||
self._saveStats(aTxn, aStore, sample);
|
||||
}
|
||||
};
|
||||
store.clear();
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
clearStats: function clearStats(aNetworks, aResultCb) {
|
||||
let index = 0;
|
||||
let stats = [];
|
||||
let self = this;
|
||||
|
||||
let callback = function(aError, aResult) {
|
||||
index++;
|
||||
|
||||
if (!aError && index < aNetworks.length) {
|
||||
self.clearInterfaceStats(aNetworks[index], callback);
|
||||
return;
|
||||
}
|
||||
|
||||
aResultCb(aError, aResult);
|
||||
};
|
||||
|
||||
if (!aNetworks[index]) {
|
||||
aResultCb(null, true);
|
||||
return;
|
||||
}
|
||||
this.clearInterfaceStats(aNetworks[index], callback);
|
||||
},
|
||||
|
||||
find: function find(aResultCb, aNetwork, aStart, aEnd, aAppId, aManifestURL) {
|
||||
find: function find(aResultCb, aOptions) {
|
||||
let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||
let start = this.normalizeDate(aStart);
|
||||
let end = this.normalizeDate(aEnd);
|
||||
let start = this.normalizeDate(aOptions.start);
|
||||
let end = this.normalizeDate(aOptions.end);
|
||||
|
||||
if (DEBUG) {
|
||||
debug("Find samples for appId: " + aAppId + " network " +
|
||||
JSON.stringify(aNetwork) + " from " + start + " until " + end);
|
||||
debug("Find: appId: " + aOptions.appId + " connectionType:" +
|
||||
aOptions.connectionType + " start: " + start + " end: " + end);
|
||||
debug("Start time: " + new Date(start));
|
||||
debug("End time: " + new Date(end));
|
||||
}
|
||||
|
||||
this.dbNewTxn("readonly", function(aTxn, aStore) {
|
||||
let network = [aNetwork.id, aNetwork.type];
|
||||
let lowerFilter = [aAppId, network, start];
|
||||
let upperFilter = [aAppId, network, end];
|
||||
this.dbNewTxn("readonly", function(txn, store) {
|
||||
let lowerFilter = [aOptions.appId, aOptions.connectionType, start];
|
||||
let upperFilter = [aOptions.appId, aOptions.connectionType, end];
|
||||
let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
|
||||
|
||||
let data = [];
|
||||
|
||||
if (!aTxn.result) {
|
||||
aTxn.result = {};
|
||||
if (!txn.result) {
|
||||
txn.result = {};
|
||||
}
|
||||
|
||||
let request = aStore.openCursor(range).onsuccess = function(event) {
|
||||
let request = store.openCursor(range).onsuccess = function(event) {
|
||||
var cursor = event.target.result;
|
||||
if (cursor){
|
||||
data.push({ rxBytes: cursor.value.rxBytes,
|
||||
|
@ -438,11 +364,66 @@ NetworkStatsDB.prototype = {
|
|||
// now - VALUES_MAX_LENGTH, fill with empty samples.
|
||||
this.fillResultSamples(start + offset, end + offset, data);
|
||||
|
||||
aTxn.result.manifestURL = aManifestURL;
|
||||
aTxn.result.network = aNetwork;
|
||||
aTxn.result.start = aStart;
|
||||
aTxn.result.end = aEnd;
|
||||
aTxn.result.data = data;
|
||||
txn.result.manifestURL = aOptions.manifestURL;
|
||||
txn.result.connectionType = aOptions.connectionType;
|
||||
txn.result.start = aOptions.start;
|
||||
txn.result.end = aOptions.end;
|
||||
txn.result.data = data;
|
||||
}.bind(this);
|
||||
}.bind(this), aResultCb);
|
||||
},
|
||||
|
||||
findAll: function findAll(aResultCb, aOptions) {
|
||||
let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||
let start = this.normalizeDate(aOptions.start);
|
||||
let end = this.normalizeDate(aOptions.end);
|
||||
|
||||
if (DEBUG) {
|
||||
debug("FindAll: appId: " + aOptions.appId +
|
||||
" start: " + start + " end: " + end + "\n");
|
||||
}
|
||||
|
||||
let self = this;
|
||||
this.dbNewTxn("readonly", function(txn, store) {
|
||||
let lowerFilter = start;
|
||||
let upperFilter = end;
|
||||
let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
|
||||
|
||||
let data = [];
|
||||
|
||||
if (!txn.result) {
|
||||
txn.result = {};
|
||||
}
|
||||
|
||||
let request = store.index("timestamp").openCursor(range).onsuccess = function(event) {
|
||||
var cursor = event.target.result;
|
||||
if (cursor) {
|
||||
if (cursor.value.appId != aOptions.appId) {
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.length > 0 &&
|
||||
data[data.length - 1].date.getTime() == cursor.value.timestamp + offset) {
|
||||
// Time is the same, so add values.
|
||||
data[data.length - 1].rxBytes += cursor.value.rxBytes;
|
||||
data[data.length - 1].txBytes += cursor.value.txBytes;
|
||||
} else {
|
||||
data.push({ rxBytes: cursor.value.rxBytes,
|
||||
txBytes: cursor.value.txBytes,
|
||||
date: new Date(cursor.value.timestamp + offset) });
|
||||
}
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
this.fillResultSamples(start + offset, end + offset, data);
|
||||
|
||||
txn.result.manifestURL = aOptions.manifestURL;
|
||||
txn.result.connectionType = aOptions.connectionType;
|
||||
txn.result.start = aOptions.start;
|
||||
txn.result.end = aOptions.end;
|
||||
txn.result.data = data;
|
||||
}.bind(this);
|
||||
}.bind(this), aResultCb);
|
||||
},
|
||||
|
@ -480,9 +461,9 @@ NetworkStatsDB.prototype = {
|
|||
},
|
||||
|
||||
logAllRecords: function logAllRecords(aResultCb) {
|
||||
this.dbNewTxn("readonly", function(aTxn, aStore) {
|
||||
aStore.mozGetAll().onsuccess = function onsuccess(event) {
|
||||
aTxn.result = event.target.result;
|
||||
this.dbNewTxn("readonly", function(txn, store) {
|
||||
store.mozGetAll().onsuccess = function onsuccess(event) {
|
||||
txn.result = event.target.result;
|
||||
};
|
||||
}, aResultCb);
|
||||
},
|
||||
|
|
|
@ -55,38 +55,9 @@ NetworkStatsData.prototype = {
|
|||
QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsData])
|
||||
};
|
||||
|
||||
// NetworkStatsInterface
|
||||
const NETWORKSTATSINTERFACE_CONTRACTID = "@mozilla.org/networkstatsinterface;1";
|
||||
const NETWORKSTATSINTERFACE_CID = Components.ID("{f540615b-d803-43ff-8200-2a9d145a5645}");
|
||||
const nsIDOMMozNetworkStatsInterface = Components.interfaces.nsIDOMMozNetworkStatsInterface;
|
||||
|
||||
function NetworkStatsInterface(aNetwork) {
|
||||
if (DEBUG) {
|
||||
debug("NetworkStatsInterface Constructor");
|
||||
}
|
||||
this.type = aNetwork.type;
|
||||
this.id = aNetwork.id;
|
||||
}
|
||||
|
||||
NetworkStatsInterface.prototype = {
|
||||
__exposedProps__: {
|
||||
id: 'r',
|
||||
type: 'r',
|
||||
},
|
||||
|
||||
classID : NETWORKSTATSINTERFACE_CID,
|
||||
classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSINTERFACE_CID,
|
||||
contractID: NETWORKSTATSINTERFACE_CONTRACTID,
|
||||
classDescription: "NetworkStatsInterface",
|
||||
interfaces: [nsIDOMMozNetworkStatsInterface],
|
||||
flags: nsIClassInfo.DOM_OBJECT}),
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsInterface])
|
||||
}
|
||||
|
||||
// NetworkStats
|
||||
const NETWORKSTATS_CONTRACTID = "@mozilla.org/networkstats;1";
|
||||
const NETWORKSTATS_CID = Components.ID("{b6fc4b14-628d-4c99-bf4e-e4ed56916cbe}");
|
||||
const NETWORKSTATS_CID = Components.ID("{6613ea55-b99c-44f9-91bf-d07da10b9b74}");
|
||||
const nsIDOMMozNetworkStats = Components.interfaces.nsIDOMMozNetworkStats;
|
||||
|
||||
function NetworkStats(aWindow, aStats) {
|
||||
|
@ -94,7 +65,7 @@ function NetworkStats(aWindow, aStats) {
|
|||
debug("NetworkStats Constructor");
|
||||
}
|
||||
this.manifestURL = aStats.manifestURL || null;
|
||||
this.network = new NetworkStatsInterface(aStats.network);
|
||||
this.connectionType = aStats.connectionType || null;
|
||||
this.start = aStats.start || null;
|
||||
this.end = aStats.end || null;
|
||||
|
||||
|
@ -107,7 +78,7 @@ function NetworkStats(aWindow, aStats) {
|
|||
NetworkStats.prototype = {
|
||||
__exposedProps__: {
|
||||
manifestURL: 'r',
|
||||
network: 'r',
|
||||
connectionType: 'r',
|
||||
start: 'r',
|
||||
end: 'r',
|
||||
data: 'r',
|
||||
|
@ -121,14 +92,13 @@ NetworkStats.prototype = {
|
|||
flags: nsIClassInfo.DOM_OBJECT}),
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStats,
|
||||
nsIDOMMozNetworkStatsData,
|
||||
nsIDOMMozNetworkStatsInterface])
|
||||
nsIDOMMozNetworkStatsData])
|
||||
}
|
||||
|
||||
// NetworkStatsManager
|
||||
|
||||
const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1";
|
||||
const NETWORKSTATSMANAGER_CID = Components.ID("{5fbdcae6-a2cd-47b3-929f-83ac75bd4881}");
|
||||
const NETWORKSTATSMANAGER_CID = Components.ID("{87529a6c-aef6-11e1-a595-4f034275cfa6}");
|
||||
const nsIDOMMozNetworkStatsManager = Components.interfaces.nsIDOMMozNetworkStatsManager;
|
||||
|
||||
function NetworkStatsManager() {
|
||||
|
@ -146,64 +116,42 @@ NetworkStatsManager.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
getSamples: function getSamples(aNetwork, aStart, aEnd, aManifestURL) {
|
||||
getNetworkStats: function getNetworkStats(aOptions) {
|
||||
this.checkPrivileges();
|
||||
|
||||
if (aStart.constructor.name !== "Date" ||
|
||||
aEnd.constructor.name !== "Date" ||
|
||||
aStart > aEnd) {
|
||||
if (!aOptions.start || !aOptions.end ||
|
||||
aOptions.start > aOptions.end) {
|
||||
throw Components.results.NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:Get",
|
||||
{ network: aNetwork,
|
||||
start: aStart,
|
||||
end: aEnd,
|
||||
manifestURL: aManifestURL,
|
||||
id: this.getRequestId(request) });
|
||||
{data: aOptions, id: this.getRequestId(request)});
|
||||
return request;
|
||||
},
|
||||
|
||||
clearStats: function clearStats(aNetwork) {
|
||||
clearAllData: function clearAllData() {
|
||||
this.checkPrivileges();
|
||||
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:Clear",
|
||||
{ network: aNetwork,
|
||||
id: this.getRequestId(request) });
|
||||
return request;
|
||||
},
|
||||
|
||||
clearAllStats: function clearAllStats() {
|
||||
this.checkPrivileges();
|
||||
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("NetworkStats:ClearAll",
|
||||
{id: this.getRequestId(request)});
|
||||
return request;
|
||||
},
|
||||
|
||||
get availableNetworks() {
|
||||
get connectionTypes() {
|
||||
this.checkPrivileges();
|
||||
|
||||
let result = ObjectWrapper.wrap(cpmm.sendSyncMessage("NetworkStats:Networks")[0], this._window);
|
||||
let networks = this.data = Cu.createArrayIn(this._window);
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
networks.push(new NetworkStatsInterface(result[i]));
|
||||
}
|
||||
|
||||
return networks;
|
||||
return ObjectWrapper.wrap(cpmm.sendSyncMessage("NetworkStats:Types")[0], this._window);
|
||||
},
|
||||
|
||||
get sampleRate() {
|
||||
this.checkPrivileges();
|
||||
return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0];
|
||||
return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0] / 1000;
|
||||
},
|
||||
|
||||
get maxStorageAge() {
|
||||
get maxStorageSamples() {
|
||||
this.checkPrivileges();
|
||||
return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0];
|
||||
return cpmm.sendSyncMessage("NetworkStats:MaxStorageSamples")[0];
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
|
@ -235,7 +183,6 @@ NetworkStatsManager.prototype = {
|
|||
break;
|
||||
|
||||
case "NetworkStats:Clear:Return":
|
||||
case "NetworkStats:ClearAll:Return":
|
||||
if (msg.error) {
|
||||
Services.DOMRequest.fireError(req, msg.error);
|
||||
return;
|
||||
|
@ -275,8 +222,7 @@ NetworkStatsManager.prototype = {
|
|||
}
|
||||
|
||||
this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return",
|
||||
"NetworkStats:Clear:Return",
|
||||
"NetworkStats:ClearAll:Return"]);
|
||||
"NetworkStats:Clear:Return"]);
|
||||
},
|
||||
|
||||
// Called from DOMRequestIpcHelper
|
||||
|
@ -299,6 +245,5 @@ NetworkStatsManager.prototype = {
|
|||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsData,
|
||||
NetworkStatsInterface,
|
||||
NetworkStats,
|
||||
NetworkStatsManager]);
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
component {3b16fe17-5583-483a-b486-b64a3243221c} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkStatsdata;1 {3b16fe17-5583-483a-b486-b64a3243221c}
|
||||
|
||||
component {b6fc4b14-628d-4c99-bf4e-e4ed56916cbe} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkStats;1 {b6fc4b14-628d-4c99-bf4e-e4ed56916cbe}
|
||||
component {6613ea55-b99c-44f9-91bf-d07da10b9b74} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkStats;1 {6613ea55-b99c-44f9-91bf-d07da10b9b74}
|
||||
|
||||
component {f540615b-d803-43ff-8200-2a9d145a5645} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkstatsinterface;1 {f540615b-d803-43ff-8200-2a9d145a5645}
|
||||
|
||||
component {5fbdcae6-a2cd-47b3-929f-83ac75bd4881} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkStatsManager;1 {5fbdcae6-a2cd-47b3-929f-83ac75bd4881}
|
||||
component {87529a6c-aef6-11e1-a595-4f034275cfa6} NetworkStatsManager.js
|
||||
contract @mozilla.org/networkStatsManager;1 {87529a6c-aef6-11e1-a595-4f034275cfa6}
|
||||
category JavaScript-navigator-property mozNetworkStats @mozilla.org/networkStatsManager;1
|
||||
|
|
|
@ -5,11 +5,7 @@
|
|||
"use strict";
|
||||
|
||||
const DEBUG = false;
|
||||
function debug(s) {
|
||||
if (DEBUG) {
|
||||
dump("-*- NetworkStatsService: " + s + "\n");
|
||||
}
|
||||
}
|
||||
function debug(s) { dump("-*- NetworkStatsService: " + s + "\n"); }
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
|
@ -26,6 +22,7 @@ const TOPIC_INTERFACE_REGISTERED = "network-interface-registered";
|
|||
const TOPIC_INTERFACE_UNREGISTERED = "network-interface-unregistered";
|
||||
const NET_TYPE_WIFI = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
|
||||
const NET_TYPE_MOBILE = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE;
|
||||
const NET_TYPE_UNKNOWN = Ci.nsINetworkInterface.NETWORK_TYPE_UNKNOWN;
|
||||
|
||||
// The maximum traffic amount can be saved in the |cachedAppStats|.
|
||||
const MAX_CACHED_TRAFFIC = 500 * 1000 * 1000; // 500 MB
|
||||
|
@ -42,19 +39,11 @@ XPCOMUtils.defineLazyServiceGetter(this, "appsService",
|
|||
"@mozilla.org/AppsService;1",
|
||||
"nsIAppsService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
|
||||
"@mozilla.org/settingsService;1",
|
||||
"nsISettingsService");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gRadioInterface", function () {
|
||||
let ril = Cc["@mozilla.org/ril;1"].getService(Ci["nsIRadioInterfaceLayer"]);
|
||||
// TODO: Bug 923382 - B2G Multi-SIM: support multiple SIM cards for network metering.
|
||||
return ril.getRadioInterface(0);
|
||||
});
|
||||
|
||||
this.NetworkStatsService = {
|
||||
init: function() {
|
||||
if (DEBUG) {
|
||||
debug("Service started");
|
||||
}
|
||||
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
Services.obs.addObserver(this, TOPIC_INTERFACE_REGISTERED, false);
|
||||
|
@ -63,41 +52,24 @@ this.NetworkStatsService = {
|
|||
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
|
||||
// Object to store network interfaces, each network interface is composed
|
||||
// by a network object (network type and network Id) and a interfaceName
|
||||
// that contains the name of the physical interface (wlan0, rmnet0, etc.).
|
||||
// The network type can be 0 for wifi or 1 for mobile. On the other hand,
|
||||
// the network id is '0' for wifi or the iccid for mobile (SIM).
|
||||
// Each networkInterface is placed in the _networks object by the index of
|
||||
// 'networkId + networkType'.
|
||||
//
|
||||
// _networks object allows to map available network interfaces at low level
|
||||
// (wlan0, rmnet0, etc.) to a network. It's not mandatory to have a
|
||||
// networkInterface per network but can't exist a networkInterface not
|
||||
// being mapped to a network.
|
||||
this._connectionTypes = Object.create(null);
|
||||
this._connectionTypes[NET_TYPE_WIFI] = { name: "wifi",
|
||||
network: Object.create(null) };
|
||||
this._connectionTypes[NET_TYPE_MOBILE] = { name: "mobile",
|
||||
network: Object.create(null) };
|
||||
|
||||
this._networks = Object.create(null);
|
||||
|
||||
// There is no way to know a priori if wifi connection is available,
|
||||
// just when the wifi driver is loaded, but it is unloaded when
|
||||
// wifi is switched off. So wifi connection is hardcoded
|
||||
let netId = this.getNetworkId('0', NET_TYPE_WIFI);
|
||||
this._networks[netId] = { network: { id: '0',
|
||||
type: NET_TYPE_WIFI },
|
||||
interfaceName: null };
|
||||
|
||||
this.messages = ["NetworkStats:Get",
|
||||
"NetworkStats:Clear",
|
||||
"NetworkStats:ClearAll",
|
||||
"NetworkStats:Networks",
|
||||
"NetworkStats:Types",
|
||||
"NetworkStats:SampleRate",
|
||||
"NetworkStats:MaxStorageAge"];
|
||||
"NetworkStats:MaxStorageSamples"];
|
||||
|
||||
this.messages.forEach(function(aMsgName) {
|
||||
ppmm.addMessageListener(aMsgName, this);
|
||||
this.messages.forEach(function(msgName) {
|
||||
ppmm.addMessageListener(msgName, this);
|
||||
}, this);
|
||||
|
||||
this._db = new NetworkStatsDB();
|
||||
this._db = new NetworkStatsDB(this._connectionTypes);
|
||||
|
||||
// Stats for all interfaces are updated periodically
|
||||
this.timer.initWithCallback(this, this._db.sampleRate,
|
||||
|
@ -116,56 +88,58 @@ this.NetworkStatsService = {
|
|||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("receiveMessage " + aMessage.name);
|
||||
|
||||
}
|
||||
let mm = aMessage.target;
|
||||
let msg = aMessage.json;
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "NetworkStats:Get":
|
||||
this.getSamples(mm, msg);
|
||||
this.getStats(mm, msg);
|
||||
break;
|
||||
case "NetworkStats:Clear":
|
||||
this.clearInterfaceStats(mm, msg);
|
||||
break;
|
||||
case "NetworkStats:ClearAll":
|
||||
this.clearDB(mm, msg);
|
||||
break;
|
||||
case "NetworkStats:Networks":
|
||||
return this.availableNetworks();
|
||||
case "NetworkStats:Types":
|
||||
// This message is sync.
|
||||
let types = [];
|
||||
for (let i in this._connectionTypes) {
|
||||
types.push(this._connectionTypes[i].name);
|
||||
}
|
||||
return types;
|
||||
case "NetworkStats:SampleRate":
|
||||
// This message is sync.
|
||||
return this._db.sampleRate;
|
||||
case "NetworkStats:MaxStorageAge":
|
||||
case "NetworkStats:MaxStorageSamples":
|
||||
// This message is sync.
|
||||
return this._db.maxStorageSamples * this._db.sampleRate;
|
||||
return this._db.maxStorageSamples;
|
||||
}
|
||||
},
|
||||
|
||||
observe: function observe(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
observe: function observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case TOPIC_INTERFACE_REGISTERED:
|
||||
case TOPIC_INTERFACE_UNREGISTERED:
|
||||
|
||||
// If new interface is registered (notified from NetworkManager),
|
||||
// the stats are updated for the new interface without waiting to
|
||||
// complete the updating period.
|
||||
|
||||
let network = aSubject.QueryInterface(Ci.nsINetworkInterface);
|
||||
// complete the updating period
|
||||
let network = subject.QueryInterface(Ci.nsINetworkInterface);
|
||||
if (DEBUG) {
|
||||
debug("Network " + network.name + " of type " + network.type + " status change");
|
||||
|
||||
let netId = this.convertNetworkInterface(network);
|
||||
if (!netId) {
|
||||
break;
|
||||
}
|
||||
|
||||
this.updateStats(netId);
|
||||
if (this._connectionTypes[network.type]) {
|
||||
this._connectionTypes[network.type].network = network;
|
||||
this.updateStats(network.type);
|
||||
}
|
||||
break;
|
||||
case "xpcom-shutdown":
|
||||
if (DEBUG) {
|
||||
debug("Service shutdown");
|
||||
}
|
||||
|
||||
this.messages.forEach(function(aMsgName) {
|
||||
ppmm.removeMessageListener(aMsgName, this);
|
||||
this.messages.forEach(function(msgName) {
|
||||
ppmm.removeMessageListener(msgName, this);
|
||||
}, this);
|
||||
|
||||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||
|
@ -186,53 +160,10 @@ this.NetworkStatsService = {
|
|||
* nsITimerCallback
|
||||
* Timer triggers the update of all stats
|
||||
*/
|
||||
notify: function(aTimer) {
|
||||
notify: function(timer) {
|
||||
this.updateAllStats();
|
||||
},
|
||||
|
||||
/*
|
||||
* nsINetworkStatsService
|
||||
*/
|
||||
|
||||
convertNetworkInterface: function(aNetwork) {
|
||||
if (aNetwork.type != NET_TYPE_MOBILE &&
|
||||
aNetwork.type != NET_TYPE_WIFI) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let id = '0';
|
||||
if (aNetwork.type == NET_TYPE_MOBILE) {
|
||||
// Bug 904542 will provide the serviceId to map the iccId with the
|
||||
// nsINetworkInterface of the NetworkManager. Now, lets assume that
|
||||
// network is mapped with the current iccId of the single SIM.
|
||||
id = gRadioInterface.rilContext.iccInfo.iccid;
|
||||
}
|
||||
|
||||
let netId = this.getNetworkId(id, aNetwork.type);
|
||||
|
||||
if (!this._networks[netId]) {
|
||||
this._networks[netId] = Object.create(null);
|
||||
this._networks[netId].network = { id: id,
|
||||
type: aNetwork.type };
|
||||
}
|
||||
|
||||
this._networks[netId].interfaceName = aNetwork.name;
|
||||
return netId;
|
||||
},
|
||||
|
||||
getNetworkId: function getNetworkId(aIccId, aNetworkType) {
|
||||
return aIccId + '' + aNetworkType;
|
||||
},
|
||||
|
||||
availableNetworks: function availableNetworks() {
|
||||
let result = [];
|
||||
for (let netId in this._networks) {
|
||||
result.push(this._networks[netId].network);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/*
|
||||
* Function called from manager to get stats from database.
|
||||
* In order to return updated stats, first is performed a call to
|
||||
|
@ -241,71 +172,69 @@ this.NetworkStatsService = {
|
|||
* Then, depending on the request (stats per appId or total stats)
|
||||
* it retrieve them from database and return to the manager.
|
||||
*/
|
||||
getSamples: function getSamples(mm, msg) {
|
||||
let self = this;
|
||||
let network = msg.network;
|
||||
let netId = this.getNetworkId(network.id, network.type);
|
||||
getStats: function getStats(mm, msg) {
|
||||
this.updateAllStats(function onStatsUpdated(aResult, aMessage) {
|
||||
|
||||
if (!this._networks[netId]) {
|
||||
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||
{ id: msg.id, error: "Invalid connectionType", result: null });
|
||||
return;
|
||||
}
|
||||
let data = msg.data;
|
||||
|
||||
let appId = 0;
|
||||
let manifestURL = msg.manifestURL;
|
||||
let options = { appId: 0,
|
||||
connectionType: data.connectionType,
|
||||
start: data.start,
|
||||
end: data.end };
|
||||
|
||||
let manifestURL = data.manifestURL;
|
||||
if (manifestURL) {
|
||||
appId = appsService.getAppLocalIdByManifestURL(manifestURL);
|
||||
let appId = appsService.getAppLocalIdByManifestURL(manifestURL);
|
||||
if (DEBUG) {
|
||||
debug("get appId: " + appId + " from manifestURL: " + manifestURL);
|
||||
}
|
||||
|
||||
if (!appId) {
|
||||
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||
{ id: msg.id, error: "Invalid manifestURL", result: null });
|
||||
return;
|
||||
}
|
||||
|
||||
options.appId = appId;
|
||||
options.manifestURL = manifestURL;
|
||||
}
|
||||
|
||||
let start = new Date(msg.start);
|
||||
let end = new Date(msg.end);
|
||||
if (DEBUG) {
|
||||
debug("getStats for options: " + JSON.stringify(options));
|
||||
}
|
||||
|
||||
this.updateStats(netId, function onStatsUpdated(aResult, aMessage) {
|
||||
debug("getstats for network " + network.id + " of type " + network.type);
|
||||
debug("appId: " + appId + " from manifestURL: " + manifestURL);
|
||||
|
||||
self._db.find(function onStatsFound(aError, aResult) {
|
||||
if (!options.connectionType || options.connectionType.length == 0) {
|
||||
this._db.findAll(function onStatsFound(error, result) {
|
||||
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||
{ id: msg.id, error: aError, result: aResult });
|
||||
}, network, start, end, appId, manifestURL);
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
clearInterfaceStats: function clearInterfaceStats(mm, msg) {
|
||||
let network = msg.network;
|
||||
let netId = this.getNetworkId(network.id, network.type);
|
||||
|
||||
debug("clear stats for network " + network.id + " of type " + network.type);
|
||||
|
||||
if (!this._networks[netId]) {
|
||||
mm.sendAsyncMessage("NetworkStats:Clear:Return",
|
||||
{ id: msg.id, error: "Invalid networkType", result: null });
|
||||
{ id: msg.id, error: error, result: result });
|
||||
}, options);
|
||||
return;
|
||||
}
|
||||
|
||||
this._db.clearInterfaceStats(network, function onDBCleared(aError, aResult) {
|
||||
mm.sendAsyncMessage("NetworkStats:Clear:Return",
|
||||
{ id: msg.id, error: aError, result: aResult });
|
||||
});
|
||||
for (let i in this._connectionTypes) {
|
||||
if (this._connectionTypes[i].name == options.connectionType) {
|
||||
this._db.find(function onStatsFound(error, result) {
|
||||
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||
{ id: msg.id, error: error, result: result });
|
||||
}, options);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||
{ id: msg.id, error: "Invalid connectionType", result: null });
|
||||
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
clearDB: function clearDB(mm, msg) {
|
||||
let networks = this.availableNetworks();
|
||||
this._db.clearStats(networks, function onDBCleared(aError, aResult) {
|
||||
mm.sendAsyncMessage("NetworkStats:ClearAll:Return",
|
||||
{ id: msg.id, error: aError, result: aResult });
|
||||
this._db.clear(function onDBCleared(error, result) {
|
||||
mm.sendAsyncMessage("NetworkStats:Clear:Return",
|
||||
{ id: msg.id, error: error, result: result });
|
||||
});
|
||||
},
|
||||
|
||||
updateAllStats: function updateAllStats(aCallback) {
|
||||
updateAllStats: function updateAllStats(callback) {
|
||||
// Update |cachedAppStats|.
|
||||
this.updateCachedAppStats();
|
||||
|
||||
|
@ -318,19 +247,18 @@ this.NetworkStatsService = {
|
|||
// the connection type is already in the queue it is not appended again,
|
||||
// else it is pushed in 'elements' array, which later will be pushed to
|
||||
// the queue array.
|
||||
for (let netId in this._networks) {
|
||||
lastElement = { netId: netId,
|
||||
queueIndex: this.updateQueueIndex(netId)};
|
||||
|
||||
for (let i in this._connectionTypes) {
|
||||
lastElement = { type: i,
|
||||
queueIndex: this.updateQueueIndex(i)};
|
||||
if (lastElement.queueIndex == -1) {
|
||||
elements.push({netId: lastElement.netId, callbacks: []});
|
||||
elements.push({type: lastElement.type, callbacks: []});
|
||||
}
|
||||
}
|
||||
|
||||
if (elements.length > 0) {
|
||||
// If length of elements is greater than 0, callback is set to
|
||||
// the last element.
|
||||
elements[elements.length - 1].callbacks.push(aCallback);
|
||||
elements[elements.length - 1].callbacks.push(callback);
|
||||
this.updateQueue = this.updateQueue.concat(elements);
|
||||
} else {
|
||||
// Else, it means that all connection types are already in the queue to
|
||||
|
@ -338,14 +266,16 @@ this.NetworkStatsService = {
|
|||
// the element in the main queue with the index of the last 'lastElement'.
|
||||
// But before is checked that element is still in the queue because it can
|
||||
// be processed while generating 'elements' array.
|
||||
let element = this.updateQueue[lastElement.queueIndex];
|
||||
if (aCallback &&
|
||||
(!element || element.netId != lastElement.netId)) {
|
||||
aCallback();
|
||||
|
||||
if (!this.updateQueue[lastElement.queueIndex] ||
|
||||
this.updateQueue[lastElement.queueIndex].type != lastElement.queueIndex) {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateQueue[lastElement.queueIndex].callbacks.push(aCallback);
|
||||
this.updateQueue[lastElement.queueIndex].callbacks.push(callback);
|
||||
}
|
||||
|
||||
// Call the function that process the elements of the queue.
|
||||
|
@ -356,14 +286,14 @@ this.NetworkStatsService = {
|
|||
}
|
||||
},
|
||||
|
||||
updateStats: function updateStats(aNetId, aCallback) {
|
||||
// Check if the connection is in the main queue, push a new element
|
||||
updateStats: function updateStats(connectionType, callback) {
|
||||
// Check if the connection type is in the main queue, push a new element
|
||||
// if it is not being processed or add a callback if it is.
|
||||
let index = this.updateQueueIndex(aNetId);
|
||||
let index = this.updateQueueIndex(connectionType);
|
||||
if (index == -1) {
|
||||
this.updateQueue.push({netId: aNetId, callbacks: [aCallback]});
|
||||
this.updateQueue.push({type: connectionType, callbacks: [callback]});
|
||||
} else {
|
||||
this.updateQueue[index].callbacks.push(aCallback);
|
||||
this.updateQueue[index].callbacks.push(callback);
|
||||
}
|
||||
|
||||
// Call the function that process the elements of the queue.
|
||||
|
@ -371,11 +301,16 @@ this.NetworkStatsService = {
|
|||
},
|
||||
|
||||
/*
|
||||
* Find if a connection is in the main queue array and return its
|
||||
* Find if a connection type is in the main queue array and return its
|
||||
* index, if it is not in the array return -1.
|
||||
*/
|
||||
updateQueueIndex: function updateQueueIndex(aNetId) {
|
||||
return this.updateQueue.map(function(e) { return e.netId; }).indexOf(aNetId);
|
||||
updateQueueIndex: function updateQueueIndex(type) {
|
||||
for (let i in this.updateQueue) {
|
||||
if (this.updateQueue[i].type == type) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -412,64 +347,64 @@ this.NetworkStatsService = {
|
|||
}
|
||||
|
||||
// Call the update function for the next element.
|
||||
this.update(this.updateQueue[0].netId, this.processQueue.bind(this));
|
||||
this.update(this.updateQueue[0].type, this.processQueue.bind(this));
|
||||
},
|
||||
|
||||
update: function update(aNetId, aCallback) {
|
||||
update: function update(connectionType, callback) {
|
||||
// Check if connection type is valid.
|
||||
if (!this._networks[aNetId]) {
|
||||
if (aCallback) {
|
||||
aCallback(false, "Invalid network " + aNetId);
|
||||
if (!this._connectionTypes[connectionType]) {
|
||||
if (callback) {
|
||||
callback(false, "Invalid network type " + connectionType);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let interfaceName = this._networks[aNetId].interfaceName;
|
||||
debug("Update stats for " + interfaceName);
|
||||
if (DEBUG) {
|
||||
debug("Update stats for " + this._connectionTypes[connectionType].name);
|
||||
}
|
||||
|
||||
// Request stats to NetworkManager, which will get stats from netd, passing
|
||||
// 'networkStatsAvailable' as a callback.
|
||||
if (interfaceName) {
|
||||
networkManager.getNetworkInterfaceStats(interfaceName,
|
||||
this.networkStatsAvailable.bind(this, aCallback, aNetId));
|
||||
let networkName = this._connectionTypes[connectionType].network.name;
|
||||
if (networkName) {
|
||||
networkManager.getNetworkInterfaceStats(networkName,
|
||||
this.networkStatsAvailable.bind(this, callback, connectionType));
|
||||
return;
|
||||
}
|
||||
|
||||
if (aCallback) {
|
||||
aCallback(true, "ok");
|
||||
if (callback) {
|
||||
callback(true, "ok");
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Callback of request stats. Store stats in database.
|
||||
*/
|
||||
networkStatsAvailable: function networkStatsAvailable(aCallback, aNetId,
|
||||
aResult, aRxBytes,
|
||||
aTxBytes, aDate) {
|
||||
if (!aResult) {
|
||||
if (aCallback) {
|
||||
aCallback(false, "Netd IPC error");
|
||||
networkStatsAvailable: function networkStatsAvailable(callback, connType, result, rxBytes, txBytes, date) {
|
||||
if (!result) {
|
||||
if (callback) {
|
||||
callback(false, "Netd IPC error");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let stats = { appId: 0,
|
||||
networkId: this._networks[aNetId].network.id,
|
||||
networkType: this._networks[aNetId].network.type,
|
||||
date: aDate,
|
||||
rxBytes: aTxBytes,
|
||||
txBytes: aRxBytes };
|
||||
connectionType: this._connectionTypes[connType].name,
|
||||
date: date,
|
||||
rxBytes: rxBytes,
|
||||
txBytes: txBytes };
|
||||
|
||||
debug("Update stats for: " + JSON.stringify(stats));
|
||||
|
||||
this._db.saveStats(stats, function onSavedStats(aError, aResult) {
|
||||
if (aCallback) {
|
||||
if (aError) {
|
||||
aCallback(false, aError);
|
||||
if (DEBUG) {
|
||||
debug("Update stats for " + stats.connectionType + ": rx=" + stats.rxBytes +
|
||||
" tx=" + stats.txBytes + " timestamp=" + stats.date);
|
||||
}
|
||||
this._db.saveStats(stats, function onSavedStats(error, result) {
|
||||
if (callback) {
|
||||
if (error) {
|
||||
callback(false, error);
|
||||
return;
|
||||
}
|
||||
|
||||
aCallback(true, "OK");
|
||||
callback(true, "OK");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -477,34 +412,26 @@ this.NetworkStatsService = {
|
|||
/*
|
||||
* Function responsible for receiving per-app stats.
|
||||
*/
|
||||
saveAppStats: function saveAppStats(aAppId, aNetwork, aTimeStamp, aRxBytes, aTxBytes, aCallback) {
|
||||
let netId = this.convertNetworkInterface(aNetwork);
|
||||
if (!netId) {
|
||||
if (aCallback) {
|
||||
aCallback.notify(false, "Invalid network type");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
debug("saveAppStats: " + aAppId + " " + netId + " " +
|
||||
saveAppStats: function saveAppStats(aAppId, aConnectionType, aTimeStamp, aRxBytes, aTxBytes, aCallback) {
|
||||
if (DEBUG) {
|
||||
debug("saveAppStats: " + aAppId + " " + aConnectionType + " " +
|
||||
aTimeStamp + " " + aRxBytes + " " + aTxBytes);
|
||||
}
|
||||
|
||||
// Check if |aAppId| and |aConnectionType| are valid.
|
||||
if (!aAppId || !this._networks[netId]) {
|
||||
debug("Invalid appId or network interface");
|
||||
if (!aAppId || aConnectionType == NET_TYPE_UNKNOWN) {
|
||||
return;
|
||||
}
|
||||
|
||||
let stats = { appId: aAppId,
|
||||
networkId: this._networks[netId].network.id,
|
||||
networkType: this._networks[netId].network.type,
|
||||
connectionType: this._connectionTypes[aConnectionType].name,
|
||||
date: new Date(aTimeStamp),
|
||||
rxBytes: aRxBytes,
|
||||
txBytes: aTxBytes };
|
||||
|
||||
// Generate an unique key from |appId| and |connectionType|,
|
||||
// which is used to retrieve data in |cachedAppStats|.
|
||||
let key = stats.appId + "" + netId;
|
||||
let key = stats.appId + stats.connectionType;
|
||||
|
||||
// |cachedAppStats| only keeps the data with the same date.
|
||||
// If the incoming date is different from |cachedAppStatsDate|,
|
||||
|
@ -551,15 +478,19 @@ this.NetworkStatsService = {
|
|||
appStats.txBytes > MAX_CACHED_TRAFFIC) {
|
||||
this._db.saveStats(appStats,
|
||||
function (error, result) {
|
||||
if (DEBUG) {
|
||||
debug("Application stats inserted in indexedDB");
|
||||
}
|
||||
}
|
||||
);
|
||||
delete this.cachedAppStats[key];
|
||||
}
|
||||
},
|
||||
|
||||
updateCachedAppStats: function updateCachedAppStats(aCallback) {
|
||||
updateCachedAppStats: function updateCachedAppStats(callback) {
|
||||
if (DEBUG) {
|
||||
debug("updateCachedAppStats: " + this.cachedAppStatsDate);
|
||||
}
|
||||
|
||||
let stats = Object.keys(this.cachedAppStats);
|
||||
if (stats.length == 0) {
|
||||
|
@ -578,16 +509,16 @@ this.NetworkStatsService = {
|
|||
if (index == stats.length - 1) {
|
||||
this.cachedAppStats = Object.create(null);
|
||||
|
||||
if (!aCallback) {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
aCallback(false, error);
|
||||
callback(false, error);
|
||||
return;
|
||||
}
|
||||
|
||||
aCallback(true, "ok");
|
||||
callback(true, "ok");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -603,17 +534,17 @@ this.NetworkStatsService = {
|
|||
},
|
||||
|
||||
logAllRecords: function logAllRecords() {
|
||||
this._db.logAllRecords(function onResult(aError, aResult) {
|
||||
if (aError) {
|
||||
debug("Error: " + aError);
|
||||
this._db.logAllRecords(function onResult(error, result) {
|
||||
if (error) {
|
||||
debug("Error: " + error);
|
||||
return;
|
||||
}
|
||||
|
||||
debug("===== LOG =====");
|
||||
debug("There are " + aResult.length + " items");
|
||||
debug(JSON.stringify(aResult));
|
||||
debug("There are " + result.length + " items");
|
||||
debug(JSON.stringify(result));
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
NetworkStatsService.init();
|
||||
|
|
|
@ -29,14 +29,14 @@ NetworkStatsServiceProxy.prototype = {
|
|||
* Function called in the protocol layer (HTTP, FTP, WebSocket ...etc)
|
||||
* to pass the per-app stats to NetworkStatsService.
|
||||
*/
|
||||
saveAppStats: function saveAppStats(aAppId, aNetwork, aTimeStamp,
|
||||
saveAppStats: function saveAppStats(aAppId, aConnectionType, aTimeStamp,
|
||||
aRxBytes, aTxBytes, aCallback) {
|
||||
if (DEBUG) {
|
||||
debug("saveAppStats: " + aAppId + " connectionType " + aNetwork.type +
|
||||
" " + aTimeStamp + " " + aRxBytes + " " + aTxBytes);
|
||||
debug("saveAppStats: " + aAppId + " " + aConnectionType + " " +
|
||||
aTimeStamp + " " + aRxBytes + " " + aTxBytes);
|
||||
}
|
||||
|
||||
NetworkStatsService.saveAppStats(aAppId, aNetwork, aTimeStamp,
|
||||
NetworkStatsService.saveAppStats(aAppId, aConnectionType, aTimeStamp,
|
||||
aRxBytes, aTxBytes, aCallback);
|
||||
},
|
||||
|
||||
|
|
|
@ -167,7 +167,7 @@ TCPSocket.prototype = {
|
|||
_txBytes: 0,
|
||||
_rxBytes: 0,
|
||||
_appId: Ci.nsIScriptSecurityManager.NO_APP_ID,
|
||||
_activeNetwork: null,
|
||||
_connectionType: Ci.nsINetworkInterface.NETWORK_TYPE_UNKNOWN,
|
||||
#endif
|
||||
|
||||
// Public accessors.
|
||||
|
@ -347,7 +347,7 @@ TCPSocket.prototype = {
|
|||
LOG("Error: Ci.nsINetworkStatsServiceProxy service is not available.");
|
||||
return;
|
||||
}
|
||||
nssProxy.saveAppStats(this._appId, this._activeNetwork, Date.now(),
|
||||
nssProxy.saveAppStats(this._appId, this._connectionType, Date.now(),
|
||||
this._rxBytes, this._txBytes);
|
||||
|
||||
// Reset the counters once the statistics is saved to NetworkStatsServiceProxy.
|
||||
|
@ -530,12 +530,12 @@ TCPSocket.prototype = {
|
|||
that._initStream(that._binaryType);
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Set _activeNetwork, which is only required for network statistics.
|
||||
// Set _connectionType, which is only required for network statistics.
|
||||
// Note that nsINetworkManager, as well as nsINetworkStatsServiceProxy, is
|
||||
// Gonk-specific.
|
||||
let networkManager = Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
|
||||
if (networkManager) {
|
||||
that._activeNetwork = networkManager.active;
|
||||
if (networkManager && networkManager.active) {
|
||||
that._connectionType = networkManager.active.type;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -12,27 +12,38 @@
|
|||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
// Test for NetworkStats
|
||||
function checkInterface(aInterface) {
|
||||
ok(!(aInterface in window), aInterface + " should be prefixed");
|
||||
ok(("Moz" + aInterface) in window, aInterface + " should be prefixed");
|
||||
}
|
||||
|
||||
function test() {
|
||||
// Test interfaces
|
||||
checkInterface("NetworkStatsManager");
|
||||
checkInterface("NetworkStats");
|
||||
checkInterface("NetworkStatsData");
|
||||
|
||||
ok('mozNetworkStats' in navigator, "navigator.mozMozNetworkStats should exist");
|
||||
ok(navigator.mozNetworkStats, "navigator.mozNetworkStats returns an object");
|
||||
|
||||
netStats = navigator.mozNetworkStats;
|
||||
|
||||
// Test IDL attributes
|
||||
ok('availableNetworks' in netStats,
|
||||
"availableNetworks should be a NetworkStats attribute");
|
||||
ok(Array.isArray(netStats.availableNetworks) && netStats.availableNetworks.length > 0,
|
||||
"availableNetworks is an array not empty.");
|
||||
ok('connectionTypes' in netStats,
|
||||
"connectionTypes should be a NetworkStats attribute");
|
||||
ok(Array.isArray(netStats.connectionTypes) && netStats.connectionTypes.length > 0,
|
||||
"connectionTypes is an array not empty.");
|
||||
|
||||
ok('sampleRate' in netStats,
|
||||
"sampleRate should be a NetworkStats attribute");
|
||||
ok(netStats.sampleRate > 0,
|
||||
"sampleRate is greater than 0.");
|
||||
|
||||
ok('maxStorageAge' in netStats,
|
||||
"maxStorageAge should be a NetworkStats attribute");
|
||||
ok(netStats.maxStorageAge > 0,
|
||||
"maxStorageAge is greater than 0.");
|
||||
ok('maxStorageSamples' in netStats,
|
||||
"maxStorageSamples should be a NetworkStats attribute");
|
||||
ok(netStats.maxStorageSamples > 0,
|
||||
"maxStorageSamples is greater than 0.");
|
||||
|
||||
// Test IDL methods
|
||||
next();
|
||||
|
@ -60,114 +71,84 @@ function checkDataDates(data, start, end, sampleRate) {
|
|||
ok(success, "data result has correct dates");
|
||||
}
|
||||
|
||||
function compareNetworks(networkA, networkB) {
|
||||
return (networkA.id == networkB.id &&
|
||||
networkA.type == networkB.type);
|
||||
}
|
||||
|
||||
var req;
|
||||
var index = -1;
|
||||
var netStats = null;
|
||||
|
||||
var steps = [
|
||||
function () {
|
||||
// Test clearAllStats
|
||||
req = netStats.clearAllStats();
|
||||
// Test clearAlldata
|
||||
req = netStats.clearAllData();
|
||||
req.onsuccess = function () {
|
||||
ok(true, "clearAllStats deleted the database");
|
||||
ok(true, "clearAllData deleted the database");
|
||||
next();
|
||||
};
|
||||
req.onerror = function () {
|
||||
ok(false, "clearAllStats deleted the database");
|
||||
ok(false, "clearAllData deleted the database");
|
||||
}
|
||||
},
|
||||
function () {
|
||||
// Check if getSamples launch exception when start is greather than end
|
||||
// Check if getNetworkStats launch exception when start is greather than end
|
||||
|
||||
// Prepare get params
|
||||
var network = netStats.availableNetworks[0];
|
||||
var type = netStats.connectionTypes[0];
|
||||
// Get dates
|
||||
var endDate = new Date();
|
||||
var startDate = new Date(endDate.getTime() + 1000);
|
||||
|
||||
try {
|
||||
netStats.getSamples(network, startDate, endDate);
|
||||
netStats.getNetworkStats({start: startDate, end: endDate});
|
||||
} catch(ex) {
|
||||
ok(true, "getSamples launch exception when start is greater than end");
|
||||
ok(true, "getNetworkStats launch exception when start is greater than end");
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
ok(false, "getSamples launch exception when start is greater than end");
|
||||
ok(false, "getNetworkStats launch exceptionwhen start is greater than end");
|
||||
next();
|
||||
return;
|
||||
},
|
||||
function () {
|
||||
// Test if call getSamples with network of type different than
|
||||
// nsIDOMMozNetworkStatsInterface launch an exception
|
||||
// Test if call getNetworkStats with undefined start param launch an exception
|
||||
|
||||
// Prepare get params
|
||||
var network = "wifi";
|
||||
var endDate = new Date();
|
||||
var startDate = new Date(endDate.getTime() - 1000);
|
||||
|
||||
var type = netStats.connectionTypes[0];
|
||||
setTimeout(function() {
|
||||
try {
|
||||
netStats.getSamples(network, new Date(), new Date());
|
||||
netStats.getNetworkStats({end: new Date()});
|
||||
} catch(ex) {
|
||||
ok(true, "getSamples launch exception if network is not " +
|
||||
"a nsIDOMMozNetworkStatsInterface");
|
||||
ok(true, "getNetworkStats launch exception when start param does not exist");
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
ok(false, "getSamples launch exception if network is not " +
|
||||
"a nsIDOMMozNetworkStatsInterface");
|
||||
ok(false, "getNetworkStats launch exception when start param does not exist");
|
||||
}, 1000);
|
||||
},
|
||||
function () {
|
||||
// Test if call getSamples with start parameter type different than Date launch an exception
|
||||
// Test if call getNetworkStats with undefined end param launch an exception
|
||||
|
||||
// Prepare get params
|
||||
var network = netStats.availableNetworks[0];
|
||||
var endDate = new Date();
|
||||
var startDate = new Date(endDate.getTime() - 1000);
|
||||
startDate = startDate.toString();
|
||||
|
||||
var type = netStats.connectionTypes[0];
|
||||
setTimeout(function() {
|
||||
try {
|
||||
netStats.getSamples(network, startDate, endDate);
|
||||
netStats.getNetworkStats({start: new Date()});
|
||||
} catch(ex) {
|
||||
ok(true, "getSamples launch exception when start param is not a Date");
|
||||
ok(true, "getNetworkStats launch exception when end param does not exist");
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
ok(false, "getSamples launch exception when start param is not a Date");
|
||||
ok(false, "getNetworkStats launch exception when end param does not exist");
|
||||
}, 1000);
|
||||
},
|
||||
function () {
|
||||
// Test if call getSamples with end parameter type different than Date launch an exception
|
||||
|
||||
ok(true, "Get system stats for a connectionType and dates adapted to samplerate");
|
||||
// Prepare get params
|
||||
var network = netStats.availableNetworks[0];
|
||||
var endDate = new Date();
|
||||
var startDate = new Date(endDate.getTime() - 1000);
|
||||
endDate = startDate.toString();
|
||||
|
||||
try {
|
||||
netStats.getSamples(network, startDate, endDate);
|
||||
} catch(ex) {
|
||||
ok(true, "getSamples launch exception when end param is not a Date");
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
ok(false, "getSamples launch exception when end param is not a Date");
|
||||
},
|
||||
function () {
|
||||
ok(true, "Get stats for a network and dates adapted to samplerate");
|
||||
// Prepare get params
|
||||
var network = netStats.availableNetworks[0];
|
||||
var type = netStats.connectionTypes[0];
|
||||
var diff = 2;
|
||||
// Get samplerate in millis
|
||||
var sampleRate = netStats.sampleRate;
|
||||
var sampleRate = netStats.sampleRate * 1000;
|
||||
// Get date with samplerate's precision
|
||||
var offset = new Date().getTimezoneOffset() * 60 * 1000;
|
||||
var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate)
|
||||
|
@ -178,11 +159,11 @@ var steps = [
|
|||
var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
|
||||
|
||||
// Launch request
|
||||
req = netStats.getSamples(network, startDate, endDate);
|
||||
req = netStats.getNetworkStats({start: startDate, end: endDate, connectionType: type});
|
||||
req.onsuccess = function () {
|
||||
ok(true, "Get system stats request ok");
|
||||
ok(req.result.manifestURL == null, "manifestURL should be null");
|
||||
ok(compareNetworks(req.result.network, network), "networks should be equals");
|
||||
ok(req.result.connectionType == type, "connectionTypes should be equals");
|
||||
ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
|
||||
ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
|
||||
var data = req.result.data;
|
||||
|
@ -192,16 +173,123 @@ var steps = [
|
|||
next();
|
||||
};
|
||||
req.onerror = function () {
|
||||
ok(false, "Get stats failure!");
|
||||
ok(false, "Get system stats for a connectionType failure!");
|
||||
}
|
||||
},
|
||||
function () {
|
||||
ok(true, "Get system stats for a network and dates not adapted to samplerate");
|
||||
ok(true, "Get system stats for all connectionTypes and dates adapted to samplerate");
|
||||
// Prepare get params
|
||||
var network = netStats.availableNetworks[0];
|
||||
var diff = 2;
|
||||
// Get samplerate in millis
|
||||
var sampleRate = netStats.sampleRate;
|
||||
var sampleRate = netStats.sampleRate * 1000;
|
||||
// Get date with samplerate's precision
|
||||
var offset = new Date().getTimezoneOffset() * 60 * 1000;
|
||||
var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate)
|
||||
* sampleRate + offset);
|
||||
var startDate = new Date(endDate.getTime() - (sampleRate * diff));
|
||||
// Calculate the number of samples that should be returned based on the
|
||||
// the samplerate and including final and initial samples.
|
||||
var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
|
||||
|
||||
// Launch request
|
||||
req = netStats.getNetworkStats({start: startDate, end: endDate});
|
||||
req.onsuccess = function () {
|
||||
ok(true, "Get stats request ok");
|
||||
ok(req.result.manifestURL == null, "manifestURL should be null");
|
||||
ok(req.result.connectionType == null, "connectionTypes should be null");
|
||||
ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
|
||||
ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
|
||||
var data = req.result.data;
|
||||
ok(Array.isArray(data) && data.length == samples,
|
||||
"data is an array of length " + samples);
|
||||
checkDataDates(data, startDate, endDate, sampleRate);
|
||||
next();
|
||||
};
|
||||
req.onerror = function () {
|
||||
ok(false, "Get system stats for all connectionTypes failure!");
|
||||
}
|
||||
},
|
||||
function () {
|
||||
ok(true, "Get app stats for a connectionType and dates adapted to samplerate");
|
||||
// Prepare get params
|
||||
var url = 'app://browser.gaiamobile.org/manifest.webapp';
|
||||
var type = netStats.connectionTypes[0];
|
||||
var diff = 2;
|
||||
// Get samplerate in millis
|
||||
var sampleRate = netStats.sampleRate * 1000;
|
||||
// Get date with samplerate's precision
|
||||
var offset = new Date().getTimezoneOffset() * 60 * 1000;
|
||||
var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate)
|
||||
* sampleRate + offset);
|
||||
var startDate = new Date(endDate.getTime() - (sampleRate * diff));
|
||||
// Calculate the number of samples that should be returned based on the
|
||||
// the samplerate and including final and initial samples.
|
||||
var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
|
||||
|
||||
// Launch request
|
||||
req = netStats.getNetworkStats({start: startDate,
|
||||
end: endDate,
|
||||
connectionType: type,
|
||||
manifestURL: url});
|
||||
req.onsuccess = function () {
|
||||
ok(true, "Get app stats request ok");
|
||||
ok(req.result.manifestURL == url, "manifestURL should be equals");
|
||||
ok(req.result.connectionType == type, "connectionTypes should be equals");
|
||||
ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
|
||||
ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
|
||||
var data = req.result.data;
|
||||
ok(Array.isArray(data) && data.length == samples,
|
||||
"data is an array of length " + samples);
|
||||
checkDataDates(data, startDate, endDate, sampleRate);
|
||||
next();
|
||||
};
|
||||
req.onerror = function () {
|
||||
ok(false, "Get app stats for a connectionType failure!");
|
||||
}
|
||||
},
|
||||
function () {
|
||||
ok(true, "Get app stats for all connectionTypes and dates adapted to samplerate");
|
||||
// Prepare get params
|
||||
var url = 'app://browser.gaiamobile.org/manifest.webapp';
|
||||
var diff = 2;
|
||||
// Get samplerate in millis
|
||||
var sampleRate = netStats.sampleRate * 1000;
|
||||
// Get date with samplerate's precision
|
||||
var offset = new Date().getTimezoneOffset() * 60 * 1000;
|
||||
var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate)
|
||||
* sampleRate + offset);
|
||||
var startDate = new Date(endDate.getTime() - (sampleRate * diff));
|
||||
// Calculate the number of samples that should be returned based on the
|
||||
// the samplerate and including final and initial samples.
|
||||
var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
|
||||
|
||||
// Launch request
|
||||
req = netStats.getNetworkStats({start: startDate,
|
||||
end: endDate,
|
||||
manifestURL: url});
|
||||
req.onsuccess = function () {
|
||||
ok(true, "Get app stats request ok");
|
||||
ok(req.result.manifestURL == url, "manifestURL should be equals");
|
||||
ok(req.result.connectionType == null, "connectionTypes should be null");
|
||||
ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
|
||||
ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
|
||||
var data = req.result.data;
|
||||
ok(Array.isArray(data) && data.length == samples,
|
||||
"data is an array of length " + samples);
|
||||
checkDataDates(data, startDate, endDate, sampleRate);
|
||||
next();
|
||||
};
|
||||
req.onerror = function () {
|
||||
ok(false, "Get app stats for all connectionTypes failure!");
|
||||
}
|
||||
},
|
||||
function () {
|
||||
ok(true, "Get system stats for a connectionType and dates not adapted to samplerate");
|
||||
// Prepare get params
|
||||
var type = netStats.connectionTypes[0];
|
||||
var diff = 2;
|
||||
// Get samplerate in millis
|
||||
var sampleRate = netStats.sampleRate * 1000;
|
||||
var endDate = new Date();
|
||||
var startDate = new Date(endDate.getTime() - (sampleRate * diff));
|
||||
// Calculate the number of samples that should be returned based on the
|
||||
|
@ -211,11 +299,11 @@ var steps = [
|
|||
Math.floor(startDate.getTime() / (sampleRate)) * sampleRate) / sampleRate + 1;
|
||||
|
||||
// Launch request
|
||||
req = netStats.getSamples(network, startDate, endDate);
|
||||
req = netStats.getNetworkStats({start: startDate, end: endDate, connectionType: type});
|
||||
req.onsuccess = function () {
|
||||
ok(true, "Get stats request ok");
|
||||
ok(true, "Get system stats request ok");
|
||||
ok(req.result.manifestURL == null, "manifestURL should be null");
|
||||
ok(compareNetworks(req.result.network, network), "networks should be equals");
|
||||
ok(req.result.connectionType == type, "connectionTypes should be equals");
|
||||
ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
|
||||
ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
|
||||
var data = req.result.data;
|
||||
|
@ -225,20 +313,40 @@ var steps = [
|
|||
next();
|
||||
};
|
||||
req.onerror = function () {
|
||||
ok(false, "Get stats failure!");
|
||||
ok(false, "Get system stats for a connectionType failure!");
|
||||
}
|
||||
},
|
||||
function () {
|
||||
// Test clearStats
|
||||
var network = netStats.availableNetworks[0];
|
||||
ok(true, "Get system stats for all connectionTypes and dates not adapted to samplerate");
|
||||
// Prepare get params
|
||||
var diff = 2;
|
||||
// Get samplerate in millis
|
||||
var sampleRate = netStats.sampleRate * 1000;
|
||||
// Get date with samplerate's precision
|
||||
var endDate = new Date();
|
||||
var startDate = new Date(endDate.getTime() - (sampleRate * diff));
|
||||
// Calculate the number of samples that should be returned based on the
|
||||
// the samplerate, including final and initial samples and taking into
|
||||
// account that these will be filtered according to precision.
|
||||
var samples = (Math.floor(endDate.getTime() / (sampleRate)) * sampleRate -
|
||||
Math.floor(startDate.getTime() / (sampleRate)) * sampleRate) / sampleRate + 1;
|
||||
|
||||
req = netStats.clearStats(network);
|
||||
// Launch request
|
||||
req = netStats.getNetworkStats({start: startDate, end: endDate});
|
||||
req.onsuccess = function () {
|
||||
ok(true, "clearStats deleted the database");
|
||||
ok(true, "Get stats request ok");
|
||||
ok(req.result.manifestURL == null, "manifestURL should be null");
|
||||
ok(req.result.connectionType == null, "connectionTypes should be null");
|
||||
ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
|
||||
ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
|
||||
var data = req.result.data;
|
||||
ok(Array.isArray(data) && data.length == samples,
|
||||
"data is an array of length " + samples);
|
||||
checkDataDates(data, startDate, endDate, sampleRate);
|
||||
next();
|
||||
};
|
||||
req.onerror = function () {
|
||||
ok(false, "clearStats deleted the database");
|
||||
ok(false, "Get system stats for all connectionType failure!");
|
||||
}
|
||||
},
|
||||
function () {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
// Test to ensure NetworkStats is enabled but mozNetworkStats.availableNetworks
|
||||
// Test to ensure NetworkStats is enabled but mozNetworkStats.connectionTypes
|
||||
// does not work in content.
|
||||
|
||||
SpecialPowers.setBoolPref("dom.mozNetworkStats.enabled", true);
|
||||
|
@ -22,12 +22,12 @@ ok('mozNetworkStats' in navigator, "navigator.mozNetworkStats should be accessib
|
|||
|
||||
var error;
|
||||
try {
|
||||
navigator.mozNetworkStats.availableNetworks;
|
||||
ok(false, "Accessing navigator.mozNetworkStats.availableNetworks should have thrown!");
|
||||
navigator.mozNetworkStats.connectionTypes;
|
||||
ok(false, "Accessing navigator.mozNetworkStats.connectionTypes should have thrown!");
|
||||
} catch (ex) {
|
||||
error = ex;
|
||||
}
|
||||
ok(error, "Got an exception accessing navigator.mozNetworkStats.availableNetworks");
|
||||
ok(error, "Got an exception accessing navigator.mozNetworkStats.connectionTypes");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
|
|
@ -5,7 +5,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|||
|
||||
Cu.import("resource://gre/modules/NetworkStatsDB.jsm");
|
||||
|
||||
const netStatsDb = new NetworkStatsDB();
|
||||
const netStatsDb = new NetworkStatsDB(this);
|
||||
|
||||
function filterTimestamp(date) {
|
||||
var sampleRate = netStatsDb.sampleRate;
|
||||
|
@ -13,15 +13,6 @@ function filterTimestamp(date) {
|
|||
return Math.floor((date.getTime() - offset) / sampleRate) * sampleRate;
|
||||
}
|
||||
|
||||
function getNetworks() {
|
||||
return [{ id: '0', type: Ci.nsIDOMMozNetworkStatsManager.WIFI },
|
||||
{ id: '1234', type: Ci.nsIDOMMozNetworkStatsManager.MOBILE }];
|
||||
}
|
||||
|
||||
function compareNetworks(networkA, networkB) {
|
||||
return (networkA[0] == networkB[0] && networkA[1] == networkB[1]);
|
||||
}
|
||||
|
||||
add_test(function test_sampleRate() {
|
||||
var sampleRate = netStatsDb.sampleRate;
|
||||
do_check_true(sampleRate > 0);
|
||||
|
@ -98,26 +89,15 @@ add_test(function test_fillResultSamples_noEmptyData() {
|
|||
});
|
||||
|
||||
add_test(function test_clear() {
|
||||
var networks = getNetworks();
|
||||
netStatsDb.clearStats(networks, function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_clear_interface() {
|
||||
var networks = getNetworks();
|
||||
netStatsDb.clearInterfaceStats(networks[0], function (error, result) {
|
||||
netStatsDb.clear(function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_internalSaveStats_singleSample() {
|
||||
var networks = getNetworks();
|
||||
|
||||
var stats = {appId: 0,
|
||||
network: [networks[0].id, networks[0].type],
|
||||
connectionType: "wifi",
|
||||
timestamp: Date.now(),
|
||||
rxBytes: 0,
|
||||
txBytes: 0,
|
||||
|
@ -133,7 +113,7 @@ add_test(function test_internalSaveStats_singleSample() {
|
|||
do_check_eq(error, null);
|
||||
do_check_eq(result.length, 1);
|
||||
do_check_eq(result[0].appId, stats.appId);
|
||||
do_check_true(compareNetworks(result[0].network, stats.network));
|
||||
do_check_eq(result[0].connectionType, stats.connectionType);
|
||||
do_check_eq(result[0].timestamp, stats.timestamp);
|
||||
do_check_eq(result[0].rxBytes, stats.rxBytes);
|
||||
do_check_eq(result[0].txBytes, stats.txBytes);
|
||||
|
@ -145,18 +125,14 @@ add_test(function test_internalSaveStats_singleSample() {
|
|||
});
|
||||
|
||||
add_test(function test_internalSaveStats_arraySamples() {
|
||||
var networks = getNetworks();
|
||||
|
||||
netStatsDb.clearStats(networks, function (error, result) {
|
||||
netStatsDb.clear(function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
|
||||
var network = [networks[0].id, networks[0].type];
|
||||
|
||||
var samples = 2;
|
||||
var stats = [];
|
||||
for (var i = 0; i < samples; i++) {
|
||||
stats.push({appId: 0,
|
||||
network: network,
|
||||
connectionType: "wifi",
|
||||
timestamp: Date.now() + (10 * i),
|
||||
rxBytes: 0,
|
||||
txBytes: 0,
|
||||
|
@ -171,16 +147,12 @@ add_test(function test_internalSaveStats_arraySamples() {
|
|||
|
||||
netStatsDb.logAllRecords(function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
|
||||
// Result has one sample more than samples because clear inserts
|
||||
// an empty sample to keep totalBytes synchronized with netd counters
|
||||
result.shift();
|
||||
do_check_eq(result.length, samples);
|
||||
|
||||
var success = true;
|
||||
for (var i = 1; i < samples; i++) {
|
||||
for (var i = 0; i < samples; i++) {
|
||||
if (result[i].appId != stats[i].appId ||
|
||||
!compareNetworks(result[i].network, stats[i].network) ||
|
||||
result[i].connectionType != stats[i].connectionType ||
|
||||
result[i].timestamp != stats[i].timestamp ||
|
||||
result[i].rxBytes != stats[i].rxBytes ||
|
||||
result[i].txBytes != stats[i].txBytes ||
|
||||
|
@ -198,23 +170,20 @@ add_test(function test_internalSaveStats_arraySamples() {
|
|||
});
|
||||
|
||||
add_test(function test_internalRemoveOldStats() {
|
||||
var networks = getNetworks();
|
||||
|
||||
netStatsDb.clearStats(networks, function (error, result) {
|
||||
netStatsDb.clear(function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
|
||||
var network = [networks[0].id, networks[0].type];
|
||||
var samples = 10;
|
||||
var stats = [];
|
||||
for (var i = 0; i < samples - 1; i++) {
|
||||
stats.push({appId: 0,
|
||||
network: network, timestamp: Date.now() + (10 * i),
|
||||
connectionType: "wifi", timestamp: Date.now() + (10 * i),
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 1234, txTotalBytes: 1234});
|
||||
}
|
||||
|
||||
stats.push({appId: 0,
|
||||
network: network, timestamp: Date.now() + (10 * samples),
|
||||
connectionType: "wifi", timestamp: Date.now() + (10 * samples),
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 1234, txTotalBytes: 1234});
|
||||
|
||||
|
@ -222,7 +191,7 @@ add_test(function test_internalRemoveOldStats() {
|
|||
netStatsDb._saveStats(txn, store, stats);
|
||||
var date = stats[stats.length -1].timestamp
|
||||
+ (netStatsDb.sampleRate * netStatsDb.maxStorageSamples - 1) - 1;
|
||||
netStatsDb._removeOldStats(txn, store, 0, network, date);
|
||||
netStatsDb._removeOldStats(txn, store, 0, "wifi", date);
|
||||
}, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
|
||||
|
@ -236,14 +205,14 @@ add_test(function test_internalRemoveOldStats() {
|
|||
});
|
||||
});
|
||||
|
||||
function processSamplesDiff(networks, lastStat, newStat, callback) {
|
||||
netStatsDb.clearStats(networks, function (error, result){
|
||||
function processSamplesDiff(lastStat, newStat, callback) {
|
||||
netStatsDb.clear(function (error, result){
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
||||
netStatsDb._saveStats(txn, store, lastStat);
|
||||
}, function(error, result) {
|
||||
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
||||
let request = store.index("network").openCursor(newStat.network, "prev");
|
||||
let request = store.index("connectionType").openCursor(newStat.connectionType, "prev");
|
||||
request.onsuccess = function onsuccess(event) {
|
||||
let cursor = event.target.result;
|
||||
do_check_neq(cursor, null);
|
||||
|
@ -261,26 +230,22 @@ function processSamplesDiff(networks, lastStat, newStat, callback) {
|
|||
}
|
||||
|
||||
add_test(function test_processSamplesDiffSameSample() {
|
||||
var networks = getNetworks();
|
||||
var network = [networks[0].id, networks[0].type];
|
||||
|
||||
var sampleRate = netStatsDb.sampleRate;
|
||||
var date = filterTimestamp(new Date());
|
||||
|
||||
var lastStat = {appId: 0,
|
||||
network: network, timestamp: date,
|
||||
connectionType: "wifi", timestamp: date,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 1234, txTotalBytes: 1234};
|
||||
|
||||
var newStat = {appId: 0,
|
||||
network: network, timestamp: date,
|
||||
connectionType: "wifi", timestamp: date,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 2234, txTotalBytes: 2234};
|
||||
|
||||
processSamplesDiff(networks, lastStat, newStat, function(result) {
|
||||
processSamplesDiff(lastStat, newStat, function(result) {
|
||||
do_check_eq(result.length, 1);
|
||||
do_check_eq(result[0].appId, newStat.appId);
|
||||
do_check_true(compareNetworks(result[0].network, newStat.network));
|
||||
do_check_eq(result[0].connectionType, newStat.connectionType);
|
||||
do_check_eq(result[0].timestamp, newStat.timestamp);
|
||||
do_check_eq(result[0].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
|
||||
do_check_eq(result[0].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
|
||||
|
@ -291,26 +256,22 @@ add_test(function test_processSamplesDiffSameSample() {
|
|||
});
|
||||
|
||||
add_test(function test_processSamplesDiffNextSample() {
|
||||
var networks = getNetworks();
|
||||
var network = [networks[0].id, networks[0].type];
|
||||
|
||||
var sampleRate = netStatsDb.sampleRate;
|
||||
var date = filterTimestamp(new Date());
|
||||
|
||||
var lastStat = {appId: 0,
|
||||
network: network, timestamp: date,
|
||||
connectionType: "wifi", timestamp: date,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 1234, txTotalBytes: 1234};
|
||||
|
||||
var newStat = {appId: 0,
|
||||
network: network, timestamp: date + sampleRate,
|
||||
connectionType: "wifi", timestamp: date + sampleRate,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 500, txTotalBytes: 500};
|
||||
|
||||
processSamplesDiff(networks, lastStat, newStat, function(result) {
|
||||
processSamplesDiff(lastStat, newStat, function(result) {
|
||||
do_check_eq(result.length, 2);
|
||||
do_check_eq(result[1].appId, newStat.appId);
|
||||
do_check_true(compareNetworks(result[1].network, newStat.network));
|
||||
do_check_eq(result[1].connectionType, newStat.connectionType);
|
||||
do_check_eq(result[1].timestamp, newStat.timestamp);
|
||||
do_check_eq(result[1].rxBytes, newStat.rxTotalBytes);
|
||||
do_check_eq(result[1].txBytes, newStat.txTotalBytes);
|
||||
|
@ -321,25 +282,23 @@ add_test(function test_processSamplesDiffNextSample() {
|
|||
});
|
||||
|
||||
add_test(function test_processSamplesDiffSamplesLost() {
|
||||
var networks = getNetworks();
|
||||
var network = [networks[0].id, networks[0].type];
|
||||
var samples = 5;
|
||||
var sampleRate = netStatsDb.sampleRate;
|
||||
var date = filterTimestamp(new Date());
|
||||
var lastStat = {appId: 0,
|
||||
network: network, timestamp: date,
|
||||
connectionType: "wifi", timestamp: date,
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 1234, txTotalBytes: 1234};
|
||||
|
||||
var newStat = {appId: 0,
|
||||
network: network, timestamp: date + (sampleRate * samples),
|
||||
connectionType: "wifi", timestamp: date + (sampleRate * samples),
|
||||
rxBytes: 0, txBytes: 0,
|
||||
rxTotalBytes: 2234, txTotalBytes: 2234};
|
||||
|
||||
processSamplesDiff(networks, lastStat, newStat, function(result) {
|
||||
processSamplesDiff(lastStat, newStat, function(result) {
|
||||
do_check_eq(result.length, samples + 1);
|
||||
do_check_eq(result[0].appId, newStat.appId);
|
||||
do_check_true(compareNetworks(result[samples].network, newStat.network));
|
||||
do_check_eq(result[samples].connectionType, newStat.connectionType);
|
||||
do_check_eq(result[samples].timestamp, newStat.timestamp);
|
||||
do_check_eq(result[samples].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
|
||||
do_check_eq(result[samples].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
|
||||
|
@ -350,17 +309,13 @@ add_test(function test_processSamplesDiffSamplesLost() {
|
|||
});
|
||||
|
||||
add_test(function test_saveStats() {
|
||||
var networks = getNetworks();
|
||||
var network = [networks[0].id, networks[0].type];
|
||||
|
||||
var stats = {appId: 0,
|
||||
networkId: networks[0].id,
|
||||
networkType: networks[0].type,
|
||||
connectionType: "wifi",
|
||||
date: new Date(),
|
||||
rxBytes: 2234,
|
||||
txBytes: 2234};
|
||||
|
||||
netStatsDb.clearStats(networks, function (error, result) {
|
||||
netStatsDb.clear(function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.saveStats(stats, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
|
@ -368,7 +323,7 @@ add_test(function test_saveStats() {
|
|||
do_check_eq(error, null);
|
||||
do_check_eq(result.length, 1);
|
||||
do_check_eq(result[0].appId, stats.appId);
|
||||
do_check_true(compareNetworks(result[0].network, network));
|
||||
do_check_eq(result[0].connectionType, stats.connectionType);
|
||||
let timestamp = filterTimestamp(stats.date);
|
||||
do_check_eq(result[0].timestamp, timestamp);
|
||||
do_check_eq(result[0].rxBytes, 0);
|
||||
|
@ -382,44 +337,35 @@ add_test(function test_saveStats() {
|
|||
});
|
||||
|
||||
add_test(function test_saveAppStats() {
|
||||
var networks = getNetworks();
|
||||
var network = [networks[0].id, networks[0].type];
|
||||
|
||||
var stats = {appId: 1,
|
||||
networkId: networks[0].id,
|
||||
networkType: networks[0].type,
|
||||
connectionType: "wifi",
|
||||
date: new Date(),
|
||||
rxBytes: 2234,
|
||||
txBytes: 2234};
|
||||
|
||||
netStatsDb.clearStats(networks, function (error, result) {
|
||||
netStatsDb.clear(function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.saveStats(stats, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.logAllRecords(function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
// The clear function clears all records of the datbase but
|
||||
// inserts a new element for each [appId, connectionId, connectionType]
|
||||
// record to keep the track of rxTotalBytes / txTotalBytes.
|
||||
// So at this point, we have two records, one for the appId 0 used in
|
||||
// past tests and the new one for appId 1
|
||||
do_check_eq(result.length, 2);
|
||||
do_check_eq(result[1].appId, stats.appId);
|
||||
do_check_true(compareNetworks(result[1].network, network));
|
||||
do_check_eq(result.length, 1);
|
||||
do_check_eq(result[0].appId, stats.appId);
|
||||
do_check_eq(result[0].connectionType, stats.connectionType);
|
||||
let timestamp = filterTimestamp(stats.date);
|
||||
do_check_eq(result[1].timestamp, timestamp);
|
||||
do_check_eq(result[1].rxBytes, stats.rxBytes);
|
||||
do_check_eq(result[1].txBytes, stats.txBytes);
|
||||
do_check_eq(result[1].rxTotalBytes, 0);
|
||||
do_check_eq(result[1].txTotalBytes, 0);
|
||||
do_check_eq(result[0].timestamp, timestamp);
|
||||
do_check_eq(result[0].rxBytes, stats.rxBytes);
|
||||
do_check_eq(result[0].txBytes, stats.txBytes);
|
||||
do_check_eq(result[0].rxTotalBytes, 0);
|
||||
do_check_eq(result[0].txTotalBytes, 0);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function prepareFind(network, stats, callback) {
|
||||
netStatsDb.clearStats(network, function (error, result) {
|
||||
function prepareFind(stats, callback) {
|
||||
netStatsDb.clear(function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
||||
netStatsDb._saveStats(txn, store, stats);
|
||||
|
@ -430,11 +376,6 @@ function prepareFind(network, stats, callback) {
|
|||
}
|
||||
|
||||
add_test(function test_find () {
|
||||
var networks = getNetworks();
|
||||
var networkWifi = [networks[0].id, networks[0].type];
|
||||
var networkMobile = [networks[1].id, networks[1].type]; // Fake mobile interface
|
||||
var appId = 0;
|
||||
|
||||
var samples = 5;
|
||||
var sampleRate = netStatsDb.sampleRate;
|
||||
var start = Date.now();
|
||||
|
@ -443,39 +384,46 @@ add_test(function test_find () {
|
|||
start = new Date(start - sampleRate);
|
||||
var stats = [];
|
||||
for (var i = 0; i < samples; i++) {
|
||||
stats.push({ appId: appId,
|
||||
network: networkWifi, timestamp: saveDate + (sampleRate * i),
|
||||
stats.push({appId: 0,
|
||||
connectionType: "wifi", timestamp: saveDate + (sampleRate * i),
|
||||
rxBytes: 0, txBytes: 10,
|
||||
rxTotalBytes: 0, txTotalBytes: 0});
|
||||
|
||||
stats.push({ appId: appId,
|
||||
network: networkMobile, timestamp: saveDate + (sampleRate * i),
|
||||
stats.push({appId: 0,
|
||||
connectionType: "mobile", timestamp: saveDate + (sampleRate * i),
|
||||
rxBytes: 0, txBytes: 10,
|
||||
rxTotalBytes: 0, txTotalBytes: 0});
|
||||
}
|
||||
|
||||
prepareFind(networks[0], stats, function(error, result) {
|
||||
prepareFind(stats, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.find(function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.network.id, networks[0].id);
|
||||
do_check_eq(result.network.type, networks[0].type);
|
||||
do_check_eq(result.connectionType, "wifi");
|
||||
do_check_eq(result.start.getTime(), start.getTime());
|
||||
do_check_eq(result.end.getTime(), end.getTime());
|
||||
do_check_eq(result.data.length, samples + 1);
|
||||
do_check_eq(result.data[0].rxBytes, null);
|
||||
do_check_eq(result.data[1].rxBytes, 0);
|
||||
do_check_eq(result.data[samples].rxBytes, 0);
|
||||
|
||||
netStatsDb.findAll(function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.connectionType, null);
|
||||
do_check_eq(result.start.getTime(), start.getTime());
|
||||
do_check_eq(result.end.getTime(), end.getTime());
|
||||
do_check_eq(result.data.length, samples + 1);
|
||||
do_check_eq(result.data[0].rxBytes, null);
|
||||
do_check_eq(result.data[1].rxBytes, 0);
|
||||
do_check_eq(result.data[1].txBytes, 20);
|
||||
do_check_eq(result.data[samples].rxBytes, 0);
|
||||
run_next_test();
|
||||
}, networks[0], start, end, appId);
|
||||
}, {appId: 0, start: start, end: end});
|
||||
}, {start: start, end: end, connectionType: "wifi", appId: 0});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_findAppStats () {
|
||||
var networks = getNetworks();
|
||||
var networkWifi = [networks[0].id, networks[0].type];
|
||||
var networkMobile = [networks[1].id, networks[1].type]; // Fake mobile interface
|
||||
|
||||
var samples = 5;
|
||||
var sampleRate = netStatsDb.sampleRate;
|
||||
var start = Date.now();
|
||||
|
@ -485,62 +433,68 @@ add_test(function test_findAppStats () {
|
|||
var stats = [];
|
||||
for (var i = 0; i < samples; i++) {
|
||||
stats.push({appId: 1,
|
||||
network: networkWifi, timestamp: saveDate + (sampleRate * i),
|
||||
connectionType: "wifi", timestamp: saveDate + (sampleRate * i),
|
||||
rxBytes: 0, txBytes: 10,
|
||||
rxTotalBytes: 0, txTotalBytes: 0});
|
||||
|
||||
stats.push({appId: 1,
|
||||
network: networkMobile, timestamp: saveDate + (sampleRate * i),
|
||||
connectionType: "mobile", timestamp: saveDate + (sampleRate * i),
|
||||
rxBytes: 0, txBytes: 10,
|
||||
rxTotalBytes: 0, txTotalBytes: 0});
|
||||
}
|
||||
|
||||
prepareFind(networks[0], stats, function(error, result) {
|
||||
prepareFind(stats, function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.find(function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.network.id, networks[0].id);
|
||||
do_check_eq(result.network.type, networks[0].type);
|
||||
do_check_eq(result.connectionType, "wifi");
|
||||
do_check_eq(result.start.getTime(), start.getTime());
|
||||
do_check_eq(result.end.getTime(), end.getTime());
|
||||
do_check_eq(result.data.length, samples + 1);
|
||||
do_check_eq(result.data[0].rxBytes, null);
|
||||
do_check_eq(result.data[1].rxBytes, 0);
|
||||
do_check_eq(result.data[samples].rxBytes, 0);
|
||||
|
||||
netStatsDb.findAll(function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.connectionType, null);
|
||||
do_check_eq(result.start.getTime(), start.getTime());
|
||||
do_check_eq(result.end.getTime(), end.getTime());
|
||||
do_check_eq(result.data.length, samples + 1);
|
||||
do_check_eq(result.data[0].rxBytes, null);
|
||||
do_check_eq(result.data[1].rxBytes, 0);
|
||||
do_check_eq(result.data[1].txBytes, 20);
|
||||
do_check_eq(result.data[samples].rxBytes, 0);
|
||||
run_next_test();
|
||||
}, networks[0], start, end, 1);
|
||||
}, {start: start, end: end, appId: 1});
|
||||
}, {start: start, end: end, connectionType: "wifi", appId: 1});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_saveMultipleAppStats () {
|
||||
var networks = getNetworks();
|
||||
var networkWifi = networks[0];
|
||||
var networkMobile = networks[1]; // Fake mobile interface
|
||||
|
||||
var saveDate = filterTimestamp(new Date());
|
||||
var cached = Object.create(null);
|
||||
|
||||
cached['1wifi'] = { appId: 1, date: new Date(),
|
||||
networkId: networkWifi.id, networkType: networkWifi.type,
|
||||
cached['1wifi'] = {appId: 1,
|
||||
connectionType: "wifi", date: new Date(),
|
||||
rxBytes: 0, txBytes: 10};
|
||||
|
||||
cached['1mobile'] = { appId: 1, date: new Date(),
|
||||
networkId: networkMobile.id, networkType: networkMobile.type,
|
||||
cached['1mobile'] = {appId: 1,
|
||||
connectionType: "mobile", date: new Date(),
|
||||
rxBytes: 0, txBytes: 10};
|
||||
|
||||
cached['2wifi'] = { appId: 2, date: new Date(),
|
||||
networkId: networkWifi.id, networkType: networkWifi.type,
|
||||
cached['2wifi'] = {appId: 2,
|
||||
connectionType: "wifi", date: new Date(),
|
||||
rxBytes: 0, txBytes: 10};
|
||||
|
||||
cached['2mobile'] = { appId: 2, date: new Date(),
|
||||
networkId: networkMobile.id, networkType: networkMobile.type,
|
||||
cached['2mobile'] = {appId: 2,
|
||||
connectionType: "mobile", date: new Date(),
|
||||
rxBytes: 0, txBytes: 10};
|
||||
|
||||
let keys = Object.keys(cached);
|
||||
let index = 0;
|
||||
|
||||
networks.push(networkMobile);
|
||||
netStatsDb.clearStats(networks, function (error, result) {
|
||||
netStatsDb.clear(function (error, result) {
|
||||
do_check_eq(error, null);
|
||||
netStatsDb.saveStats(cached[keys[index]],
|
||||
function callback(error, result) {
|
||||
|
@ -548,17 +502,10 @@ add_test(function test_saveMultipleAppStats () {
|
|||
|
||||
if (index == keys.length - 1) {
|
||||
netStatsDb.logAllRecords(function(error, result) {
|
||||
// Again, result has two samples more than expected samples because
|
||||
// clear inserts one empty sample for each network to keep totalBytes
|
||||
// synchronized with netd counters. so the first two samples have to
|
||||
// be discarted.
|
||||
result.shift();
|
||||
result.shift();
|
||||
|
||||
do_check_eq(error, null);
|
||||
do_check_eq(result.length, 4);
|
||||
do_check_eq(result[0].appId, 1);
|
||||
do_check_true(compareNetworks(result[0].network,[networkWifi.id, networkWifi.type]));
|
||||
do_check_eq(result[0].connectionType, 'mobile');
|
||||
do_check_eq(result[0].rxBytes, 0);
|
||||
do_check_eq(result[0].txBytes, 10);
|
||||
run_next_test();
|
||||
|
|
|
@ -4,56 +4,48 @@
|
|||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
add_test(function test_clearDB() {
|
||||
var networks = NetworkStatsService.availableNetworks();
|
||||
NetworkStatsService._db.clearStats(networks, function onDBCleared(error, result) {
|
||||
NetworkStatsService._db.clear(function onDBCleared(error, result) {
|
||||
do_check_eq(result, null);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
function getNetworkId() {
|
||||
var network = (NetworkStatsService.availableNetworks())[0];
|
||||
return NetworkStatsService.getNetworkId(network.id, network.type);
|
||||
}
|
||||
|
||||
add_test(function test_networkStatsAvailable_ok() {
|
||||
var netId = getNetworkId();
|
||||
NetworkStatsService.networkStatsAvailable(function (success, msg) {
|
||||
do_check_eq(success, true);
|
||||
run_next_test();
|
||||
}, netId, true, 1234, 4321, new Date());
|
||||
}, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, true, 1234, 4321, new Date());
|
||||
});
|
||||
|
||||
add_test(function test_networkStatsAvailable_failure() {
|
||||
var netId = getNetworkId();
|
||||
NetworkStatsService.networkStatsAvailable(function (success, msg) {
|
||||
do_check_eq(success, false);
|
||||
run_next_test();
|
||||
}, netId, false, 1234, 4321, new Date());
|
||||
}, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, false, 1234, 4321, new Date());
|
||||
});
|
||||
|
||||
add_test(function test_update_invalidNetwork() {
|
||||
add_test(function test_update_invalidConnection() {
|
||||
NetworkStatsService.update(-1, function (success, msg) {
|
||||
do_check_eq(success, false);
|
||||
do_check_eq(msg, "Invalid network -1");
|
||||
do_check_eq(msg, "Invalid network type -1");
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_update() {
|
||||
var netId = getNetworkId();
|
||||
NetworkStatsService.update(netId, function (success, msg) {
|
||||
NetworkStatsService.update(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, function (success, msg) {
|
||||
do_check_eq(success, true);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_updateQueueIndex() {
|
||||
NetworkStatsService.updateQueue = [{netId: 0, callbacks: null},
|
||||
{netId: 1, callbacks: null},
|
||||
{netId: 2, callbacks: null},
|
||||
{netId: 3, callbacks: null},
|
||||
{netId: 4, callbacks: null}];
|
||||
NetworkStatsService.updateQueue = [{type: 0, callbacks: null},
|
||||
{type: 1, callbacks: null},
|
||||
{type: 2, callbacks: null},
|
||||
{type: 3, callbacks: null},
|
||||
{type: 4, callbacks: null}];
|
||||
var index = NetworkStatsService.updateQueueIndex(3);
|
||||
do_check_eq(index, 3);
|
||||
index = NetworkStatsService.updateQueueIndex(10);
|
||||
|
@ -71,8 +63,7 @@ add_test(function test_updateAllStats() {
|
|||
});
|
||||
|
||||
add_test(function test_updateStats_ok() {
|
||||
var netId = getNetworkId();
|
||||
NetworkStatsService.updateStats(netId, function(success, msg){
|
||||
NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, function(success, msg){
|
||||
do_check_eq(success, true);
|
||||
run_next_test();
|
||||
});
|
||||
|
@ -86,20 +77,15 @@ add_test(function test_updateStats_failure() {
|
|||
});
|
||||
|
||||
add_test(function test_queue() {
|
||||
// Fill networks with fake network interfaces
|
||||
// Fill connections with fake network interfaces (wlan0 and rmnet0)
|
||||
// to enable netd async requests
|
||||
var network = {id: "1234", type: Ci.nsIDOMMozNetworkStatsManager.MOBILE};
|
||||
var netId1 = NetworkStatsService.getNetworkId(network.id, network.type);
|
||||
NetworkStatsService._networks[netId1] = { network: network,
|
||||
interfaceName: "net1" };
|
||||
NetworkStatsService._connectionTypes[Ci.nsINetworkInterface.NETWORK_TYPE_WIFI]
|
||||
.network.name = 'wlan0';
|
||||
NetworkStatsService._connectionTypes[Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE]
|
||||
.network.name = 'rmnet0';
|
||||
|
||||
network = {id: "5678", type: Ci.nsIDOMMozNetworkStatsManager.MOBILE};
|
||||
var netId2 = NetworkStatsService.getNetworkId(network.id, network.type);
|
||||
NetworkStatsService._networks[netId2] = { network: network,
|
||||
interfaceName: "net2" };
|
||||
|
||||
NetworkStatsService.updateStats(netId1);
|
||||
NetworkStatsService.updateStats(netId2);
|
||||
NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI);
|
||||
NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE);
|
||||
do_check_eq(NetworkStatsService.updateQueue.length, 2);
|
||||
do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 1);
|
||||
|
||||
|
@ -107,8 +93,8 @@ add_test(function test_queue() {
|
|||
return;
|
||||
};
|
||||
|
||||
NetworkStatsService.updateStats(netId1, callback);
|
||||
NetworkStatsService.updateStats(netId2, callback);
|
||||
NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, callback);
|
||||
NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, callback);
|
||||
|
||||
do_check_eq(NetworkStatsService.updateQueue.length, 2);
|
||||
do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 2);
|
||||
|
|
|
@ -9,66 +9,33 @@ XPCOMUtils.defineLazyServiceGetter(this, "nssProxy",
|
|||
"@mozilla.org/networkstatsServiceProxy;1",
|
||||
"nsINetworkStatsServiceProxy");
|
||||
|
||||
function mokConvertNetworkInterface() {
|
||||
NetworkStatsService.convertNetworkInterface = function(aNetwork) {
|
||||
if (aNetwork.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE &&
|
||||
aNetwork.type != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let id = '0';
|
||||
if (aNetwork.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
|
||||
id = '1234'
|
||||
}
|
||||
|
||||
let netId = this.getNetworkId(id, aNetwork.type);
|
||||
|
||||
if (!this._networks[netId]) {
|
||||
this._networks[netId] = Object.create(null);
|
||||
this._networks[netId].network = { id: id,
|
||||
type: aNetwork.type };
|
||||
}
|
||||
|
||||
return netId;
|
||||
};
|
||||
}
|
||||
|
||||
add_test(function test_saveAppStats() {
|
||||
var cachedAppStats = NetworkStatsService.cachedAppStats;
|
||||
var timestamp = NetworkStatsService.cachedAppStatsDate.getTime();
|
||||
var samples = 5;
|
||||
|
||||
// Create to fake nsINetworkInterfaces. As nsINetworkInterface can not
|
||||
// be instantiated, these two vars will emulate it by filling the properties
|
||||
// that will be used.
|
||||
var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
|
||||
var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
|
||||
|
||||
// Insert fake mobile network interface in NetworkStatsService
|
||||
var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
|
||||
|
||||
do_check_eq(Object.keys(cachedAppStats).length, 0);
|
||||
|
||||
for (var i = 0; i < samples; i++) {
|
||||
nssProxy.saveAppStats(1, wifi, timestamp, 10, 20);
|
||||
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
|
||||
timestamp, 10, 20);
|
||||
|
||||
nssProxy.saveAppStats(1, mobile, timestamp, 10, 20);
|
||||
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
|
||||
timestamp, 10, 20);
|
||||
}
|
||||
|
||||
var key1 = 1 + NetworkStatsService.getNetworkId(wifi.id, wifi.type);
|
||||
var key2 = 1 + mobileNetId;
|
||||
var key1 = 1 + 'wifi';
|
||||
var key2 = 1 + 'mobile';
|
||||
|
||||
do_check_eq(Object.keys(cachedAppStats).length, 2);
|
||||
do_check_eq(cachedAppStats[key1].appId, 1);
|
||||
do_check_eq(cachedAppStats[key1].networkId, wifi.id);
|
||||
do_check_eq(cachedAppStats[key1].networkType, wifi.type);
|
||||
do_check_eq(cachedAppStats[key1].connectionType, 'wifi');
|
||||
do_check_eq(new Date(cachedAppStats[key1].date).getTime() / 1000,
|
||||
Math.floor(timestamp / 1000));
|
||||
do_check_eq(cachedAppStats[key1].rxBytes, 50);
|
||||
do_check_eq(cachedAppStats[key1].txBytes, 100);
|
||||
do_check_eq(cachedAppStats[key2].appId, 1);
|
||||
do_check_eq(cachedAppStats[key2].networkId, mobile.id);
|
||||
do_check_eq(cachedAppStats[key2].networkType, mobile.type);
|
||||
do_check_eq(cachedAppStats[key2].connectionType, 'mobile');
|
||||
do_check_eq(new Date(cachedAppStats[key2].date).getTime() / 1000,
|
||||
Math.floor(timestamp / 1000));
|
||||
do_check_eq(cachedAppStats[key2].rxBytes, 50);
|
||||
|
@ -80,11 +47,7 @@ add_test(function test_saveAppStats() {
|
|||
add_test(function test_saveAppStatsWithDifferentDates() {
|
||||
var today = NetworkStatsService.cachedAppStatsDate;
|
||||
var tomorrow = new Date(today.getTime() + (24 * 60 * 60 * 1000));
|
||||
|
||||
var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
|
||||
var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
|
||||
|
||||
var key = 1 + NetworkStatsService.getNetworkId(wifi.id, wifi.type);
|
||||
var key = 1 + 'wifi';
|
||||
|
||||
NetworkStatsService.updateCachedAppStats(
|
||||
function (success, msg) {
|
||||
|
@ -92,20 +55,21 @@ add_test(function test_saveAppStatsWithDifferentDates() {
|
|||
|
||||
do_check_eq(Object.keys(NetworkStatsService.cachedAppStats).length, 0);
|
||||
|
||||
nssProxy.saveAppStats(1, wifi, today.getTime(), 10, 20);
|
||||
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
|
||||
today.getTime(), 10, 20);
|
||||
|
||||
nssProxy.saveAppStats(1, mobile, today.getTime(), 10, 20);
|
||||
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
|
||||
today.getTime(), 10, 20);
|
||||
|
||||
var saveAppStatsCb = {
|
||||
notify: function notify(success, message) {
|
||||
do_check_eq(success, true);
|
||||
|
||||
var cachedAppStats = NetworkStatsService.cachedAppStats;
|
||||
var key = 2 + NetworkStatsService.getNetworkId(mobile.id, mobile.type);
|
||||
var key = 2 + 'mobile';
|
||||
do_check_eq(Object.keys(cachedAppStats).length, 1);
|
||||
do_check_eq(cachedAppStats[key].appId, 2);
|
||||
do_check_eq(cachedAppStats[key].networkId, mobile.id);
|
||||
do_check_eq(cachedAppStats[key].networkType, mobile.type);
|
||||
do_check_eq(cachedAppStats[key].connectionType, 'mobile');
|
||||
do_check_eq(new Date(cachedAppStats[key].date).getTime() / 1000,
|
||||
Math.floor(tomorrow.getTime() / 1000));
|
||||
do_check_eq(cachedAppStats[key].rxBytes, 30);
|
||||
|
@ -115,7 +79,8 @@ add_test(function test_saveAppStatsWithDifferentDates() {
|
|||
}
|
||||
};
|
||||
|
||||
nssProxy.saveAppStats(2, mobile, tomorrow.getTime(), 30, 40, saveAppStatsCb);
|
||||
nssProxy.saveAppStats(2, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
|
||||
tomorrow.getTime(), 30, 40, saveAppStatsCb);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -123,7 +88,6 @@ add_test(function test_saveAppStatsWithDifferentDates() {
|
|||
add_test(function test_saveAppStatsWithMaxCachedTraffic() {
|
||||
var timestamp = NetworkStatsService.cachedAppStatsDate.getTime();
|
||||
var maxtraffic = NetworkStatsService.maxCachedTraffic;
|
||||
var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
|
||||
|
||||
NetworkStatsService.updateCachedAppStats(
|
||||
function (success, msg) {
|
||||
|
@ -132,11 +96,13 @@ add_test(function test_saveAppStatsWithMaxCachedTraffic() {
|
|||
var cachedAppStats = NetworkStatsService.cachedAppStats;
|
||||
do_check_eq(Object.keys(cachedAppStats).length, 0);
|
||||
|
||||
nssProxy.saveAppStats(1, wifi, timestamp, 10, 20);
|
||||
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
|
||||
timestamp, 10, 20);
|
||||
|
||||
do_check_eq(Object.keys(cachedAppStats).length, 1);
|
||||
|
||||
nssProxy.saveAppStats(1, wifi, timestamp, maxtraffic, 20);
|
||||
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
|
||||
timestamp, maxtraffic, 20);
|
||||
|
||||
do_check_eq(Object.keys(cachedAppStats).length, 0);
|
||||
|
||||
|
@ -149,9 +115,5 @@ function run_test() {
|
|||
|
||||
Cu.import("resource://gre/modules/NetworkStatsService.jsm");
|
||||
|
||||
// Function convertNetworkInterface of NetworkStatsService causes errors when dealing
|
||||
// with RIL to get the iccid, so overwrite it.
|
||||
mokConvertNetworkInterface();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ class nsGeolocationRequest
|
|||
void Shutdown();
|
||||
|
||||
void SendLocation(nsIDOMGeoPosition* location);
|
||||
bool WantsHighAccuracy() {return !mShutdown && mOptions && mOptions->mEnableHighAccuracy;}
|
||||
bool WantsHighAccuracy() {return mOptions && mOptions->mEnableHighAccuracy;}
|
||||
void SetTimeoutTimer();
|
||||
nsIPrincipal* GetPrincipal();
|
||||
|
||||
|
@ -445,7 +445,7 @@ nsGeolocationRequest::Allow()
|
|||
maximumAge = mOptions->mMaximumAge;
|
||||
}
|
||||
}
|
||||
gs->UpdateAccuracy(WantsHighAccuracy());
|
||||
gs->SetHigherAccuracy(mOptions && mOptions->mEnableHighAccuracy);
|
||||
|
||||
bool canUseCache = lastPosition && maximumAge > 0 &&
|
||||
(PRTime(PR_Now() / PR_USEC_PER_MSEC) - maximumAge <=
|
||||
|
@ -585,12 +585,12 @@ nsGeolocationRequest::Shutdown()
|
|||
mTimeoutTimer = nullptr;
|
||||
}
|
||||
|
||||
// If there are no other high accuracy requests, the geolocation service will
|
||||
// notify the provider to switch to the default accuracy.
|
||||
// This should happen last, to ensure that this request isn't taken into consideration
|
||||
// when deciding whether existing requests still require high accuracy.
|
||||
if (mOptions && mOptions->mEnableHighAccuracy) {
|
||||
nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
|
||||
if (gs) {
|
||||
gs->UpdateAccuracy();
|
||||
gs->SetHigherAccuracy(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -901,9 +901,9 @@ nsGeolocationService::HighAccuracyRequested()
|
|||
}
|
||||
|
||||
void
|
||||
nsGeolocationService::UpdateAccuracy(bool aForceHigh)
|
||||
nsGeolocationService::SetHigherAccuracy(bool aEnable)
|
||||
{
|
||||
bool highRequired = aForceHigh || HighAccuracyRequested();
|
||||
bool highRequired = aEnable || HighAccuracyRequested();
|
||||
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
ContentChild* cpc = ContentChild::GetSingleton();
|
||||
|
@ -1061,7 +1061,6 @@ Geolocation::Shutdown()
|
|||
|
||||
if (mService) {
|
||||
mService->RemoveLocator(this);
|
||||
mService->UpdateAccuracy();
|
||||
}
|
||||
|
||||
mService = nullptr;
|
||||
|
|
|
@ -85,8 +85,8 @@ public:
|
|||
// create, or reinitalize the callback timer
|
||||
void SetDisconnectTimer();
|
||||
|
||||
// Update the accuracy and notify the provider if changed
|
||||
void UpdateAccuracy(bool aForceHigh = false);
|
||||
// request higher accuracy, if possible
|
||||
void SetHigherAccuracy(bool aEnable);
|
||||
bool HighAccuracyRequested();
|
||||
|
||||
private:
|
||||
|
|
|
@ -73,9 +73,6 @@ function WifiGeoPositionProvider() {
|
|||
this.timer = null;
|
||||
this.hasSeenWiFi = false;
|
||||
this.started = false;
|
||||
// this is only used when logging is enabled, to debug interactions with the
|
||||
// geolocation service
|
||||
this.highAccuracy = false;
|
||||
}
|
||||
|
||||
WifiGeoPositionProvider.prototype = {
|
||||
|
@ -135,12 +132,10 @@ WifiGeoPositionProvider.prototype = {
|
|||
},
|
||||
|
||||
setHighAccuracy: function(enable) {
|
||||
this.highAccuracy = enable;
|
||||
LOG("setting highAccuracy to " + (this.highAccuracy?"TRUE":"FALSE"));
|
||||
},
|
||||
|
||||
onChange: function(accessPoints) {
|
||||
LOG("onChange called, highAccuracy = " + (this.highAccuracy?"TRUE":"FALSE"));
|
||||
LOG("onChange called");
|
||||
this.hasSeenWiFi = true;
|
||||
|
||||
let url = Services.urlFormatter.formatURLPref("geo.wifi.uri");
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
const providerCID = Components.ID("{14aa4b81-e266-45cb-88f8-89595dece114}");
|
||||
const providerContract = "@mozilla.org/geolocation/provider;1";
|
||||
|
||||
const categoryName = "geolocation-provider";
|
||||
|
||||
var provider = {
|
||||
QueryInterface: function eventsink_qi(iid) {
|
||||
if (iid.equals(Components.interfaces.nsISupports) ||
|
||||
iid.equals(Components.interfaces.nsIFactory) ||
|
||||
iid.equals(Components.interfaces.nsIGeolocationProvider))
|
||||
return this;
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
createInstance: function eventsink_ci(outer, iid) {
|
||||
if (outer)
|
||||
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
||||
return this.QueryInterface(iid);
|
||||
},
|
||||
lockFactory: function eventsink_lockf(lock) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
startup: function() {
|
||||
},
|
||||
watch: function() {
|
||||
},
|
||||
shutdown: function() {
|
||||
},
|
||||
setHighAccuracy: function(enable) {
|
||||
this._isHigh = enable;
|
||||
if (enable) {
|
||||
this._seenHigh = true;
|
||||
}
|
||||
},
|
||||
_isHigh: false,
|
||||
_seenHigh: false
|
||||
};
|
||||
|
||||
let runningInParent = true;
|
||||
try {
|
||||
runningInParent = Components.classes["@mozilla.org/xre/runtime;1"].
|
||||
getService(Components.interfaces.nsIXULRuntime).processType
|
||||
== Components.interfaces.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
||||
}
|
||||
catch (e) { }
|
||||
|
||||
function successCallback()
|
||||
{
|
||||
do_check_true(false);
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
function errorCallback()
|
||||
{
|
||||
do_check_true(false);
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
if (runningInParent) {
|
||||
// XPCShell does not get a profile by default. The geolocation service
|
||||
// depends on the settings service which uses IndexedDB and IndexedDB
|
||||
// needs a place where it can store databases.
|
||||
do_get_profile();
|
||||
|
||||
Components.manager.nsIComponentRegistrar.registerFactory(providerCID,
|
||||
"Unit test geo provider", providerContract, provider);
|
||||
var catMan = Components.classes["@mozilla.org/categorymanager;1"]
|
||||
.getService(Components.interfaces.nsICategoryManager);
|
||||
catMan.nsICategoryManager.addCategoryEntry(categoryName, "unit test",
|
||||
providerContract, false, true);
|
||||
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
prefs.setBoolPref("geo.testing.ignore_ipc_principal", true);
|
||||
prefs.setBoolPref("geo.wifi.scan", false);
|
||||
}
|
||||
|
||||
let geolocation = Cc["@mozilla.org/geolocation;1"].createInstance(Ci.nsISupports);
|
||||
|
||||
do_test_pending();
|
||||
|
||||
let watchID1 = geolocation.watchPosition(successCallback, errorCallback);
|
||||
let watchID2 = geolocation.watchPosition(successCallback, errorCallback,
|
||||
{enableHighAccuracy: true});
|
||||
|
||||
do_timeout(1000, function() {
|
||||
geolocation.clearWatch(watchID2);
|
||||
do_timeout(1000, check_results);
|
||||
});
|
||||
}
|
||||
|
||||
function check_results()
|
||||
{
|
||||
if (runningInParent) {
|
||||
// check the provider was set to high accuracy during the test
|
||||
do_check_true(provider._seenHigh);
|
||||
// check the provider is not currently set to high accuracy
|
||||
do_check_false(provider._isHigh);
|
||||
}
|
||||
do_test_finished();
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
const providerCID = Components.ID("{14aa4b81-e266-45cb-88f8-89595dece114}");
|
||||
const providerContract = "@mozilla.org/geolocation/provider;1";
|
||||
|
||||
const categoryName = "geolocation-provider";
|
||||
|
||||
var provider = {
|
||||
QueryInterface: function eventsink_qi(iid) {
|
||||
if (iid.equals(Components.interfaces.nsISupports) ||
|
||||
iid.equals(Components.interfaces.nsIFactory) ||
|
||||
iid.equals(Components.interfaces.nsIGeolocationProvider))
|
||||
return this;
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
createInstance: function eventsink_ci(outer, iid) {
|
||||
if (outer)
|
||||
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
||||
return this.QueryInterface(iid);
|
||||
},
|
||||
lockFactory: function eventsink_lockf(lock) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
startup: function() {
|
||||
},
|
||||
watch: function() {
|
||||
},
|
||||
shutdown: function() {
|
||||
},
|
||||
setHighAccuracy: function(enable) {
|
||||
this._isHigh = enable;
|
||||
if (enable) {
|
||||
this._seenHigh = true;
|
||||
}
|
||||
},
|
||||
_isHigh: false,
|
||||
_seenHigh: false
|
||||
};
|
||||
|
||||
function run_test()
|
||||
{
|
||||
// XPCShell does not get a profile by default. The geolocation service
|
||||
// depends on the settings service which uses IndexedDB and IndexedDB
|
||||
// needs a place where it can store databases.
|
||||
do_get_profile();
|
||||
|
||||
Components.manager.nsIComponentRegistrar.registerFactory(providerCID,
|
||||
"Unit test geo provider", providerContract, provider);
|
||||
var catMan = Components.classes["@mozilla.org/categorymanager;1"]
|
||||
.getService(Components.interfaces.nsICategoryManager);
|
||||
catMan.nsICategoryManager.addCategoryEntry(categoryName, "unit test",
|
||||
providerContract, false, true);
|
||||
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
prefs.setBoolPref("geo.testing.ignore_ipc_principal", true);
|
||||
prefs.setBoolPref("geo.wifi.scan", false);
|
||||
|
||||
run_test_in_child("test_geolocation_reset_accuracy.js", check_results);
|
||||
}
|
||||
|
||||
function check_results()
|
||||
{
|
||||
// check the provider was set to high accuracy during the test
|
||||
do_check_true(provider._seenHigh);
|
||||
// check the provider is not currently set to high accuracy
|
||||
do_check_false(provider._isHigh);
|
||||
|
||||
do_test_finished();
|
||||
}
|
|
@ -12,8 +12,3 @@ skip-if = os == "android"
|
|||
skip-if = os == "android"
|
||||
[test_geolocation_timeout_wrap.js]
|
||||
skip-if = os == "mac" || os == "android"
|
||||
[test_geolocation_reset_accuracy.js]
|
||||
# Bug 919946: test hangs consistently on Android
|
||||
skip-if = os == "android"
|
||||
[test_geolocation_reset_accuracy_wrap.js]
|
||||
skip-if = os == "mac" || os == "android"
|
||||
|
|
|
@ -54,7 +54,7 @@ function takeReferenceSnapshot() {
|
|||
"reference div should disappear when it becomes display:none");
|
||||
}
|
||||
|
||||
function myOnStopFrame(aRequest) {
|
||||
function myOnStopFrame() {
|
||||
gOnStopFrameCounter++;
|
||||
ok(true, "myOnStopFrame called");
|
||||
let currentSnapshot = snapshotWindow(window, false);
|
||||
|
@ -64,7 +64,8 @@ function myOnStopFrame(aRequest) {
|
|||
"at call #" + gOnStopFrameCounter + " to onStopFrame");
|
||||
cleanUpAndFinish();
|
||||
}
|
||||
setTimeout(function() { myOnStopFrame(0, 0); }, 1000);
|
||||
else
|
||||
setTimeout(myOnStopFrame, 1);
|
||||
}
|
||||
|
||||
function failTest() {
|
||||
|
@ -80,8 +81,6 @@ function cleanUpAndFinish() {
|
|||
if (gIsTestFinished) {
|
||||
return;
|
||||
}
|
||||
let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
|
||||
imgLoadingContent.removeObserver(gMyDecoderObserver);
|
||||
SimpleTest.finish();
|
||||
gIsTestFinished = true;
|
||||
}
|
||||
|
@ -89,19 +88,12 @@ function cleanUpAndFinish() {
|
|||
function main() {
|
||||
takeReferenceSnapshot();
|
||||
|
||||
// Create, customize & attach decoder observer
|
||||
observer = new ImageDecoderObserverStub();
|
||||
observer.frameComplete = myOnStopFrame;
|
||||
gMyDecoderObserver =
|
||||
Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
||||
.createScriptedObserver(observer);
|
||||
let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
|
||||
imgLoadingContent.addObserver(gMyDecoderObserver);
|
||||
|
||||
// We want to test the cold loading behavior, so clear cache in case an
|
||||
// earlier test got our image in there already.
|
||||
clearImageCache();
|
||||
|
||||
setTimeout(myOnStopFrame, 1);
|
||||
|
||||
// kick off image-loading! myOnStopFrame handles the rest.
|
||||
gImg.setAttribute("src", "lime-anim-100x100.svg");
|
||||
|
||||
|
|
|
@ -50,11 +50,7 @@ struct jsid;
|
|||
typedef ptrdiff_t jsid;
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
typedef wchar_t jschar;
|
||||
#else
|
||||
typedef uint16_t jschar;
|
||||
#endif
|
||||
typedef char16_t jschar;
|
||||
|
||||
namespace JS {
|
||||
|
||||
|
|
|
@ -4,6 +4,16 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
ifndef .PYMAKE
|
||||
ifeq (,$(MAKE_VERSION))
|
||||
$(error GNU Make is required)
|
||||
endif
|
||||
make_min_ver := 3.81
|
||||
ifneq ($(make_min_ver),$(firstword $(sort $(make_min_ver) $(MAKE_VERSION))))
|
||||
$(error GNU Make $(make_min_ver) or higher is required)
|
||||
endif
|
||||
endif
|
||||
|
||||
TOPLEVEL_BUILD := 1
|
||||
|
||||
run_for_side_effects := $(shell echo "MAKE: $(MAKE)")
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include "assembler/wtf/Assertions.h"
|
||||
#include "assembler/wtf/VMTags.h"
|
||||
#include "js/Utility.h"
|
||||
|
||||
namespace JSC {
|
||||
|
||||
|
@ -42,7 +43,14 @@ size_t ExecutableAllocator::determinePageSize()
|
|||
|
||||
ExecutablePool::Allocation ExecutableAllocator::systemAlloc(size_t n)
|
||||
{
|
||||
void* allocation = mmap(NULL, n, INITIAL_PROTECTION_FLAGS, MAP_PRIVATE | MAP_ANON, VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY, 0);
|
||||
void* allocation;
|
||||
#ifdef JSGC_ROOT_ANALYSIS
|
||||
do {
|
||||
#endif
|
||||
allocation = mmap(NULL, n, INITIAL_PROTECTION_FLAGS, MAP_PRIVATE | MAP_ANON, VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY, 0);
|
||||
#ifdef JSGC_ROOT_ANALYSIS
|
||||
} while (allocation && JS::IsPoisonedPtr(allocation));
|
||||
#endif
|
||||
if (allocation == MAP_FAILED)
|
||||
allocation = NULL;
|
||||
ExecutablePool::Allocation alloc = { reinterpret_cast<char*>(allocation), n };
|
||||
|
|
|
@ -76,8 +76,9 @@ def InvokeClWithDependencyGeneration(cmdline):
|
|||
# We can't handle pathes with spaces properly in mddepend.pl, but
|
||||
# we can assume that anything in a path with spaces is a system
|
||||
# header and throw it away.
|
||||
dep = normcase(dep)
|
||||
if ' ' not in dep:
|
||||
rule.add_dependencies([normcase(dep)])
|
||||
rule.add_dependencies([dep])
|
||||
else:
|
||||
# Make sure we preserve the relevant output from cl. mozprocess
|
||||
# swallows the newline delimiter, so we need to re-add it.
|
||||
|
|
|
@ -632,7 +632,7 @@ endif
|
|||
PWD := $(CURDIR)
|
||||
endif
|
||||
|
||||
NSINSTALL_PY := $(PYTHON) $(call core_abspath,$(topsrcdir)/config/nsinstall.py)
|
||||
NSINSTALL_PY := $(PYTHON) $(abspath $(topsrcdir)/config/nsinstall.py)
|
||||
# For Pymake, wherever we use nsinstall.py we're also going to try to make it
|
||||
# a native command where possible. Since native commands can't be used outside
|
||||
# of single-line commands, we continue to provide INSTALL for general use.
|
||||
|
@ -691,7 +691,7 @@ else
|
|||
IS_LANGUAGE_REPACK = 1
|
||||
endif
|
||||
|
||||
EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/$(1)/en-US,$(call core_realpath,$(L10NBASEDIR))/$(AB_CD)/$(subst /locales,,$(1)))
|
||||
EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/$(1)/en-US,$(or $(realpath $(L10NBASEDIR)),$(abspath $(L10NBASEDIR)))/$(AB_CD)/$(subst /locales,,$(1)))
|
||||
|
||||
ifdef relativesrcdir
|
||||
LOCALE_SRCDIR ?= $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir))
|
||||
|
@ -742,7 +742,7 @@ ifdef MOZ_DEBUG
|
|||
JAVAC_FLAGS += -g
|
||||
endif
|
||||
|
||||
CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py)
|
||||
CREATE_PRECOMPLETE_CMD = $(PYTHON) $(abspath $(topsrcdir)/config/createprecomplete.py)
|
||||
|
||||
# MDDEPDIR is the subdirectory where dependency files are stored
|
||||
MDDEPDIR := .deps
|
||||
|
|
|
@ -16,7 +16,7 @@ $(error Do not include functions.mk twice!)
|
|||
endif
|
||||
INCLUDED_FUNCTIONS_MK = 1
|
||||
|
||||
core_abspath = $(if $(findstring :,$(1)),$(1),$(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1)))
|
||||
core_realpath = $(if $(realpath $(1)),$(realpath $(1)),$(call core_abspath,$(1)))
|
||||
core_abspath = $(error core_abspath is unsupported, use $$(abspath) instead)
|
||||
core_realpath = $(error core_realpath is unsupported)
|
||||
|
||||
core_winabspath = $(firstword $(subst /, ,$(call core_abspath,$(1)))):$(subst $(space),,$(patsubst %,\\%,$(wordlist 2,$(words $(subst /, ,$(call core_abspath,$(1)))), $(strip $(subst /, ,$(call core_abspath,$(1)))))))
|
||||
core_winabspath = $(error core_winabspath is unsupported)
|
||||
|
|
|
@ -4076,11 +4076,13 @@ elif test "$OS_ARCH" != "WINNT" -a "$OS_ARCH" != "OS2"; then
|
|||
AC_DEFINE(XP_UNIX)
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(threadsafe,
|
||||
[ --enable-threadsafe Enable support for multiple threads.],
|
||||
[if test "x$enableval" = "xyes"; then
|
||||
JS_THREADSAFE=1
|
||||
MOZ_ARG_DISABLE_BOOL(threadsafe,
|
||||
[ --disable-threadsafe Disable support for multiple threads.],
|
||||
JS_THREADSAFE= )
|
||||
if test -n "$JS_THREADSAFE"; then
|
||||
AC_DEFINE(JS_THREADSAFE)
|
||||
fi],)
|
||||
fi
|
||||
|
||||
if test "$MOZ_DEBUG"; then
|
||||
AC_DEFINE(MOZ_REFLOW_PERF)
|
||||
|
|
|
@ -253,8 +253,8 @@ assertEq(f(-1,false), 0);
|
|||
assertEq(f(-5,false), 1);
|
||||
|
||||
assertAsmTypeFail('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return (i32[0]+1)|0 } return f");
|
||||
assertAsmTypeFail('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] + 1.0); } return f");
|
||||
new Float64Array(BUF_64KB)[0] = 2.3;
|
||||
assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] + 2.0) } return f"), this, null, BUF_64KB)(), 2.3+2);
|
||||
assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] - 2.0) } return f"), this, null, BUF_64KB)(), 2.3-2);
|
||||
assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] * 2.0) } return f"), this, null, BUF_64KB)(), 2.3*2);
|
||||
assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] / 2.0) } return f"), this, null, BUF_64KB)(), 2.3/2);
|
||||
|
|
|
@ -91,6 +91,12 @@ assertEq(f(0, 1.3), 1);
|
|||
assertEq(f(4088, 2.5), 2);
|
||||
assertEq(f(4096, 3.8), 0);
|
||||
|
||||
var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j) {i=i|0;j=+j; f64[i>>3] = j; return (~~f64[i>>3])|0}; return f');
|
||||
var f = asmLink(code, this, null, new ArrayBuffer(4096));
|
||||
assertEq(f(0, 1.3), 1);
|
||||
assertEq(f(4088, 2.5), 2);
|
||||
assertEq(f(4096, 3.8), 0);
|
||||
|
||||
var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j) {i=i|0;j=+j; f64[i>>3] = j; return +f64[i>>3]}; return f');
|
||||
var f = asmLink(code, this, null, new ArrayBuffer(4096));
|
||||
assertEq(f(0, 1.3), 1.3);
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
gcparam("maxBytes", gcparam("gcBytes") + 1024);
|
||||
test();
|
||||
function test() {
|
||||
var upvar = "";
|
||||
function f() { upvar += ""; }
|
||||
test();
|
||||
eval('');
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ evaluate("gcparam(\"maxBytes\", gcparam(\"gcBytes\") + 4*1024);");
|
|||
evaluate("\
|
||||
function testDontEnum(F) { \
|
||||
function test() {\
|
||||
var upvar = \"\";\
|
||||
function f() { upvar += \"\"; }\
|
||||
typeof (new test(\"1\")) != 'function'\
|
||||
}\
|
||||
test();\
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// |jit-test| error: TypeError
|
||||
|
||||
function $ERROR() {}
|
||||
function testMultipleArgumentsObjects() {
|
||||
var testargs = arguments;
|
||||
var f = function (which) {
|
||||
var args = [ testargs ];
|
||||
return args[which][0];
|
||||
};
|
||||
var arr = [0, 0, 0, 0, 1];
|
||||
for (var i = 0; i < arr.length; i++)
|
||||
$ERROR[i] = f(arr[i]);
|
||||
}
|
||||
testMultipleArgumentsObjects()
|
|
@ -387,8 +387,7 @@ class Type
|
|||
Signed,
|
||||
Unsigned,
|
||||
Intish,
|
||||
Void,
|
||||
Unknown
|
||||
Void
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -414,7 +413,7 @@ class Type
|
|||
}
|
||||
|
||||
bool isIntish() const {
|
||||
return isInt() || which_ == Intish || which_ == Unknown;
|
||||
return isInt() || which_ == Intish;
|
||||
}
|
||||
|
||||
bool isDouble() const {
|
||||
|
@ -422,7 +421,7 @@ class Type
|
|||
}
|
||||
|
||||
bool isDoublish() const {
|
||||
return isDouble() || which_ == Doublish || which_ == Unknown;
|
||||
return isDouble() || which_ == Doublish;
|
||||
}
|
||||
|
||||
bool isVoid() const {
|
||||
|
@ -449,7 +448,6 @@ class Type
|
|||
case Intish:
|
||||
return MIRType_Int32;
|
||||
case Void:
|
||||
case Unknown:
|
||||
return MIRType_None;
|
||||
}
|
||||
MOZ_ASSUME_UNREACHABLE("Invalid Type");
|
||||
|
@ -465,7 +463,6 @@ class Type
|
|||
case Unsigned: return "unsigned";
|
||||
case Intish: return "intish";
|
||||
case Void: return "void";
|
||||
case Unknown: return "unknown";
|
||||
}
|
||||
MOZ_ASSUME_UNREACHABLE("Invalid Type");
|
||||
}
|
||||
|
@ -3947,14 +3944,14 @@ CheckCoerceToInt(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *
|
|||
if (!CheckExpr(f, operand, &operandDef, &operandType))
|
||||
return false;
|
||||
|
||||
if (operandType.isDouble()) {
|
||||
if (operandType.isDoublish()) {
|
||||
*def = f.unary<MTruncateToInt32>(operandDef);
|
||||
*type = Type::Signed;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!operandType.isIntish())
|
||||
return f.failf(operand, "%s is not a subtype of double or intish", operandType.toChars());
|
||||
return f.failf(operand, "%s is not a subtype of doublish or intish", operandType.toChars());
|
||||
|
||||
*def = operandDef;
|
||||
*type = Type::Signed;
|
||||
|
@ -4160,29 +4157,20 @@ CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *typ
|
|||
if (numAddOrSub > (1<<20))
|
||||
return f.fail(expr, "too many + or - without intervening coercion");
|
||||
|
||||
if (expr->isKind(PNK_ADD)) {
|
||||
if (lhsType.isInt() && rhsType.isInt()) {
|
||||
*def = f.binary<MAdd>(lhsDef, rhsDef, MIRType_Int32);
|
||||
*type = Type::Intish;
|
||||
} else if (lhsType.isDouble() && rhsType.isDouble()) {
|
||||
*def = f.binary<MAdd>(lhsDef, rhsDef, MIRType_Double);
|
||||
*type = Type::Double;
|
||||
} else {
|
||||
return f.failf(expr, "operands to + must both be int or double, got %s and %s",
|
||||
lhsType.toChars(), rhsType.toChars());
|
||||
}
|
||||
} else {
|
||||
if (lhsType.isInt() && rhsType.isInt()) {
|
||||
*def = f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32);
|
||||
*def = expr->isKind(PNK_ADD)
|
||||
? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Int32)
|
||||
: f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32);
|
||||
*type = Type::Intish;
|
||||
} else if (lhsType.isDoublish() && rhsType.isDoublish()) {
|
||||
*def = f.binary<MSub>(lhsDef, rhsDef, MIRType_Double);
|
||||
*def = expr->isKind(PNK_ADD)
|
||||
? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Double)
|
||||
: f.binary<MSub>(lhsDef, rhsDef, MIRType_Double);
|
||||
*type = Type::Double;
|
||||
} else {
|
||||
return f.failf(expr, "operands to - must both be int or doublish, got %s and %s",
|
||||
return f.failf(expr, "operands to +/- must both be int or doublish, got %s and %s",
|
||||
lhsType.toChars(), rhsType.toChars());
|
||||
}
|
||||
}
|
||||
|
||||
if (numAddOrSubOut)
|
||||
*numAddOrSubOut = numAddOrSub;
|
||||
|
|
|
@ -8845,6 +8845,11 @@ DoInstanceOfFallback(JSContext *cx, ICInstanceOf_Fallback *stub,
|
|||
|
||||
RootedObject obj(cx, &rhs.toObject());
|
||||
|
||||
// For functions, keep track of the |prototype| property in type information,
|
||||
// for use during Ion compilation.
|
||||
if (obj->is<JSFunction>() && IsIonEnabled(cx))
|
||||
types::EnsureTrackPropertyTypes(cx, obj, NameToId(cx->names().prototype));
|
||||
|
||||
bool cond = false;
|
||||
if (!HasInstance(cx, obj, lhs, &cond))
|
||||
return false;
|
||||
|
|
|
@ -6056,15 +6056,17 @@ CodeGenerator::addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Regi
|
|||
bool
|
||||
CodeGenerator::addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex,
|
||||
Register temp, FloatRegister tempFloat, ValueOperand index,
|
||||
ConstantOrRegister value, bool strict)
|
||||
ConstantOrRegister value, bool strict, bool guardHoles)
|
||||
{
|
||||
switch (gen->info().executionMode()) {
|
||||
case SequentialExecution: {
|
||||
SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict);
|
||||
SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict,
|
||||
guardHoles);
|
||||
return addCache(ins, allocateCache(cache));
|
||||
}
|
||||
case ParallelExecution: {
|
||||
SetElementParIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict);
|
||||
SetElementParIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict,
|
||||
guardHoles);
|
||||
return addCache(ins, allocateCache(cache));
|
||||
}
|
||||
default:
|
||||
|
@ -6218,7 +6220,7 @@ CodeGenerator::visitSetElementCacheV(LSetElementCacheV *ins)
|
|||
ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetElementCacheV::Value));
|
||||
|
||||
return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value,
|
||||
ins->mir()->strict());
|
||||
ins->mir()->strict(), ins->mir()->guardHoles());
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -6237,7 +6239,7 @@ CodeGenerator::visitSetElementCacheT(LSetElementCacheT *ins)
|
|||
value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(tmp));
|
||||
|
||||
return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value,
|
||||
ins->mir()->strict());
|
||||
ins->mir()->strict(), ins->mir()->guardHoles());
|
||||
}
|
||||
|
||||
typedef bool (*SetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, HandleValue);
|
||||
|
|
|
@ -343,7 +343,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
bool needsTypeBarrier);
|
||||
bool addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex, Register temp,
|
||||
FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
|
||||
bool strict);
|
||||
bool strict, bool guardHoles);
|
||||
bool checkForAbortPar(LInstruction *lir);
|
||||
|
||||
bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
|
||||
|
|
|
@ -211,16 +211,25 @@ IsPhiObservable(MPhi *phi, Observability observe)
|
|||
break;
|
||||
}
|
||||
|
||||
// If the Phi is of the |this| value, it must always be observable.
|
||||
uint32_t slot = phi->slot();
|
||||
CompileInfo &info = phi->block()->info();
|
||||
if (info.fun() && slot == info.thisSlot())
|
||||
JSFunction *fun = info.fun();
|
||||
|
||||
// If the Phi is of the |this| value, it must always be observable.
|
||||
if (fun && slot == info.thisSlot())
|
||||
return true;
|
||||
|
||||
// If the function is heavyweight, and the Phi is of the |scopeChain|
|
||||
// value, and the function may need an arguments object, then make sure
|
||||
// to preserve the scope chain, because it may be needed to construct the
|
||||
// arguments object during bailout.
|
||||
if (fun && fun->isHeavyweight() && info.hasArguments() && slot == info.scopeChainSlot())
|
||||
return true;
|
||||
|
||||
// If the Phi is one of the formal argument, and we are using an argument
|
||||
// object in the function. The phi might be observable after a bailout.
|
||||
// For inlined frames this is not needed, as they are captured in the inlineResumePoint.
|
||||
if (info.fun() && info.hasArguments()) {
|
||||
if (fun && info.hasArguments()) {
|
||||
uint32_t first = info.firstArgSlot();
|
||||
if (first <= slot && slot - first < info.nargs()) {
|
||||
// If arguments obj aliases formals, then the arg slots will never be used.
|
||||
|
|
|
@ -7232,8 +7232,14 @@ IonBuilder::setElemTryCache(bool *emitted, MDefinition *object,
|
|||
return true;
|
||||
}
|
||||
|
||||
// We can avoid worrying about holes in the IC if we know a priori we are safe
|
||||
// from them. If TI can guard that there are no indexed properties on the prototype
|
||||
// chain, we know that we anen't missing any setters by overwriting the hole with
|
||||
// another value.
|
||||
bool guardHoles = ElementAccessHasExtraIndexedProperty(constraints(), object);
|
||||
|
||||
// Emit SetElementCache.
|
||||
MInstruction *ins = MSetElementCache::New(object, index, value, script()->strict);
|
||||
MInstruction *ins = MSetElementCache::New(object, index, value, script()->strict, guardHoles);
|
||||
current->add(ins);
|
||||
current->push(value);
|
||||
|
||||
|
|
|
@ -1554,17 +1554,19 @@ GetPropertyIC::tryAttachProxy(JSContext *cx, IonScript *ion, HandleObject obj,
|
|||
|
||||
static void
|
||||
GenerateProxyClassGuards(MacroAssembler &masm, Register object, Register scratchReg,
|
||||
Label *failures, Label *success)
|
||||
Label *failures)
|
||||
{
|
||||
Label success;
|
||||
// Ensure that the incoming object has one of the magic class pointers, i.e,
|
||||
// that it is one of an ObjectProxy, FunctionProxy, or OuterWindowProxy.
|
||||
// This is equivalent to obj->is<ProxyObject>().
|
||||
masm.branchTestObjClass(Assembler::Equal, object, scratchReg,
|
||||
CallableProxyClassPtr, success);
|
||||
CallableProxyClassPtr, &success);
|
||||
masm.branchTestObjClass(Assembler::Equal, object, scratchReg,
|
||||
UncallableProxyClassPtr, success);
|
||||
UncallableProxyClassPtr, &success);
|
||||
masm.branchTestObjClass(Assembler::NotEqual, object, scratchReg,
|
||||
OuterWindowProxyClassPtr, failures);
|
||||
masm.bind(&success);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1592,9 +1594,7 @@ GetPropertyIC::tryAttachGenericProxy(JSContext *cx, IonScript *ion, HandleObject
|
|||
|
||||
masm.setFramePushed(ion->frameSize());
|
||||
|
||||
Label proxySuccess;
|
||||
GenerateProxyClassGuards(masm, object(), scratchReg, &failures, &proxySuccess);
|
||||
masm.bind(&proxySuccess);
|
||||
GenerateProxyClassGuards(masm, object(), scratchReg, &failures);
|
||||
|
||||
// Ensure that the incoming object is not a DOM proxy, so that we can get to
|
||||
// the specialized stubs
|
||||
|
@ -2179,12 +2179,12 @@ SetPropertyIC::attachGenericProxy(JSContext *cx, IonScript *ion, void *returnAdd
|
|||
Register scratch = regSet.takeGeneral();
|
||||
masm.push(scratch);
|
||||
|
||||
GenerateProxyClassGuards(masm, object(), scratch, &proxyFailures, &proxySuccess);
|
||||
GenerateProxyClassGuards(masm, object(), scratch, &proxyFailures);
|
||||
|
||||
// Remove the DOM proxies. They'll take care of themselves so this stub doesn't
|
||||
// catch too much.
|
||||
masm.branchTestProxyHandlerFamily(Assembler::Equal, object(), scratch,
|
||||
GetDOMProxyHandlerFamily(), &proxyFailures);
|
||||
// catch too much. The failure case is actually Equal. Fall through to the failure code.
|
||||
masm.branchTestProxyHandlerFamily(Assembler::NotEqual, object(), scratch,
|
||||
GetDOMProxyHandlerFamily(), &proxySuccess);
|
||||
|
||||
masm.bind(&proxyFailures);
|
||||
masm.pop(scratch);
|
||||
|
@ -3466,14 +3466,17 @@ IsTypedArrayElementSetInlineable(JSObject *obj, const Value &idval, const Value
|
|||
|
||||
static bool
|
||||
GenerateSetDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
|
||||
JSObject *obj, const Value &idval, Register object, ValueOperand indexVal,
|
||||
ConstantOrRegister value, Register tempToUnboxIndex, Register temp)
|
||||
JSObject *obj, const Value &idval, bool guardHoles, Register object,
|
||||
ValueOperand indexVal, ConstantOrRegister value, Register tempToUnboxIndex,
|
||||
Register temp)
|
||||
{
|
||||
JS_ASSERT(obj->isNative());
|
||||
JS_ASSERT(idval.isInt32());
|
||||
|
||||
Label failures;
|
||||
Label outOfBounds; // index >= capacity || index > initialized length
|
||||
Label outOfBounds; // index represents a known hole, or an illegal append
|
||||
|
||||
Label markElem, postBarrier; // used if TI protects us from worrying about holes.
|
||||
|
||||
// Guard object is a dense array.
|
||||
Shape *shape = obj->lastProperty();
|
||||
|
@ -3495,6 +3498,14 @@ GenerateSetDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttac
|
|||
// Compute the location of the element.
|
||||
BaseIndex target(elements, index, TimesEight);
|
||||
|
||||
// If TI cannot help us deal with HOLES by preventing indexed properties
|
||||
// on the prototype chain, we have to be very careful to check for ourselves
|
||||
// to avoid stomping on what should be a setter call. Start by only allowing things
|
||||
// within the initialized length.
|
||||
if (guardHoles) {
|
||||
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
|
||||
masm.branch32(Assembler::BelowOrEqual, initLength, index, &outOfBounds);
|
||||
} else {
|
||||
// Guard that we can increase the initialized length.
|
||||
Address capacity(elements, ObjectElements::offsetOfCapacity());
|
||||
masm.branch32(Assembler::BelowOrEqual, capacity, index, &outOfBounds);
|
||||
|
@ -3504,7 +3515,6 @@ GenerateSetDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttac
|
|||
masm.branch32(Assembler::Below, initLength, index, &outOfBounds);
|
||||
|
||||
// if (initLength == index)
|
||||
Label markElem, postBarrier;
|
||||
masm.branch32(Assembler::NotEqual, initLength, index, &markElem);
|
||||
{
|
||||
// Increase initialize length.
|
||||
|
@ -3524,20 +3534,22 @@ GenerateSetDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttac
|
|||
masm.jump(&postBarrier);
|
||||
}
|
||||
// else
|
||||
{
|
||||
// Mark old element.
|
||||
masm.bind(&markElem);
|
||||
if (cx->zone()->needsBarrier())
|
||||
masm.callPreBarrier(target, MIRType_Value);
|
||||
}
|
||||
|
||||
// Call post barrier if necessary, and recalculate elements pointer if it got cobbered.
|
||||
if (cx->zone()->needsBarrier())
|
||||
masm.callPreBarrier(target, MIRType_Value);
|
||||
|
||||
// Call post barrier if necessary, and recalculate elements pointer if it got clobbered.
|
||||
if (!guardHoles)
|
||||
masm.bind(&postBarrier);
|
||||
Register postBarrierScratch = elements;
|
||||
if (masm.maybeCallPostBarrier(object, value, postBarrierScratch))
|
||||
masm.loadPtr(Address(object, JSObject::offsetOfElements()), elements);
|
||||
|
||||
// Store the value.
|
||||
if (guardHoles)
|
||||
masm.branchTestMagic(Assembler::Equal, target, &failures);
|
||||
masm.storeConstantOrRegister(value, target);
|
||||
}
|
||||
attacher.jumpRejoin(masm);
|
||||
|
@ -3556,14 +3568,18 @@ SetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, c
|
|||
MacroAssembler masm(cx);
|
||||
RepatchStubAppender attacher(*this);
|
||||
if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval,
|
||||
object(), index(), value(),
|
||||
tempToUnboxIndex(), temp()))
|
||||
guardHoles(), object(), index(),
|
||||
value(), tempToUnboxIndex(),
|
||||
temp()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
setHasDenseStub();
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "dense array");
|
||||
const char *message = guardHoles() ?
|
||||
"dense array (holes)" :
|
||||
"dense array";
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, message);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -3704,13 +3720,18 @@ SetElementParIC::attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObjec
|
|||
MacroAssembler masm(cx);
|
||||
DispatchStubPrepender attacher(*this);
|
||||
if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval,
|
||||
object(), index(), value(),
|
||||
tempToUnboxIndex(), temp()))
|
||||
guardHoles(), object(), index(),
|
||||
value(), tempToUnboxIndex(),
|
||||
temp()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, "parallel dense array");
|
||||
const char *message = guardHoles() ?
|
||||
"parallel dense array (holes)" :
|
||||
"parallel dense array";
|
||||
|
||||
return linkAndAttachStub(cx, masm, attacher, ion, message);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -808,13 +808,14 @@ class SetElementIC : public RepatchIonCache
|
|||
ValueOperand index_;
|
||||
ConstantOrRegister value_;
|
||||
bool strict_;
|
||||
bool guardHoles_;
|
||||
|
||||
bool hasDenseStub_ : 1;
|
||||
|
||||
public:
|
||||
SetElementIC(Register object, Register tempToUnboxIndex, Register temp,
|
||||
FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
|
||||
bool strict)
|
||||
bool strict, bool guardHoles)
|
||||
: object_(object),
|
||||
tempToUnboxIndex_(tempToUnboxIndex),
|
||||
temp_(temp),
|
||||
|
@ -822,6 +823,7 @@ class SetElementIC : public RepatchIonCache
|
|||
index_(index),
|
||||
value_(value),
|
||||
strict_(strict),
|
||||
guardHoles_(guardHoles),
|
||||
hasDenseStub_(false)
|
||||
{
|
||||
}
|
||||
|
@ -851,6 +853,9 @@ class SetElementIC : public RepatchIonCache
|
|||
bool strict() const {
|
||||
return strict_;
|
||||
}
|
||||
bool guardHoles() const {
|
||||
return guardHoles_;
|
||||
}
|
||||
|
||||
bool hasDenseStub() const {
|
||||
return hasDenseStub_;
|
||||
|
@ -1182,18 +1187,20 @@ class SetElementParIC : public ParallelIonCache
|
|||
ValueOperand index_;
|
||||
ConstantOrRegister value_;
|
||||
bool strict_;
|
||||
bool guardHoles_;
|
||||
|
||||
public:
|
||||
SetElementParIC(Register object, Register tempToUnboxIndex, Register temp,
|
||||
FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
|
||||
bool strict)
|
||||
bool strict, bool guardHoles)
|
||||
: object_(object),
|
||||
tempToUnboxIndex_(tempToUnboxIndex),
|
||||
temp_(temp),
|
||||
tempFloat_(tempFloat),
|
||||
index_(index),
|
||||
value_(value),
|
||||
strict_(strict)
|
||||
strict_(strict),
|
||||
guardHoles_(guardHoles)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1226,6 +1233,9 @@ class SetElementParIC : public ParallelIonCache
|
|||
bool strict() const {
|
||||
return strict_;
|
||||
}
|
||||
bool guardHoles() const {
|
||||
return guardHoles_;
|
||||
}
|
||||
|
||||
bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval);
|
||||
bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr);
|
||||
|
|
|
@ -2802,6 +2802,7 @@ PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *const
|
|||
if (name && object->singleton() && object->singleton()->isNative()) {
|
||||
Shape *shape = object->singleton()->nativeLookup(cx, name);
|
||||
if (shape &&
|
||||
shape->hasSlot() &&
|
||||
shape->hasDefaultGetter() &&
|
||||
object->singleton()->nativeGetSlot(shape->slot()).isUndefined())
|
||||
{
|
||||
|
|
|
@ -7135,10 +7135,13 @@ class MSetElementCache
|
|||
public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
|
||||
{
|
||||
bool strict_;
|
||||
bool guardHoles_;
|
||||
|
||||
MSetElementCache(MDefinition *obj, MDefinition *index, MDefinition *value, bool strict)
|
||||
MSetElementCache(MDefinition *obj, MDefinition *index, MDefinition *value, bool strict,
|
||||
bool guardHoles)
|
||||
: MSetElementInstruction(obj, index, value),
|
||||
strict_(strict)
|
||||
strict_(strict),
|
||||
guardHoles_(guardHoles)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -7146,13 +7149,16 @@ class MSetElementCache
|
|||
INSTRUCTION_HEADER(SetElementCache);
|
||||
|
||||
static MSetElementCache *New(MDefinition *obj, MDefinition *index, MDefinition *value,
|
||||
bool strict) {
|
||||
return new MSetElementCache(obj, index, value, strict);
|
||||
bool strict, bool guardHoles) {
|
||||
return new MSetElementCache(obj, index, value, strict, guardHoles);
|
||||
}
|
||||
|
||||
bool strict() const {
|
||||
return strict_;
|
||||
}
|
||||
bool guardHoles() const {
|
||||
return guardHoles_;
|
||||
}
|
||||
|
||||
TypePolicy *typePolicy() {
|
||||
return this;
|
||||
|
|
|
@ -10,4 +10,6 @@
|
|||
|
||||
#include "js/RequiredDefines.h"
|
||||
|
||||
#include "mozilla/Char16.h"
|
||||
|
||||
#endif /* js_confdefs_h */
|
||||
|
|
|
@ -50,8 +50,6 @@ BEGIN_TEST(testChromeBuffer)
|
|||
|
||||
if (!JS_AddNamedObjectRoot(cx, &trusted_glob, "trusted-global"))
|
||||
return false;
|
||||
if (!JS_AddNamedObjectRoot(cx, &trusted_fun, "trusted-function"))
|
||||
return false;
|
||||
|
||||
JSFunction *fun;
|
||||
|
||||
|
@ -70,6 +68,8 @@ BEGIN_TEST(testChromeBuffer)
|
|||
"trusted", 1, ¶mName, bytes, strlen(bytes),
|
||||
"", 0));
|
||||
trusted_fun = JS_GetFunctionObject(fun);
|
||||
if (!JS_AddNamedObjectRoot(cx, &trusted_fun, "trusted-function"))
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun));
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
@ -83,7 +84,6 @@ class MediaPipeline : public sigslot::has_slots<> {
|
|||
rtcp_state_(MP_CONNECTING),
|
||||
main_thread_(main_thread),
|
||||
sts_thread_(sts_thread),
|
||||
transport_(new PipelineTransport(this)),
|
||||
rtp_send_srtp_(),
|
||||
rtcp_send_srtp_(),
|
||||
rtp_recv_srtp_(),
|
||||
|
@ -102,6 +102,8 @@ class MediaPipeline : public sigslot::has_slots<> {
|
|||
if (!rtcp_transport_) {
|
||||
rtcp_transport_ = rtp_transport;
|
||||
}
|
||||
// PipelineTransport() will access this->sts_thread_; moved here for safety
|
||||
transport_ = new PipelineTransport(this);
|
||||
}
|
||||
|
||||
virtual ~MediaPipeline();
|
||||
|
@ -517,7 +519,7 @@ class MediaPipelineReceiveVideo : public MediaPipelineReceive {
|
|||
MediaPipelineReceive(pc, main_thread, sts_thread,
|
||||
stream, track_id, conduit, rtp_transport,
|
||||
rtcp_transport),
|
||||
renderer_(new PipelineRenderer(this)),
|
||||
renderer_(new PipelineRenderer(MOZ_THIS_IN_INITIALIZER_LIST())),
|
||||
listener_(new PipelineListener(stream->AsSourceStream(), track_id)) {
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* character literals. C++11's char16_t is a distinct builtin type. C11's
|
||||
* char16_t is a typedef for uint_least16_t. Technically, char16_t is a 16-bit
|
||||
* code unit of a Unicode code point, not a "character".
|
||||
*
|
||||
* For now, Char16.h only supports C++ because we don't want mix different C
|
||||
* and C++ definitions of char16_t in the same code base.
|
||||
*/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
@ -39,10 +36,26 @@
|
|||
* typedef from wchar_t.
|
||||
*/
|
||||
# define MOZ_CHAR16_IS_NOT_WCHAR
|
||||
#elif !defined(__cplusplus)
|
||||
# if defined(WIN32)
|
||||
# include <yvals.h>
|
||||
typedef wchar_t char16_t;
|
||||
# else
|
||||
/**
|
||||
* We can't use the stdint.h uint16_t type here because including
|
||||
* stdint.h will break building some of our C libraries, such as
|
||||
* sqlite.
|
||||
*/
|
||||
typedef unsigned short char16_t;
|
||||
# endif
|
||||
#else
|
||||
# error "Char16.h requires C++11 (or something like it) for UTF-16 support."
|
||||
#endif
|
||||
|
||||
/* This is a temporary hack until bug 927728 is fixed. */
|
||||
#define __PRUNICHAR__
|
||||
typedef char16_t PRUnichar;
|
||||
|
||||
/*
|
||||
* Macro arguments used in concatenation or stringification won't be expanded.
|
||||
* Therefore, in order for |MOZ_UTF16(FOO)| to work as expected (which is to
|
||||
|
@ -53,9 +66,12 @@
|
|||
*/
|
||||
#define MOZ_UTF16(s) MOZ_UTF16_HELPER(s)
|
||||
|
||||
#if defined(__cplusplus) && \
|
||||
(__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__))
|
||||
static_assert(sizeof(char16_t) == 2, "Is char16_t type 16 bits?");
|
||||
static_assert(char16_t(-1) > char16_t(0), "Is char16_t type unsigned?");
|
||||
static_assert(sizeof(MOZ_UTF16('A')) == 2, "Is char literal 16 bits?");
|
||||
static_assert(sizeof(MOZ_UTF16("")[0]) == 2, "Is string char 16 bits?");
|
||||
#endif
|
||||
|
||||
#endif /* mozilla_Char16_h */
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* the WeakPtrs to it and allows the WeakReference to live beyond the lifetime
|
||||
* of 'Foo'.
|
||||
*
|
||||
* AtomicSupportsWeakPtr can be used for a variant with an atomically updated
|
||||
* reference counter.
|
||||
*
|
||||
* The overhead of WeakPtr is that accesses to 'Foo' becomes an additional
|
||||
* dereference, and an additional heap allocated pointer sized object shared
|
||||
* between all of the WeakPtrs.
|
||||
|
@ -63,7 +60,6 @@
|
|||
#define mozilla_WeakPtr_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/NullPtr.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
@ -76,8 +72,8 @@ template <typename T, class WeakReference> class SupportsWeakPtrBase;
|
|||
namespace detail {
|
||||
|
||||
// This can live beyond the lifetime of the class derived from SupportsWeakPtrBase.
|
||||
template<class T, RefCountAtomicity Atomicity>
|
||||
class WeakReference : public RefCounted<WeakReference<T, Atomicity>, Atomicity>
|
||||
template<class T>
|
||||
class WeakReference : public ::mozilla::RefCounted<WeakReference<T> >
|
||||
{
|
||||
public:
|
||||
explicit WeakReference(T* p) : ptr(p) {}
|
||||
|
@ -86,8 +82,8 @@ class WeakReference : public RefCounted<WeakReference<T, Atomicity>, Atomicity>
|
|||
}
|
||||
|
||||
private:
|
||||
friend class WeakPtrBase<T, WeakReference>;
|
||||
friend class SupportsWeakPtrBase<T, WeakReference>;
|
||||
friend class WeakPtrBase<T, WeakReference<T> >;
|
||||
friend class SupportsWeakPtrBase<T, WeakReference<T> >;
|
||||
void detach() {
|
||||
ptr = nullptr;
|
||||
}
|
||||
|
@ -121,30 +117,10 @@ class SupportsWeakPtrBase
|
|||
};
|
||||
|
||||
template <typename T>
|
||||
class SupportsWeakPtr
|
||||
: public SupportsWeakPtrBase<T, detail::WeakReference<T, detail::NonAtomicRefCount> >
|
||||
class SupportsWeakPtr : public SupportsWeakPtrBase<T, detail::WeakReference<T> >
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class AtomicSupportsWeakPtr
|
||||
: public SupportsWeakPtrBase<T, detail::WeakReference<T, detail::AtomicRefCount> >
|
||||
{
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
struct WeakReferenceCount
|
||||
{
|
||||
static const RefCountAtomicity atomicity =
|
||||
IsBaseOf<AtomicSupportsWeakPtr<T>, T>::value
|
||||
? AtomicRefCount
|
||||
: NonAtomicRefCount;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename T, class WeakReference>
|
||||
class WeakPtrBase
|
||||
{
|
||||
|
@ -177,9 +153,9 @@ class WeakPtrBase
|
|||
};
|
||||
|
||||
template <typename T>
|
||||
class WeakPtr : public WeakPtrBase<T, detail::WeakReference<T, detail::WeakReferenceCount<T>::atomicity> >
|
||||
class WeakPtr : public WeakPtrBase<T, detail::WeakReference<T> >
|
||||
{
|
||||
typedef WeakPtrBase<T, detail::WeakReference<T, detail::WeakReferenceCount<T>::atomicity> > Base;
|
||||
typedef WeakPtrBase<T, detail::WeakReference<T> > Base;
|
||||
public:
|
||||
WeakPtr(const WeakPtr<T>& o) : Base(o) {}
|
||||
WeakPtr(const Base& o) : Base(o) {}
|
||||
|
|
|
@ -8,12 +8,12 @@ include $(topsrcdir)/config/config.mk
|
|||
# http://code.google.com/p/android/issues/detail?id=3639
|
||||
AB_rCD = $(if $(filter he, $(AB_CD)),iw,$(if $(filter id, $(AB_CD)),in,$(subst -,-r,$(AB_CD))))
|
||||
|
||||
SYNCSTRINGSPATH = $(call core_abspath,$(call MERGE_FILE,sync_strings.dtd))
|
||||
STRINGSPATH = $(call core_abspath,$(call MERGE_FILE,android_strings.dtd))
|
||||
SYNCSTRINGSPATH = $(abspath $(call MERGE_FILE,sync_strings.dtd))
|
||||
STRINGSPATH = $(abspath $(call MERGE_FILE,android_strings.dtd))
|
||||
ifeq (,$(XPI_NAME))
|
||||
BRANDPATH = $(call core_abspath,$(DEPTH)/dist/bin/chrome/$(AB_CD)/locale/branding/brand.dtd)
|
||||
BRANDPATH = $(abspath $(DEPTH)/dist/bin/chrome/$(AB_CD)/locale/branding/brand.dtd)
|
||||
else
|
||||
BRANDPATH = $(call core_abspath,$(DIST)/xpi-stage/$(XPI_NAME)/chrome/$(AB_CD)/locale/branding/brand.dtd)
|
||||
BRANDPATH = $(abspath $(DIST)/xpi-stage/$(XPI_NAME)/chrome/$(AB_CD)/locale/branding/brand.dtd)
|
||||
endif
|
||||
$(warnIfEmpty,AB_CD) # todo: $(errorIfEmpty )
|
||||
|
||||
|
@ -32,7 +32,7 @@ chrome-%::
|
|||
@$(MAKE) $(dir-res-values)-$(AB_rCD)/strings.xml AB_CD=$*
|
||||
|
||||
# setup the path to bookmarks.inc. copied and tweaked version of MERGE_FILE from config/config.mk
|
||||
MOBILE_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/mobile/locales/en-US,$(call core_realpath,$(L10NBASEDIR))/$(AB_CD)/mobile)
|
||||
MOBILE_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/mobile/locales/en-US,$(or $(realpath $(L10NBASEDIR)),$(abspath $(L10NBASEDIR)))/$(AB_CD)/mobile)
|
||||
|
||||
ifdef LOCALE_MERGEDIR
|
||||
BOOKMARKSPATH = $(firstword \
|
||||
|
@ -40,7 +40,7 @@ BOOKMARKSPATH = $(firstword \
|
|||
$(wildcard $(MOBILE_LOCALE_SRCDIR)/profile/bookmarks.inc ) \
|
||||
$(topsrcdir)/mobile/locales/en-US/profile/bookmarks.inc )
|
||||
else
|
||||
BOOKMARKSPATH = $(call core_abspath,$(MOBILE_LOCALE_SRCDIR)/profile/bookmarks.inc)
|
||||
BOOKMARKSPATH = $(abspath $(MOBILE_LOCALE_SRCDIR)/profile/bookmarks.inc)
|
||||
endif
|
||||
|
||||
# Determine the ../res/values[-*]/ path
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# 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/.
|
||||
|
||||
TESTROOT = $(call core_abspath,$(DEPTH))/_tests/xpcshell/$(relativesrcdir)
|
||||
TESTROOT = $(abspath $(DEPTH))/_tests/xpcshell/$(relativesrcdir)
|
||||
|
||||
DEFINES += -DBIN_SUFFIX=$(BIN_SUFFIX)
|
||||
|
||||
|
|
|
@ -25,6 +25,19 @@
|
|||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Force-include Char16.h in order to define PRUnichar as char16_t everywhere.
|
||||
* Note that this should be the first #include to make sure that prtypes.h does
|
||||
* not attempt to define PRUnichar. This includes the following hunspell-specific
|
||||
* includes.
|
||||
*
|
||||
* We don't use this to build elfhack and elf-dynstr-gc since those builds happen
|
||||
* during the export tier. Also, disable this when building assembly files too.
|
||||
*/
|
||||
#if !defined(ELFHACK_BUILD) && !defined(ELFDYNSTRGC_BUILD) && !defined(__ASSEMBLER__)
|
||||
#include "mozilla/Char16.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Force-include hunspell_alloc_hooks.h and hunspell_fopen_hooks.h for hunspell,
|
||||
* so that we don't need to modify them directly.
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "nsINetworkManager.h"
|
||||
#include "nsINetworkStatsServiceProxy.h"
|
||||
#endif
|
||||
|
||||
|
@ -966,6 +967,7 @@ WebSocketChannel::WebSocketChannel() :
|
|||
mCountRecv(0),
|
||||
mCountSent(0),
|
||||
mAppId(0),
|
||||
mConnectionType(NETWORK_NO_TYPE),
|
||||
mIsInBrowser(false)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
|
||||
|
@ -1080,9 +1082,9 @@ WebSocketChannel::BeginOpen()
|
|||
NS_GetAppInfo(localChannel, &mAppId, &mIsInBrowser);
|
||||
}
|
||||
|
||||
// obtain active network
|
||||
// obtain active connection type
|
||||
if (mAppId != NECKO_NO_APP_ID) {
|
||||
GetActiveNetwork();
|
||||
GetConnectionType(&mConnectionType);
|
||||
}
|
||||
|
||||
rv = localChannel->AsyncOpen(this, mHttpChannel);
|
||||
|
@ -3272,7 +3274,7 @@ WebSocketChannel::OnDataAvailable(nsIRequest *aRequest,
|
|||
}
|
||||
|
||||
nsresult
|
||||
WebSocketChannel::GetActiveNetwork()
|
||||
WebSocketChannel::GetConnectionType(int32_t *type)
|
||||
{
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
@ -3281,11 +3283,15 @@ WebSocketChannel::GetActiveNetwork()
|
|||
nsCOMPtr<nsINetworkManager> networkManager = do_GetService("@mozilla.org/network/manager;1", &result);
|
||||
|
||||
if (NS_FAILED(result) || !networkManager) {
|
||||
mActiveNetwork = nullptr;
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
*type = NETWORK_NO_TYPE;
|
||||
}
|
||||
|
||||
result = networkManager->GetActive(getter_AddRefs(mActiveNetwork));
|
||||
nsCOMPtr<nsINetworkInterface> networkInterface;
|
||||
result = networkManager->GetActive(getter_AddRefs(networkInterface));
|
||||
|
||||
if (networkInterface) {
|
||||
result = networkInterface->GetType(type);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
#else
|
||||
|
@ -3297,8 +3303,9 @@ nsresult
|
|||
WebSocketChannel::SaveNetworkStats(bool enforce)
|
||||
{
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Check if the active network and app id are valid.
|
||||
if(!mActiveNetwork || mAppId == NECKO_NO_APP_ID) {
|
||||
// Check if the connection type and app id are valid.
|
||||
if(mConnectionType == NETWORK_NO_TYPE ||
|
||||
mAppId == NECKO_NO_APP_ID) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -3322,7 +3329,7 @@ WebSocketChannel::SaveNetworkStats(bool enforce)
|
|||
return rv;
|
||||
}
|
||||
|
||||
mNetworkStatsServiceProxy->SaveAppStats(mAppId, mActiveNetwork, PR_Now() / 1000,
|
||||
mNetworkStatsServiceProxy->SaveAppStats(mAppId, mConnectionType, PR_Now() / 1000,
|
||||
mCountRecv, mCountSent, nullptr);
|
||||
|
||||
// Reset the counters after saving.
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
#include "nsIHttpChannelInternal.h"
|
||||
#include "BaseWebSocketChannel.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "nsINetworkManager.h"
|
||||
#endif
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsDeque.h"
|
||||
|
@ -258,17 +254,16 @@ private:
|
|||
// These members are used for network per-app metering (bug 855949)
|
||||
// Currently, they are only available on gonk.
|
||||
public:
|
||||
const static int32_t NETWORK_NO_TYPE = -1; // default conntection type
|
||||
const static uint64_t NETWORK_STATS_THRESHOLD = 65536;
|
||||
|
||||
private:
|
||||
uint64_t mCountRecv;
|
||||
uint64_t mCountSent;
|
||||
uint32_t mAppId;
|
||||
int32_t mConnectionType;
|
||||
bool mIsInBrowser;
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsCOMPtr<nsINetworkInterface> mActiveNetwork;
|
||||
#endif
|
||||
nsresult GetActiveNetwork();
|
||||
nsresult GetConnectionType(int32_t *);
|
||||
nsresult SaveNetworkStats(bool);
|
||||
void CountRecvBytes(uint64_t recvBytes)
|
||||
{
|
||||
|
|
|
@ -87,7 +87,7 @@ NSS_EXTRA_DLLS += freebl_64int_3
|
|||
NSS_EXTRA_DLLS += freebl_64fpu_3
|
||||
endif
|
||||
|
||||
ABS_DIST := $(call core_abspath,$(DIST))
|
||||
ABS_DIST := $(abspath $(DIST))
|
||||
ifeq ($(HOST_OS_ARCH),WINNT)
|
||||
ifdef CYGDRIVE_MOUNT
|
||||
ABS_DIST := $(shell cygpath -w $(ABS_DIST) | sed -e 's|\\|/|g')
|
||||
|
@ -288,7 +288,7 @@ export::
|
|||
cp -Rp $(topsrcdir)/security/coreconf $(NSS_SRCDIR)/security
|
||||
cp -Rp $(topsrcdir)/security/dbm $(NSS_SRCDIR)/security
|
||||
cp -Rp $(topsrcdir)/dbm $(NSS_SRCDIR)
|
||||
(cd $(NSS_SRCDIR) && patch -p1 < $(call core_abspath,$(MOZ_NSS_PATCH)))
|
||||
(cd $(NSS_SRCDIR) && patch -p1 < $(abspath $(MOZ_NSS_PATCH)))
|
||||
else
|
||||
NSS_SRCDIR = $(topsrcdir)
|
||||
endif
|
||||
|
@ -450,7 +450,7 @@ endif # MOZ_FOLD_LIBS
|
|||
|
||||
ifeq ($(NSINSTALL_PY),$(NSINSTALL))
|
||||
DEFAULT_GMAKE_FLAGS += PYTHON='$(PYTHON)'
|
||||
DEFAULT_GMAKE_FLAGS += NSINSTALL_PY='$(call core_abspath,$(topsrcdir)/config/nsinstall.py)'
|
||||
DEFAULT_GMAKE_FLAGS += NSINSTALL_PY='$(abspath $(topsrcdir)/config/nsinstall.py)'
|
||||
DEFAULT_GMAKE_FLAGS += NSINSTALL='$$(PYTHON) $$(NSINSTALL_PY)'
|
||||
else
|
||||
DEFAULT_GMAKE_FLAGS += NSINSTALL='$(NSINSTALL)'
|
||||
|
|
|
@ -73,12 +73,6 @@ getSiteKey(const nsACString & hostName, uint16_t port,
|
|||
key.AppendInt(port);
|
||||
}
|
||||
|
||||
void
|
||||
getSiteKey(const nsNSSSocketInfo & socketInfo, /*out*/ nsCSubstring & key)
|
||||
{
|
||||
getSiteKey(socketInfo.GetHostName(), socketInfo.GetPort(), key);
|
||||
}
|
||||
|
||||
/* SSM_UserCertChoice: enum for cert choice info */
|
||||
typedef enum {ASK, AUTO} SSM_UserCertChoice;
|
||||
|
||||
|
@ -1205,13 +1199,13 @@ nsSSLIOLayerPoll(PRFileDesc * fd, int16_t in_flags, int16_t *out_flags)
|
|||
}
|
||||
|
||||
nsSSLIOLayerHelpers::nsSSLIOLayerHelpers()
|
||||
: mutex("nsSSLIOLayerHelpers.mutex")
|
||||
, mRenegoUnrestrictedSites(nullptr)
|
||||
: mRenegoUnrestrictedSites(nullptr)
|
||||
, mTreatUnsafeNegotiationAsBroken(false)
|
||||
, mWarnLevelMissingRFC5746(1)
|
||||
, mTLSIntoleranceInfo(16)
|
||||
, mFalseStartRequireNPN(true)
|
||||
, mFalseStartRequireForwardSecrecy(false)
|
||||
, mutex("nsSSLIOLayerHelpers.mutex")
|
||||
{
|
||||
}
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче