Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2013-10-17 13:56:42 +02:00
Родитель d07136e97e 7e043134db
Коммит 0f7453cae3
122 изменённых файлов: 2217 добавлений и 1838 удалений

Просмотреть файл

@ -18,4 +18,4 @@
# Modifying this file will now automatically clobber the buildbot machines \o/ # 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)) ifeq (,$(MAKE_VERSION))
$(error GNU Make is required) $(error GNU Make is required)
endif endif
ifeq (,$(filter-out 3.78 3.79,$(MAKE_VERSION))) make_min_ver := 3.81
$(error GNU Make 3.80 or higher is required) ifneq ($(make_min_ver),$(firstword $(sort $(make_min_ver) $(MAKE_VERSION))))
$(error GNU Make $(make_min_ver) or higher is required)
endif endif
endif endif

Просмотреть файл

@ -466,12 +466,11 @@ sdnAccessible::get_innerHTML(BSTR __RPC_FAR* aInnerHTML)
if (IsDefunct()) if (IsDefunct())
return CO_E_OBJNOTCONNECTED; return CO_E_OBJNOTCONNECTED;
nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(mNode); if (!mNode->IsElement())
if (!htmlElement)
return S_FALSE; return S_FALSE;
nsAutoString innerHTML; nsAutoString innerHTML;
htmlElement->GetInnerHTML(innerHTML); mNode->AsElement()->GetInnerHTML(innerHTML);
if (innerHTML.IsEmpty()) if (innerHTML.IsEmpty())
return S_FALSE; return S_FALSE;

Просмотреть файл

@ -1,4 +1,4 @@
{ {
"revision": "86e06b1db110e34eb66826d3b1bdee3a5d57b3a7", "revision": "563d1aa93586165246ab2ab9d40566a598f56387",
"repo_path": "/integration/gaia-central" "repo_path": "/integration/gaia-central"
} }

Просмотреть файл

@ -9,13 +9,18 @@ DEFINES += \
-DB2G_NAME=L\"$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)\" \ -DB2G_NAME=L\"$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)\" \
-DGAIA_PATH=L\"$(subst /,\\\\,$(GAIA_PATH))\" \ -DGAIA_PATH=L\"$(subst /,\\\\,$(GAIA_PATH))\" \
$(NULL) $(NULL)
GAIA_MAKE=make
else # Non-windows machines use the same wrapper program else # Non-windows machines use the same wrapper program
CSRCS = run-b2g.c CSRCS = run-b2g.c
DEFINES += \ DEFINES += \
-DB2G_NAME=\"$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)\" \ -DB2G_NAME=\"$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)\" \
-DGAIA_PATH=\"$(GAIA_PATH)\" \ -DGAIA_PATH=\"$(GAIA_PATH)\" \
$(NULL) $(NULL)
endif
ifdef .PYMAKE
# For use of GNU make in pymake builds.
GAIA_MAKE=$(GMAKE)
else
GAIA_MAKE=$(MAKE) GAIA_MAKE=$(MAKE)
endif endif
@ -27,6 +32,6 @@ GENERATED_DIRS += $(DIST)/bin/$(GAIA_PATH)
include $(topsrcdir)/config/rules.mk include $(topsrcdir)/config/rules.mk
libs:: libs::
$(GAIA_MAKE) -j1 -C $(GAIADIR) clean +$(GAIA_MAKE) -j1 -C $(GAIADIR) clean
$(GAIA_MAKE) -j1 -C $(GAIADIR) profile +$(GAIA_MAKE) -j1 -C $(GAIADIR) profile
(cd $(GAIADIR)/profile && tar $(TAR_CREATE_FLAGS) - .) | (cd $(abspath $(DIST))/bin/$(GAIA_PATH) && tar -xf -) (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 # 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/. # 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 include $(topsrcdir)/config/config.mk
@ -34,7 +34,7 @@ include $(topsrcdir)/config/rules.mk
$(all_xpis): $(DISTROEXT)/%.xpi: $(call mkdir_deps,$(DISTROEXT)) libs-% $(all_xpis): $(DISTROEXT)/%.xpi: $(call mkdir_deps,$(DISTROEXT)) libs-%
cd $* && \ cd $* && \
$(ZIP) -r9XD $@ * -x \*.in -x \*.mkdir.done $(ZIP) -r9XD $@ * -x \*.in -x \*.mkdir.done
cd $(call core_abspath,$(srcdir)/$*) && \ cd $(abspath $(srcdir)/$*) && \
$(ZIP) -r9XD $@ * -x \*.in -x \*.mkdir.done $(ZIP) -r9XD $@ * -x \*.in -x \*.mkdir.done
.PHONY: $(all_xpis:.xpi=) .PHONY: $(all_xpis:.xpi=)

Просмотреть файл

@ -5,7 +5,7 @@
include $(topsrcdir)/config/config.mk include $(topsrcdir)/config/config.mk
abs_srcdir = $(call core_abspath,$(srcdir)) abs_srcdir = $(abspath $(srcdir))
CHROME_DEPS += $(abs_srcdir)/content/overrides/app-license.html 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 wrongBrowserNotificationObject = new basicNotification();
var wrongBrowserNotification; 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 () { run: function () {
this.notifyObj1 = new basicNotification(); this.notifyObj1 = new basicNotification();
this.notifyObj1.id += "_1"; this.notifyObj1.id += "_1";
@ -862,7 +972,7 @@ var tests = [
this.notification2.remove(); 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() { run: function() {
this.notifyObj = new basicNotification(); this.notifyObj = new basicNotification();
var normalCallback = this.notifyObj.options.eventCallback; var normalCallback = this.notifyObj.options.eventCallback;

Просмотреть файл

@ -8,7 +8,7 @@ browser_path := \"$(browser_path)\"
_PROFILE_DIR = $(TARGET_DEPTH)/_profile/pgo _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 _CERTS_SRC_DIR = $(ABSOLUTE_TOPSRCDIR)/build/pgo/certs
AUTOMATION_PPARGS = \ AUTOMATION_PPARGS = \

Просмотреть файл

@ -76,8 +76,9 @@ def InvokeClWithDependencyGeneration(cmdline):
# We can't handle pathes with spaces properly in mddepend.pl, but # 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 # we can assume that anything in a path with spaces is a system
# header and throw it away. # header and throw it away.
dep = normcase(dep)
if ' ' not in dep: if ' ' not in dep:
rule.add_dependencies([normcase(dep)]) rule.add_dependencies([dep])
else: else:
# Make sure we preserve the relevant output from cl. mozprocess # Make sure we preserve the relevant output from cl. mozprocess
# swallows the newline delimiter, so we need to re-add it. # swallows the newline delimiter, so we need to re-add it.

Просмотреть файл

@ -19,14 +19,12 @@ topsrcdir = $(TOPSRCDIR)
DEPTH = $(OBJDIR) DEPTH = $(OBJDIR)
include $(OBJDIR)/config/autoconf.mk include $(OBJDIR)/config/autoconf.mk
core_abspath = $(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1))
DIST = $(OBJDIR)/dist DIST = $(OBJDIR)/dist
postflight_all: postflight_all:
mkdir -p $(DIST_UNI)/$(MOZ_PKG_APPNAME) mkdir -p $(DIST_UNI)/$(MOZ_PKG_APPNAME)
rm -f $(DIST_ARCH_2)/universal 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 # 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. # actually does a universal staging with both OBJDIR_ARCH_1 and OBJDIR_ARCH_2.
$(MAKE) -C $(OBJDIR_ARCH_1)/$(MOZ_BUILD_APP)/installer \ $(MAKE) -C $(OBJDIR_ARCH_1)/$(MOZ_BUILD_APP)/installer \

Просмотреть файл

@ -101,7 +101,6 @@ public class RedirOutputThread extends Thread
} }
catch (IOException e) catch (IOException e)
{ {
// Toast.makeText(SUTAgentAndroid.me.getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
e.printStackTrace(); e.printStackTrace();
} }
} }

Просмотреть файл

@ -43,13 +43,11 @@ import android.os.PowerManager;
import android.os.RemoteException; import android.os.RemoteException;
import android.provider.Settings; import android.provider.Settings;
import android.util.Log; import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;
import android.os.Environment; import android.os.Environment;
public class WatcherService extends Service 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"; private final String LOGTAG = "Watcher";
String sErrorPrefix = "##Installer Error## "; String sErrorPrefix = "##Installer Error## ";
@ -135,16 +133,16 @@ public class WatcherService extends Service
try { try {
if (nStayOn != 0) { if (nStayOn != 0) {
if (!Settings.System.putInt(getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB)) { 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) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
String sExcept = e.getMessage(); 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) public String GetIniData(String sSection, String sKey, String sFile, String sDefault)
@ -228,7 +226,7 @@ public class WatcherService extends Service
{ {
if (!this.bStartedTimer) if (!this.bStartedTimer)
{ {
doToast("WatcherService started"); Log.i(LOGTAG, "WatcherService started");
myTimer = new Timer(); myTimer = new Timer();
Date startSchedule = new Date(System.currentTimeMillis() + lDelay); Date startSchedule = new Date(System.currentTimeMillis() + lDelay);
myTimer.schedule(new MyTime(), startSchedule, lPeriod); myTimer.schedule(new MyTime(), startSchedule, lPeriod);
@ -237,11 +235,11 @@ public class WatcherService extends Service
} }
else else
{ {
doToast("WatcherService unknown command"); Log.w(LOGTAG, "WatcherService unknown command");
} }
} }
else else
doToast("WatcherService created"); Log.w(LOGTAG, "WatcherService intent had null command");
} }
public void writeVersion() { public void writeVersion() {
@ -280,7 +278,7 @@ public class WatcherService extends Service
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
doToast("WatcherService destroyed"); Log.i(LOGTAG, "WatcherService destroyed");
if (pwl != null) if (pwl != null)
pwl.release(); pwl.release();
stopForegroundCompat(R.string.foreground_service_started); 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() public void CheckMem()
{ {
System.gc(); System.gc();

Просмотреть файл

@ -16,6 +16,8 @@ WRAP_LDFLAGS=
include $(topsrcdir)/config/rules.mk 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))) test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX): %$(DLL_SUFFIX): %.$(OBJ_SUFFIX) elfhack $(filter inject/%,$(CSRCS:.c=.$(OBJ_SUFFIX)))
$(MKSHLIB) $(LDFLAGS) $< -nostartfiles $(MKSHLIB) $(LDFLAGS) $< -nostartfiles
@echo === @echo ===

Просмотреть файл

@ -30,5 +30,7 @@ $(CSRCS): %.c: ../inject.c
GARBAGE += $(CSRCS) GARBAGE += $(CSRCS)
DEFINES += -DELFHACK_BUILD
CFLAGS := -O2 -fno-stack-protector $(filter -m% -I%,$(CFLAGS)) CFLAGS := -O2 -fno-stack-protector $(filter -m% -I%,$(CFLAGS))
$(CPU)-noinit.$(OBJ_SUFFIX): DEFINES += -DNOINIT $(CPU)-noinit.$(OBJ_SUFFIX): DEFINES += -DNOINIT

Просмотреть файл

@ -128,7 +128,7 @@ GARBAGE += \
ifndef CROSS_COMPILE ifndef CROSS_COMPILE
ifdef USE_ELF_DYNSTR_GC ifdef USE_ELF_DYNSTR_GC
elf-dynstr-gc: elf-dynstr-gc.c $(GLOBAL_DEPS) $(call mkdir_deps,$(MDDEPDIR)) 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
endif endif

Просмотреть файл

@ -13,7 +13,7 @@ ifndef JAVA_CLASSPATH
endif endif
# DEBUG_JARSIGNER always debug signs. # 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) \ --keytool=$(KEYTOOL) \
--jarsigner=$(JARSIGNER) \ --jarsigner=$(JARSIGNER) \
$(NULL) $(NULL)

Просмотреть файл

@ -632,7 +632,7 @@ endif
PWD := $(CURDIR) PWD := $(CURDIR)
endif 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 # 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 # 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. # of single-line commands, we continue to provide INSTALL for general use.
@ -691,7 +691,7 @@ else
IS_LANGUAGE_REPACK = 1 IS_LANGUAGE_REPACK = 1
endif 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 ifdef relativesrcdir
LOCALE_SRCDIR ?= $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir)) LOCALE_SRCDIR ?= $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir))
@ -742,7 +742,7 @@ ifdef MOZ_DEBUG
JAVAC_FLAGS += -g JAVAC_FLAGS += -g
endif 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 is the subdirectory where dependency files are stored
MDDEPDIR := .deps MDDEPDIR := .deps

Просмотреть файл

@ -16,7 +16,7 @@ $(error Do not include functions.mk twice!)
endif endif
INCLUDED_FUNCTIONS_MK = 1 INCLUDED_FUNCTIONS_MK = 1
core_abspath = $(if $(findstring :,$(1)),$(1),$(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1))) core_abspath = $(error core_abspath is unsupported, use $$(abspath) instead)
core_realpath = $(if $(realpath $(1)),$(realpath $(1)),$(call core_abspath,$(1))) 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 endif
# Copy NSPR to the SDK # Copy NSPR to the SDK
ABS_DIST = $(call core_abspath,$(DIST)) ABS_DIST = $(abspath $(DIST))
ifdef MOZ_FOLD_LIBS ifdef MOZ_FOLD_LIBS
# Trick the nspr build system into not building shared libraries. # 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 844404.html
load 847127.html load 847127.html
load 849601.html load 849601.html
skip-if(Android) load 851353-1.html
load 863950.html load 863950.html
load 864448.html load 864448.html

Просмотреть файл

@ -1173,7 +1173,9 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
nsIDocument::UnlockPointer(); nsIDocument::UnlockPointer();
} }
if (GetParent()) { if (GetParent()) {
NS_RELEASE(mParent); nsINode* p = mParent;
mParent = nullptr;
NS_RELEASE(p);
} else { } else {
mParent = nullptr; mParent = nullptr;
} }

Просмотреть файл

@ -506,6 +506,7 @@ support-files =
[test_bug814576.html] [test_bug814576.html]
[test_bug819051.html] [test_bug819051.html]
[test_bug820909.html] [test_bug820909.html]
[test_bug840098.html]
[test_bug868999.html] [test_bug868999.html]
[test_bug869000.html] [test_bug869000.html]
[test_bug869002.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 skip-if(Android||B2G) load 789933-1.html # bug 833371
load 794463-1.html load 794463-1.html
load 802926-1.html load 802926-1.html
load 916128-1.html

Просмотреть файл

@ -14,8 +14,8 @@
#include "mozilla/RefPtr.h" #include "mozilla/RefPtr.h"
#define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \ #define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
{ 0x8b8da863, 0xd151, 0x4014, \ { 0x9a6a5bdf, 0x1261, 0x4057, \
{ 0x8b, 0xdc, 0x62, 0xb5, 0x0d, 0xc0, 0x2b, 0x62 } } { 0x85, 0xcc, 0xaf, 0x97, 0x6c, 0x36, 0x99, 0xa9 } }
class gfxContext; class gfxContext;
class gfxASurface; class gfxASurface;
@ -71,6 +71,9 @@ public:
GraphicsFilter aFilter, GraphicsFilter aFilter,
uint32_t aFlags = RenderFlagPremultAlpha) = 0; 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. // Gives you a stream containing the image represented by this context.
// The format is given in aMimeTime, for example "image/png". // The format is given in aMimeTime, for example "image/png".
// //

Просмотреть файл

@ -41,7 +41,7 @@
#include "nsTArray.h" #include "nsTArray.h"
#include "imgIEncoder.h" #include "ImageEncoder.h"
#include "gfxContext.h" #include "gfxContext.h"
#include "gfxASurface.h" #include "gfxASurface.h"
@ -928,16 +928,18 @@ CanvasRenderingContext2D::SetDimensions(int32_t width, int32_t height)
{ {
ClearTarget(); ClearTarget();
// Zero sized surfaces cause issues, so just go with 1x1. // Zero sized surfaces can cause problems.
if (height == 0 || width == 0) {
mZero = true;
mWidth = 1;
mHeight = 1;
} else {
mZero = false; mZero = false;
if (height == 0) {
height = 1;
mZero = true;
}
if (width == 0) {
width = 1;
mZero = true;
}
mWidth = width; mWidth = width;
mHeight = height; mHeight = height;
}
return NS_OK; return NS_OK;
} }
@ -1050,71 +1052,71 @@ CanvasRenderingContext2D::Render(gfxContext *ctx, GraphicsFilter aFilter, uint32
return rv; return rv;
} }
NS_IMETHODIMP void
CanvasRenderingContext2D::GetInputStream(const char *aMimeType, CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer,
const PRUnichar *aEncoderOptions, int32_t* aFormat)
nsIInputStream **aStream)
{ {
EnsureTarget(); *aImageBuffer = nullptr;
if (!IsTargetValid()) { *aFormat = 0;
return NS_ERROR_FAILURE;
}
nsRefPtr<gfxASurface> surface; nsRefPtr<gfxASurface> surface;
nsresult rv = GetThebesSurface(getter_AddRefs(surface));
if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) { if (NS_FAILED(rv)) {
return NS_ERROR_FAILURE; return;
} }
nsresult rv;
const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
static const fallible_t fallible = fallible_t(); static const fallible_t fallible = fallible_t();
nsAutoArrayPtr<char> conid(new (fallible) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]); uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
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]);
if (!imageBuffer) { if (!imageBuffer) {
return NS_ERROR_OUT_OF_MEMORY; return;
} }
nsRefPtr<gfxImageSurface> imgsurf = nsRefPtr<gfxImageSurface> imgsurf =
new gfxImageSurface(imageBuffer.get(), new gfxImageSurface(imageBuffer,
gfxIntSize(mWidth, mHeight), gfxIntSize(mWidth, mHeight),
mWidth * 4, mWidth * 4,
gfxImageFormatARGB32); gfxImageFormatARGB32);
if (!imgsurf || imgsurf->CairoStatus()) { if (!imgsurf || imgsurf->CairoStatus()) {
return NS_ERROR_FAILURE; delete[] imageBuffer;
return;
} }
nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf); nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
if (!ctx || ctx->HasError()) { if (!ctx || ctx->HasError()) {
return NS_ERROR_FAILURE; delete[] imageBuffer;
return;
} }
ctx->SetOperator(gfxContext::OPERATOR_SOURCE); ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
ctx->SetSource(surface, gfxPoint(0, 0)); ctx->SetSource(surface, gfxPoint(0, 0));
ctx->Paint(); ctx->Paint();
rv = encoder->InitFromData(imageBuffer.get(), *aImageBuffer = imageBuffer;
mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4, *aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
imgIEncoder::INPUT_FORMAT_HOSTARGB, }
nsDependentString(aEncoderOptions));
NS_ENSURE_SUCCESS(rv, rv);
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 SurfaceFormat
@ -3815,6 +3817,9 @@ NS_IMETHODIMP
CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface) CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
{ {
EnsureTarget(); EnsureTarget();
if (!IsTargetValid()) {
return NS_ERROR_FAILURE;
}
nsRefPtr<gfxASurface> thebesSurface = nsRefPtr<gfxASurface> thebesSurface =
gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget); gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);

Просмотреть файл

@ -22,6 +22,7 @@
#include "mozilla/gfx/Rect.h" #include "mozilla/gfx/Rect.h"
#include "mozilla/gfx/2D.h" #include "mozilla/gfx/2D.h"
#include "gfx2DGlue.h" #include "gfx2DGlue.h"
#include "imgIEncoder.h"
class nsXULElement; class nsXULElement;
@ -460,6 +461,8 @@ public:
friend class CanvasRenderingContext2DUserData; friend class CanvasRenderingContext2DUserData;
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
protected: protected:
nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY, nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
uint32_t aWidth, uint32_t aHeight, 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)/../../html/content/src \
-I$(srcdir)/../../../js/xpconnect/src \ -I$(srcdir)/../../../js/xpconnect/src \
-I$(srcdir)/../../../dom/base \ -I$(srcdir)/../../../dom/base \
-I$(srcdir)/../../../image/src \
-I$(topsrcdir)/content/xul/content/src \ -I$(topsrcdir)/content/xul/content/src \
$(NULL) $(NULL)

Просмотреть файл

@ -27,7 +27,7 @@
#include "nsIVariant.h" #include "nsIVariant.h"
#include "imgIEncoder.h" #include "ImageEncoder.h"
#include "gfxContext.h" #include "gfxContext.h"
#include "gfxPattern.h" #include "gfxPattern.h"
@ -381,8 +381,10 @@ WebGLContext::SetDimensions(int32_t width, int32_t height)
return NS_OK; return NS_OK;
// Zero-sized surfaces can cause problems. // Zero-sized surfaces can cause problems.
if (width == 0 || height == 0) { if (width == 0) {
width = 1; width = 1;
}
if (height == 0) {
height = 1; 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 NS_IMETHODIMP
WebGLContext::GetInputStream(const char* aMimeType, WebGLContext::GetInputStream(const char* aMimeType,
const PRUnichar* aEncoderOptions, const PRUnichar* aEncoderOptions,
@ -742,48 +792,22 @@ WebGLContext::GetInputStream(const char* aMimeType,
if (!gl) if (!gl)
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight), uint8_t* imageBuffer = nullptr;
gfxImageFormatARGB32); int32_t format = 0;
if (surf->CairoStatus() != 0) GetImageBuffer(&imageBuffer, &format);
if (!imageBuffer) {
return NS_ERROR_FAILURE; 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(), nsCString enccid("@mozilla.org/image/encoder;2?type=");
mWidth * mHeight * 4, enccid += aMimeType;
mWidth, mHeight, nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
surf->Stride(), if (!encoder) {
format, return NS_ERROR_FAILURE;
nsDependentString(aEncoderOptions)); }
NS_ENSURE_SUCCESS(rv, rv);
return CallQueryInterface(encoder, aStream); return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
encoder, aEncoderOptions, aStream);
} }
NS_IMETHODIMP NS_IMETHODIMP

Просмотреть файл

@ -167,6 +167,7 @@ public:
NS_IMETHOD Render(gfxContext *ctx, NS_IMETHOD Render(gfxContext *ctx,
GraphicsFilter f, GraphicsFilter f,
uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE; uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
NS_IMETHOD GetInputStream(const char* aMimeType, NS_IMETHOD GetInputStream(const char* aMimeType,
const PRUnichar* aEncoderOptions, const PRUnichar* aEncoderOptions,
nsIInputStream **aStream) MOZ_OVERRIDE; nsIInputStream **aStream) MOZ_OVERRIDE;

Просмотреть файл

@ -22,6 +22,7 @@ CPP_SOURCES += [
'DocumentRendererChild.cpp', 'DocumentRendererChild.cpp',
'DocumentRendererParent.cpp', 'DocumentRendererParent.cpp',
'ImageData.cpp', 'ImageData.cpp',
'ImageEncoder.cpp',
] ]
if CONFIG['MOZ_WEBGL']: if CONFIG['MOZ_WEBGL']:

Просмотреть файл

@ -7,42 +7,43 @@
<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas> <canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<script> <script>
var gCompares = 0; function compareAsync(file, canvas, type, callback)
function compareAsync(file, canvas, type)
{ {
++gCompares;
var reader = new FileReader(); var reader = new FileReader();
reader.onload = reader.onload =
function(e) { function(e) {
is(e.target.result, canvas.toDataURL(type), is(e.target.result, canvas.toDataURL(type),
"<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()"); "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
if (--gCompares == 0) { callback(canvas);
SimpleTest.finish();
}
}; };
reader.readAsDataURL(file); 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(); SimpleTest.waitForExplicitFinish();
addLoadEvent(function () { addLoadEvent(function () {
var canvas = document.getElementById('c'); var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d'); var ctx = canvas.getContext('2d');
ctx.drawImage(document.getElementById('yellow75.png'), 0, 0); ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
var pngfile = canvas.mozGetAsFile("foo.png"); test1(canvas);
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");
}); });
</script> </script>

Просмотреть файл

@ -1,5 +1,5 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<title>Canvas test: mozGetAsFile</title> <title>Canvas test: toBlob</title>
<script src="/MochiKit/MochiKit.js"></script> <script src="/MochiKit/MochiKit.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script> <script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"> <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> <canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<script> <script>
var gCompares = 2; function BlobListener(type, canvas, callback, file)
function BlobListener(type, canvas, file)
{ {
is(file.type, type, is(file.type, type,
"When a valid type is specified that should be returned"); "When a valid type is specified that should be returned");
@ -18,23 +16,31 @@ function BlobListener(type, canvas, file)
function(e) { function(e) {
is(e.target.result, canvas.toDataURL(type), is(e.target.result, canvas.toDataURL(type),
"<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()"); "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
if (--gCompares == 0) { callback(canvas);
SimpleTest.finish();
}
}; };
reader.readAsDataURL(file); 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(); SimpleTest.waitForExplicitFinish();
addLoadEvent(function () { addLoadEvent(function () {
var canvas = document.getElementById('c'); var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d'); var ctx = canvas.getContext('2d');
ctx.drawImage(document.getElementById('yellow75.png'), 0, 0); ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas)); test1(canvas);
canvas.toBlob(BlobListener.bind(undefined, "image/jpeg", canvas), "image/jpeg");
}); });
</script> </script>

Просмотреть файл

@ -227,10 +227,9 @@ protected:
const JS::Value& aEncoderOptions, const JS::Value& aEncoderOptions,
nsAString& aParams, nsAString& aParams,
bool* usingCustomParseOptions); bool* usingCustomParseOptions);
nsresult ExtractData(const nsAString& aType, nsresult ExtractData(nsAString& aType,
const nsAString& aOptions, const nsAString& aOptions,
nsIInputStream** aStream, nsIInputStream** aStream);
bool& aFellBackToPNG);
nsresult ToDataURLImpl(JSContext* aCx, nsresult ToDataURLImpl(JSContext* aCx,
const nsAString& aMimeType, const nsAString& aMimeType,
const JS::Value& aEncoderOptions, const JS::Value& aEncoderOptions,

Просмотреть файл

@ -5,11 +5,11 @@
#include "mozilla/dom/HTMLCanvasElement.h" #include "mozilla/dom/HTMLCanvasElement.h"
#include "Layers.h" #include "ImageEncoder.h"
#include "imgIEncoder.h"
#include "jsapi.h" #include "jsapi.h"
#include "jsfriendapi.h" #include "jsfriendapi.h"
#include "gfxImageSurface.h" #include "gfxImageSurface.h"
#include "Layers.h"
#include "mozilla/Base64.h" #include "mozilla/Base64.h"
#include "mozilla/CheckedInt.h" #include "mozilla/CheckedInt.h"
#include "mozilla/dom/CanvasRenderingContext2D.h" #include "mozilla/dom/CanvasRenderingContext2D.h"
@ -23,6 +23,7 @@
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsDisplayList.h" #include "nsDisplayList.h"
#include "nsDOMFile.h" #include "nsDOMFile.h"
#include "nsDOMJSUtils.h"
#include "nsFrameManager.h" #include "nsFrameManager.h"
#include "nsIScriptSecurityManager.h" #include "nsIScriptSecurityManager.h"
#include "nsITimer.h" #include "nsITimer.h"
@ -46,29 +47,6 @@ namespace {
typedef mozilla::dom::HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement typedef mozilla::dom::HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement
HTMLImageOrCanvasOrVideoElement; 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 } // anonymous namespace
namespace mozilla { namespace mozilla {
@ -369,10 +347,10 @@ HTMLCanvasElement::MozFetchAsStream(nsIInputStreamCallback *aCallback,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
nsresult rv; nsresult rv;
bool fellBackToPNG = false;
nsCOMPtr<nsIInputStream> inputData; 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); NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAsyncInputStream> asyncData = do_QueryInterface(inputData, &rv); nsCOMPtr<nsIAsyncInputStream> asyncData = do_QueryInterface(inputData, &rv);
@ -404,68 +382,15 @@ HTMLCanvasElement::GetMozPrintCallback() const
} }
nsresult nsresult
HTMLCanvasElement::ExtractData(const nsAString& aType, HTMLCanvasElement::ExtractData(nsAString& aType,
const nsAString& aOptions, const nsAString& aOptions,
nsIInputStream** aStream, nsIInputStream** aStream)
bool& aFellBackToPNG)
{ {
// note that if we don't have a current context, the spec says we're return ImageEncoder::ExtractData(aType,
// supposed to just return transparent black pixels of the canvas aOptions,
// dimensions. GetSize(),
nsRefPtr<gfxImageSurface> emptyCanvas; mCurrentContext,
nsIntSize size = GetWidthHeight(); aStream);
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;
} }
nsresult nsresult
@ -516,8 +441,6 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
const JS::Value& aEncoderOptions, const JS::Value& aEncoderOptions,
nsAString& aDataURL) nsAString& aDataURL)
{ {
bool fallbackToPNG = false;
nsIntSize size = GetWidthHeight(); nsIntSize size = GetWidthHeight();
if (size.height == 0 || size.width == 0) { if (size.height == 0 || size.width == 0) {
aDataURL = NS_LITERAL_STRING("data:,"); aDataURL = NS_LITERAL_STRING("data:,");
@ -538,23 +461,18 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
} }
nsCOMPtr<nsIInputStream> stream; 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 // If there are unrecognized custom parse options, we should fall back to
// the default values for the encoder without any options at all. // the default values for the encoder without any options at all.
if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) { if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
fallbackToPNG = false; rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
} }
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// build data URL string // build data URL string
if (fallbackToPNG) aDataURL = NS_LITERAL_STRING("data:") + type + NS_LITERAL_STRING(";base64,");
aDataURL = NS_LITERAL_STRING("data:image/png;base64,");
else
aDataURL = NS_LITERAL_STRING("data:") + type +
NS_LITERAL_STRING(";base64,");
uint64_t count; uint64_t count;
rv = stream->Available(&count); rv = stream->Available(&count);
@ -564,7 +482,6 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
return Base64EncodeInputStream(stream, aDataURL, (uint32_t)count, aDataURL.Length()); return Base64EncodeInputStream(stream, aDataURL, (uint32_t)count, aDataURL.Length());
} }
// XXXkhuey the encoding should be off the main thread, but we're lazy.
void void
HTMLCanvasElement::ToBlob(JSContext* aCx, HTMLCanvasElement::ToBlob(JSContext* aCx,
FileCallback& aCallback, FileCallback& aCallback,
@ -597,58 +514,35 @@ HTMLCanvasElement::ToBlob(JSContext* aCx,
#ifdef DEBUG #ifdef DEBUG
if (mCurrentContext) { 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(); nsIntSize elementSize = GetWidthHeight();
MOZ_ASSERT(elementSize.width == mCurrentContext->GetWidth()); MOZ_ASSERT(elementSize.width == mCurrentContext->GetWidth() ||
MOZ_ASSERT(elementSize.height == mCurrentContext->GetHeight()); (elementSize.width == 0 && mCurrentContext->GetWidth() == 1));
MOZ_ASSERT(elementSize.height == mCurrentContext->GetHeight() ||
(elementSize.height == 0 && mCurrentContext->GetHeight() == 1));
} }
#endif #endif
bool fallbackToPNG = false; nsCOMPtr<nsIScriptContext> scriptContext =
GetScriptContextFromJSContext(nsContentUtils::GetCurrentJSContext());
nsCOMPtr<nsIInputStream> stream; uint8_t* imageBuffer = nullptr;
aRv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG); int32_t format = 0;
// If there are unrecognized custom parse options, we should fall back to if (mCurrentContext) {
// the default values for the encoder without any options at all. mCurrentContext->GetImageBuffer(&imageBuffer, &format);
if (aRv.ErrorCode() == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
fallbackToPNG = false;
aRv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
} }
if (aRv.Failed()) { aRv = ImageEncoder::ExtractDataAsync(type,
return; params,
} usingCustomParseOptions,
imageBuffer,
if (fallbackToPNG) { format,
type.AssignLiteral("image/png"); GetSize(),
} mCurrentContext,
scriptContext,
uint64_t imgSize; aCallback);
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);
} }
already_AddRefed<nsIDOMFile> already_AddRefed<nsIDOMFile>
@ -682,17 +576,10 @@ HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
const nsAString& aType, const nsAString& aType,
nsIDOMFile** aResult) nsIDOMFile** aResult)
{ {
bool fallbackToPNG = false;
nsCOMPtr<nsIInputStream> stream; nsCOMPtr<nsIInputStream> stream;
nsresult rv = ExtractData(aType, EmptyString(), getter_AddRefs(stream),
fallbackToPNG);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString type(aType); nsAutoString type(aType);
if (fallbackToPNG) { nsresult rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
type.AssignLiteral("image/png"); NS_ENSURE_SUCCESS(rv, rv);
}
uint64_t imgSize; uint64_t imgSize;
rv = stream->Available(&imgSize); rv = stream->Available(&imgSize);

Просмотреть файл

@ -22,6 +22,7 @@ INCLUDES += \
-I$(srcdir)/../../../../editor/libeditor/text \ -I$(srcdir)/../../../../editor/libeditor/text \
-I$(srcdir)/../../../../editor/txmgr/src \ -I$(srcdir)/../../../../editor/txmgr/src \
-I$(srcdir)/../../../../netwerk/base/src \ -I$(srcdir)/../../../../netwerk/base/src \
-I$(srcdir)/../../../../content/canvas/src \
-I$(srcdir) \ -I$(srcdir) \
-I$(topsrcdir)/xpcom/ds \ -I$(topsrcdir)/xpcom/ds \
-I$(topsrcdir)/content/media/ \ -I$(topsrcdir)/content/media/ \

Просмотреть файл

@ -16,25 +16,20 @@
const int32_t txExecutionState::kMaxRecursionDepth = 20000; const int32_t txExecutionState::kMaxRecursionDepth = 20000;
nsresult txLoadedDocumentsHash::init(txXPathNode* aSourceDocument) void
txLoadedDocumentsHash::init(txXPathNode* aSourceDocument)
{ {
mSourceDocument = aSourceDocument; mSourceDocument = aSourceDocument;
nsAutoString baseURI; nsAutoString baseURI;
txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI); txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
txLoadedDocumentEntry* entry = PutEntry(baseURI); PutEntry(baseURI)->mDocument = mSourceDocument;
if (!entry) {
return NS_ERROR_FAILURE;
}
entry->mDocument = mSourceDocument;
return NS_OK;
} }
txLoadedDocumentsHash::~txLoadedDocumentsHash() txLoadedDocumentsHash::~txLoadedDocumentsHash()
{ {
if (mSourceDocument) {
nsAutoString baseURI; nsAutoString baseURI;
txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI); txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
@ -43,6 +38,7 @@ txLoadedDocumentsHash::~txLoadedDocumentsHash()
delete entry->mDocument.forget(); delete entry->mDocument.forget();
} }
} }
}
txExecutionState::txExecutionState(txStylesheet* aStylesheet, txExecutionState::txExecutionState(txStylesheet* aStylesheet,
bool aDisableLoads) bool aDisableLoads)
@ -118,14 +114,7 @@ txExecutionState::init(const txXPathNode& aNode,
mOutputHandler->startDocument(); mOutputHandler->startDocument();
// Set up loaded-documents-hash // Set up loaded-documents-hash
nsAutoPtr<txXPathNode> document(txXPathNodeUtils::getOwnerDocument(aNode)); mLoadedDocuments.init(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();
// Init members // Init members
rv = mKeyHash.init(); rv = mKeyHash.init();

Просмотреть файл

@ -57,11 +57,12 @@ class txLoadedDocumentsHash : public nsTHashtable<txLoadedDocumentEntry>
{ {
public: public:
txLoadedDocumentsHash() txLoadedDocumentsHash()
: nsTHashtable<txLoadedDocumentEntry>(8) : nsTHashtable<txLoadedDocumentEntry>(8),
mSourceDocument(nullptr)
{ {
} }
~txLoadedDocumentsHash(); ~txLoadedDocumentsHash();
nsresult init(txXPathNode* aSourceDocument); void init(txXPathNode* aSourceDocument);
private: private:
friend class txExecutionState; friend class txExecutionState;

Просмотреть файл

@ -101,10 +101,12 @@ LOCAL_INCLUDES += \
$(NULL) $(NULL)
endif endif
ABS_DIST := $(abspath $(DIST))
EXTRA_EXPORT_MDDEPEND_FILES := $(addsuffix .pp,$(binding_dependency_trackers)) EXTRA_EXPORT_MDDEPEND_FILES := $(addsuffix .pp,$(binding_dependency_trackers))
EXPORTS_GENERATED_FILES := $(exported_binding_headers) $(exported_generated_events_headers) 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 EXPORTS_GENERATED_TARGET := export
INSTALL_TARGETS += EXPORTS_GENERATED INSTALL_TARGETS += EXPORTS_GENERATED
@ -118,7 +120,7 @@ globalgen_headers_FILES := \
UnionConversions.h \ UnionConversions.h \
UnionTypes.h \ UnionTypes.h \
$(NULL) $(NULL)
globalgen_headers_DEST = $(DIST)/include/mozilla/dom globalgen_headers_DEST = $(ABS_DIST)/include/mozilla/dom
globalgen_headers_TARGET := export globalgen_headers_TARGET := export
INSTALL_TARGETS += globalgen_headers INSTALL_TARGETS += globalgen_headers

Просмотреть файл

@ -4,8 +4,6 @@
#include "nsISupports.idl" #include "nsISupports.idl"
interface nsIDOMMozNetworkStatsInterface;
[scriptable, builtinclass, uuid(3b16fe17-5583-483a-b486-b64a3243221c)] [scriptable, builtinclass, uuid(3b16fe17-5583-483a-b486-b64a3243221c)]
interface nsIDOMMozNetworkStatsData : nsISupports interface nsIDOMMozNetworkStatsData : nsISupports
{ {
@ -14,7 +12,7 @@ interface nsIDOMMozNetworkStatsData : nsISupports
readonly attribute jsval date; // Date. readonly attribute jsval date; // Date.
}; };
[scriptable, builtinclass, uuid(b6fc4b14-628d-4c99-bf4e-e4ed56916cbe)] [scriptable, builtinclass, uuid(6613ea55-b99c-44f9-91bf-d07da10b9b74)]
interface nsIDOMMozNetworkStats : nsISupports interface nsIDOMMozNetworkStats : nsISupports
{ {
/** /**
@ -24,12 +22,13 @@ interface nsIDOMMozNetworkStats : nsISupports
readonly attribute DOMString manifestURL; 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. readonly attribute jsval data; // array of NetworkStatsData.
// one element per day. // one element per day.

Просмотреть файл

@ -6,65 +6,57 @@
interface nsIDOMDOMRequest; interface nsIDOMDOMRequest;
/** dictionary NetworkStatsOptions
* Represents a data interface for which the manager is recording statistics.
*/
[scriptable, uuid(f540615b-d803-43ff-8200-2a9d145a5645)]
interface nsIDOMMozNetworkStatsInterface : nsISupports
{ {
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 interface nsIDOMMozNetworkStatsManager : nsISupports
{ {
/** /**
* Constants for known interface types. * Query network statistics.
*/
const long WIFI = 0;
const long MOBILE = 1;
/**
* Find samples between two dates start and end, both included.
* *
* If manifestURL is provided, per-app usage is retrieved, * If options.connectionType is not provided, return statistics for all known
* otherwise the target will be system usage. * 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, nsIDOMDOMRequest getNetworkStats(in jsval options);
in jsval start,
in jsval end,
[optional] in DOMString manifestURL);
/** /**
* 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. * Time in seconds between samples stored in database.
*/
readonly attribute jsval availableNetworks; // array of nsIDOMMozNetworkStatsInterface.
/**
* Minimum time in milliseconds between samples stored in the database.
*/ */
readonly attribute long sampleRate; readonly attribute long sampleRate;
/** /**
* Time in milliseconds recorded by the API until present time. All samples * Maximum number of samples stored in the database per connection type.
* older than maxStorageAge from now are deleted.
*/ */
readonly attribute long long maxStorageAge; readonly attribute long maxStorageSamples;
}; };

Просмотреть файл

@ -4,8 +4,6 @@
#include "nsISupports.idl" #include "nsISupports.idl"
interface nsINetworkInterface;
[scriptable, function, uuid(5f821529-1d80-4ab5-a933-4e1b3585b6bc)] [scriptable, function, uuid(5f821529-1d80-4ab5-a933-4e1b3585b6bc)]
interface nsINetworkStatsServiceProxyCallback : nsISupports interface nsINetworkStatsServiceProxyCallback : nsISupports
{ {
@ -16,20 +14,20 @@ interface nsINetworkStatsServiceProxyCallback : nsISupports
void notify(in boolean aResult, in jsval aMessage); 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 interface nsINetworkStatsServiceProxy : nsISupports
{ {
/* /*
* An interface used to record per-app traffic data. * An interface used to record per-app traffic data.
* @param aAppId app id * @param aAppId app id
* @param aNetworkInterface network * @param aConnectionType network connection type (0 for wifi, 1 for mobile)
* @param aTimeStamp time stamp * @param aTimeStamp time stamp
* @param aRxBytes received data amount * @param aRxBytes received data amount
* @param aTxBytes transmitted data amount * @param aTxBytes transmitted data amount
* @param aCallback an optional callback * @param aCallback an optional callback
*/ */
void saveAppStats(in unsigned long aAppId, void saveAppStats(in unsigned long aAppId,
in nsINetworkInterface aNetwork, in long aConnectionType,
in unsigned long long aTimeStamp, in unsigned long long aTimeStamp,
in unsigned long long aRxBytes, in unsigned long long aRxBytes,
in unsigned long long aTxBytes, in unsigned long long aTxBytes,

Просмотреть файл

@ -16,7 +16,8 @@ Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
const DB_NAME = "net_stats"; const DB_NAME = "net_stats";
const DB_VERSION = 2; 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 // Constant defining the maximum values allowed per interface. If more, older
// will be erased. // will be erased.
@ -25,11 +26,12 @@ const VALUES_MAX_LENGTH = 6 * 30;
// Constant defining the rate of the samples. Daily. // Constant defining the rate of the samples. Daily.
const SAMPLE_RATE = 1000 * 60 * 60 * 24; const SAMPLE_RATE = 1000 * 60 * 60 * 24;
this.NetworkStatsDB = function NetworkStatsDB() { this.NetworkStatsDB = function NetworkStatsDB(aConnectionTypes) {
if (DEBUG) { if (DEBUG) {
debug("Constructor"); debug("Constructor");
} }
this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME]); this._connectionTypes = aConnectionTypes;
this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME_V2]);
} }
NetworkStatsDB.prototype = { NetworkStatsDB.prototype = {
@ -42,7 +44,7 @@ NetworkStatsDB.prototype = {
function errorCb(error) { function errorCb(error) {
txnCb(error, null); 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) { upgradeSchema: function upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) {
@ -67,57 +69,69 @@ NetworkStatsDB.prototype = {
if (DEBUG) { if (DEBUG) {
debug("Created object stores and indexes"); 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) { } else if (currVersion == 1) {
// In order to support per-app traffic data storage, the original // In order to support per-app traffic data storage, the original
// objectStore needs to be replaced by a new objectStore with new // objectStore needs to be replaced by a new objectStore with new
// key path ("appId") and new index ("appId"). // key path ("appId") and new index ("appId").
// Also, since now networks are identified by their let newObjectStore;
// [networkId, networkType] not just by their connectionType, newObjectStore = db.createObjectStore(STORE_NAME_V2, { keyPath: ["appId", "connectionType", "timestamp"] });
// to modify the keyPath is mandatory to delete the object store newObjectStore.createIndex("appId", "appId", { unique: false });
// and create it again. Old data is going to be deleted because the newObjectStore.createIndex("connectionType", "connectionType", { unique: false });
// networkId for each sample can not be set. 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); db.deleteObjectStore(STORE_NAME);
return;
}
objectStore = db.createObjectStore(STORE_NAME, { keyPath: ["appId", "network", "timestamp"] }); let oldStats = cursor.value;
objectStore.createIndex("appId", "appId", { unique: false }); let newStats = { appId: 0,
objectStore.createIndex("network", "network", { unique: false }); connectionType: oldStats.connectionType,
objectStore.createIndex("networkType", "networkType", { unique: false }); timestamp: oldStats.timestamp,
objectStore.createIndex("timestamp", "timestamp", { unique: false }); rxBytes: oldStats.rxBytes,
objectStore.createIndex("rxBytes", "rxBytes", { unique: false }); txBytes: oldStats.txBytes,
objectStore.createIndex("txBytes", "txBytes", { unique: false }); rxTotalBytes: oldStats.rxTotalBytes,
objectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false }); txTotalBytes: oldStats.txTotalBytes };
objectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false }); this._saveStats(aTransaction, newObjectStore, newStats);
cursor.continue();
debug("Created object stores and indexes for version 2"); }.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) { normalizeDate: function normalizeDate(aDate) {
// Convert to UTC according to timezone and // Convert to UTC according to timezone and
// filter timestamp to get SAMPLE_RATE precission // filter timestamp to get SAMPLE_RATE precission
@ -126,42 +140,29 @@ NetworkStatsDB.prototype = {
return timestamp; return timestamp;
}, },
saveStats: function saveStats(aStats, aResultCb) { saveStats: function saveStats(stats, aResultCb) {
let timestamp = this.normalizeDate(aStats.date); let timestamp = this.normalizeDate(stats.date);
let stats = { appId: aStats.appId, stats = { appId: stats.appId,
networkId: aStats.networkId, connectionType: stats.connectionType,
networkType: aStats.networkType,
timestamp: timestamp, timestamp: timestamp,
rxBytes: (aStats.appId == 0) ? 0 : aStats.rxBytes, rxBytes: (stats.appId == 0) ? 0 : stats.rxBytes,
txBytes: (aStats.appId == 0) ? 0 : aStats.txBytes, txBytes: (stats.appId == 0) ? 0 : stats.txBytes,
rxTotalBytes: (aStats.appId == 0) ? aStats.rxBytes : 0, rxTotalBytes: (stats.appId == 0) ? stats.rxBytes : 0,
txTotalBytes: (aStats.appId == 0) ? aStats.txBytes : 0 }; txTotalBytes: (stats.appId == 0) ? stats.txBytes : 0 };
stats = this.importData(stats); this.dbNewTxn("readwrite", function(txn, store) {
this.dbNewTxn("readwrite", function(aTxn, aStore) {
if (DEBUG) { if (DEBUG) {
debug("Filtered time: " + new Date(timestamp)); debug("Filtered time: " + new Date(timestamp));
debug("New stats: " + JSON.stringify(stats)); 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) { request.onsuccess = function onsuccess(event) {
let cursor = event.target.result; let cursor = event.target.result;
if (!cursor) { if (!cursor) {
// Empty, so save first element. // Empty, so save first element.
this._saveStats(txn, store, stats);
// 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);
return; return;
} }
@ -176,10 +177,10 @@ NetworkStatsDB.prototype = {
} }
// Remove stats previous to now - VALUE_MAX_LENGTH // 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 // Process stats before save
this._processSamplesDiff(aTxn, aStore, cursor, stats); this._processSamplesDiff(txn, store, cursor, stats);
}.bind(this); }.bind(this);
}.bind(this), aResultCb); }.bind(this), aResultCb);
}, },
@ -188,21 +189,20 @@ NetworkStatsDB.prototype = {
* This function check that stats are saved in the database following the sample rate. * 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. * In this way is easier to find elements when stats are requested.
*/ */
_processSamplesDiff: function _processSamplesDiff(aTxn, aStore, aLastSampleCursor, aNewSample) { _processSamplesDiff: function _processSamplesDiff(txn, store, lastSampleCursor, newSample) {
let lastSample = aLastSampleCursor.value; let lastSample = lastSampleCursor.value;
// Get difference between last and new sample. // 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) { if (diff % 1) {
// diff is decimal, so some error happened because samples are stored as a multiple // diff is decimal, so some error happened because samples are stored as a multiple
// of SAMPLE_RATE // of SAMPLE_RATE
aTxn.abort(); txn.abort();
throw new Error("Error processing samples"); throw new Error("Error processing samples");
} }
if (DEBUG) { if (DEBUG) {
debug("New: " + aNewSample.timestamp + " - Last: " + debug("New: " + newSample.timestamp + " - Last: " + lastSample.timestamp + " - diff: " + diff);
lastSample.timestamp + " - diff: " + diff);
} }
// If the incoming data is obtained from netd (|newSample.appId| is 0), // 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|. // |txTotalBytes|/|rxTotalBytes| and the last |txTotalBytes|/|rxTotalBytes|.
// Else, the incoming data is per-app data (|newSample.appId| is not 0), // Else, the incoming data is per-app data (|newSample.appId| is not 0),
// the |txBytes|/|rxBytes| is directly the new |txBytes|/|rxBytes|. // the |txBytes|/|rxBytes| is directly the new |txBytes|/|rxBytes|.
if (aNewSample.appId == 0) { if (newSample.appId == 0) {
let rxDiff = aNewSample.rxTotalBytes - lastSample.rxTotalBytes; let rxDiff = newSample.rxTotalBytes - lastSample.rxTotalBytes;
let txDiff = aNewSample.txTotalBytes - lastSample.txTotalBytes; let txDiff = newSample.txTotalBytes - lastSample.txTotalBytes;
if (rxDiff < 0 || txDiff < 0) { if (rxDiff < 0 || txDiff < 0) {
rxDiff = aNewSample.rxTotalBytes; rxDiff = newSample.rxTotalBytes;
txDiff = aNewSample.txTotalBytes; txDiff = newSample.txTotalBytes;
} }
aNewSample.rxBytes = rxDiff; newSample.rxBytes = rxDiff;
aNewSample.txBytes = txDiff; newSample.txBytes = txDiff;
} }
if (diff == 1) { if (diff == 1) {
@ -227,12 +227,11 @@ NetworkStatsDB.prototype = {
// If the incoming data is per-data data, new |rxTotalBytes|/|txTotalBytes| // If the incoming data is per-data data, new |rxTotalBytes|/|txTotalBytes|
// needs to be obtained by adding new |rxBytes|/|txBytes| to last // needs to be obtained by adding new |rxBytes|/|txBytes| to last
// |rxTotalBytes|/|txTotalBytes|. // |rxTotalBytes|/|txTotalBytes|.
if (aNewSample.appId != 0) { if (newSample.appId != 0) {
aNewSample.rxTotalBytes = aNewSample.rxBytes + lastSample.rxTotalBytes; newSample.rxTotalBytes = newSample.rxBytes + lastSample.rxTotalBytes;
aNewSample.txTotalBytes = aNewSample.txBytes + lastSample.txTotalBytes; newSample.txTotalBytes = newSample.txBytes + lastSample.txTotalBytes;
} }
this._saveStats(txn, store, newSample);
this._saveStats(aTxn, aStore, aNewSample);
return; return;
} }
if (diff > 1) { if (diff > 1) {
@ -245,20 +244,19 @@ NetworkStatsDB.prototype = {
let data = []; let data = [];
for (let i = diff - 2; i >= 0; i--) { for (let i = diff - 2; i >= 0; i--) {
let time = aNewSample.timestamp - SAMPLE_RATE * (i + 1); let time = newSample.timestamp - SAMPLE_RATE * (i + 1);
let sample = { appId: aNewSample.appId, let sample = {appId: newSample.appId,
network: aNewSample.network, connectionType: newSample.connectionType,
timestamp: time, timestamp: time,
rxBytes: 0, rxBytes: 0,
txBytes: 0, txBytes: 0,
rxTotalBytes: lastSample.rxTotalBytes, rxTotalBytes: lastSample.rxTotalBytes,
txTotalBytes: lastSample.txTotalBytes}; txTotalBytes: lastSample.txTotalBytes};
data.push(sample); data.push(sample);
} }
data.push(aNewSample); data.push(newSample);
this._saveStats(aTxn, aStore, data); this._saveStats(txn, store, data);
return; return;
} }
if (diff == 0 || diff < 0) { 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. // If diff < 0, clock or timezone changed back. Place data in the last sample.
lastSample.rxBytes += aNewSample.rxBytes; lastSample.rxBytes += newSample.rxBytes;
lastSample.txBytes += aNewSample.txBytes; lastSample.txBytes += newSample.txBytes;
// If incoming data is obtained from netd, last |rxTotalBytes|/|txTotalBytes| // If incoming data is obtained from netd, last |rxTotalBytes|/|txTotalBytes|
// needs to get updated by replacing the new |rxTotalBytes|/|txTotalBytes|. // needs to get updated by replacing the new |rxTotalBytes|/|txTotalBytes|.
if (aNewSample.appId == 0) { if (newSample.appId == 0) {
lastSample.rxTotalBytes = aNewSample.rxTotalBytes; lastSample.rxTotalBytes = newSample.rxTotalBytes;
lastSample.txTotalBytes = aNewSample.txTotalBytes; lastSample.txTotalBytes = newSample.txTotalBytes;
} else { } else {
// Else, the incoming data is per-app data, old |rxTotalBytes|/ // Else, the incoming data is per-app data, old |rxTotalBytes|/
// |txTotalBytes| needs to get updated by adding the new // |txTotalBytes| needs to get updated by adding the new
// |rxBytes|/|txBytes| to last |rxTotalBytes|/|txTotalBytes|. // |rxBytes|/|txBytes| to last |rxTotalBytes|/|txTotalBytes|.
lastSample.rxTotalBytes += aNewSample.rxBytes; lastSample.rxTotalBytes += newSample.rxBytes;
lastSample.txTotalBytes += aNewSample.txBytes; lastSample.txTotalBytes += newSample.txBytes;
} }
if (DEBUG) { if (DEBUG) {
debug("Update: " + JSON.stringify(lastSample)); 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) { if (DEBUG) {
debug("_saveStats: " + JSON.stringify(aNetworkStats)); debug("_saveStats: " + JSON.stringify(networkStats));
} }
if (Array.isArray(aNetworkStats)) { if (Array.isArray(networkStats)) {
let len = aNetworkStats.length - 1; let len = networkStats.length - 1;
for (let i = 0; i <= len; i++) { for (let i = 0; i <= len; i++) {
aStore.put(aNetworkStats[i]); store.put(networkStats[i]);
} }
} else { } 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. // Callback function to remove old items when new ones are added.
let filterDate = aDate - (SAMPLE_RATE * VALUES_MAX_LENGTH - 1); let filterDate = date - (SAMPLE_RATE * VALUES_MAX_LENGTH - 1);
let lowerFilter = [aAppId, aNetwork, 0]; let lowerFilter = [appId, connType, 0];
let upperFilter = [aAppId, aNetwork, filterDate]; let upperFilter = [appId, connType, filterDate];
let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false); let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
let lastSample = null; store.openCursor(range).onsuccess = function(event) {
let self = this;
aStore.openCursor(range).onsuccess = function(event) {
var cursor = event.target.result; var cursor = event.target.result;
if (cursor) { if (cursor) {
lastSample = cursor.value;
cursor.delete(); cursor.delete();
cursor.continue(); cursor.continue();
return;
} }
}.bind(this);
// 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);
}
};
};
}, },
clearInterfaceStats: function clearInterfaceStats(aNetwork, aResultCb) { clear: function clear(aResultCb) {
let network = [aNetwork.id, aNetwork.type]; this.dbNewTxn("readwrite", function(txn, store) {
let self = this; if (DEBUG) {
debug("Going to clear all!");
// 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;
} }
store.clear();
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);
}
};
}, aResultCb); }, aResultCb);
}, },
clearStats: function clearStats(aNetworks, aResultCb) { find: function find(aResultCb, aOptions) {
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) {
let offset = (new Date()).getTimezoneOffset() * 60 * 1000; let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
let start = this.normalizeDate(aStart); let start = this.normalizeDate(aOptions.start);
let end = this.normalizeDate(aEnd); let end = this.normalizeDate(aOptions.end);
if (DEBUG) { if (DEBUG) {
debug("Find samples for appId: " + aAppId + " network " + debug("Find: appId: " + aOptions.appId + " connectionType:" +
JSON.stringify(aNetwork) + " from " + start + " until " + end); aOptions.connectionType + " start: " + start + " end: " + end);
debug("Start time: " + new Date(start)); debug("Start time: " + new Date(start));
debug("End time: " + new Date(end)); debug("End time: " + new Date(end));
} }
this.dbNewTxn("readonly", function(aTxn, aStore) { this.dbNewTxn("readonly", function(txn, store) {
let network = [aNetwork.id, aNetwork.type]; let lowerFilter = [aOptions.appId, aOptions.connectionType, start];
let lowerFilter = [aAppId, network, start]; let upperFilter = [aOptions.appId, aOptions.connectionType, end];
let upperFilter = [aAppId, network, end];
let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false); let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
let data = []; let data = [];
if (!aTxn.result) { if (!txn.result) {
aTxn.result = {}; txn.result = {};
} }
let request = aStore.openCursor(range).onsuccess = function(event) { let request = store.openCursor(range).onsuccess = function(event) {
var cursor = event.target.result; var cursor = event.target.result;
if (cursor){ if (cursor){
data.push({ rxBytes: cursor.value.rxBytes, data.push({ rxBytes: cursor.value.rxBytes,
@ -438,11 +364,66 @@ NetworkStatsDB.prototype = {
// now - VALUES_MAX_LENGTH, fill with empty samples. // now - VALUES_MAX_LENGTH, fill with empty samples.
this.fillResultSamples(start + offset, end + offset, data); this.fillResultSamples(start + offset, end + offset, data);
aTxn.result.manifestURL = aManifestURL; txn.result.manifestURL = aOptions.manifestURL;
aTxn.result.network = aNetwork; txn.result.connectionType = aOptions.connectionType;
aTxn.result.start = aStart; txn.result.start = aOptions.start;
aTxn.result.end = aEnd; txn.result.end = aOptions.end;
aTxn.result.data = data; 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);
}.bind(this), aResultCb); }.bind(this), aResultCb);
}, },
@ -480,9 +461,9 @@ NetworkStatsDB.prototype = {
}, },
logAllRecords: function logAllRecords(aResultCb) { logAllRecords: function logAllRecords(aResultCb) {
this.dbNewTxn("readonly", function(aTxn, aStore) { this.dbNewTxn("readonly", function(txn, store) {
aStore.mozGetAll().onsuccess = function onsuccess(event) { store.mozGetAll().onsuccess = function onsuccess(event) {
aTxn.result = event.target.result; txn.result = event.target.result;
}; };
}, aResultCb); }, aResultCb);
}, },

Просмотреть файл

@ -55,38 +55,9 @@ NetworkStatsData.prototype = {
QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsData]) 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 // NetworkStats
const NETWORKSTATS_CONTRACTID = "@mozilla.org/networkstats;1"; 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; const nsIDOMMozNetworkStats = Components.interfaces.nsIDOMMozNetworkStats;
function NetworkStats(aWindow, aStats) { function NetworkStats(aWindow, aStats) {
@ -94,7 +65,7 @@ function NetworkStats(aWindow, aStats) {
debug("NetworkStats Constructor"); debug("NetworkStats Constructor");
} }
this.manifestURL = aStats.manifestURL || null; this.manifestURL = aStats.manifestURL || null;
this.network = new NetworkStatsInterface(aStats.network); this.connectionType = aStats.connectionType || null;
this.start = aStats.start || null; this.start = aStats.start || null;
this.end = aStats.end || null; this.end = aStats.end || null;
@ -107,7 +78,7 @@ function NetworkStats(aWindow, aStats) {
NetworkStats.prototype = { NetworkStats.prototype = {
__exposedProps__: { __exposedProps__: {
manifestURL: 'r', manifestURL: 'r',
network: 'r', connectionType: 'r',
start: 'r', start: 'r',
end: 'r', end: 'r',
data: 'r', data: 'r',
@ -121,14 +92,13 @@ NetworkStats.prototype = {
flags: nsIClassInfo.DOM_OBJECT}), flags: nsIClassInfo.DOM_OBJECT}),
QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStats, QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStats,
nsIDOMMozNetworkStatsData, nsIDOMMozNetworkStatsData])
nsIDOMMozNetworkStatsInterface])
} }
// NetworkStatsManager // NetworkStatsManager
const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1"; 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; const nsIDOMMozNetworkStatsManager = Components.interfaces.nsIDOMMozNetworkStatsManager;
function NetworkStatsManager() { function NetworkStatsManager() {
@ -146,64 +116,42 @@ NetworkStatsManager.prototype = {
} }
}, },
getSamples: function getSamples(aNetwork, aStart, aEnd, aManifestURL) { getNetworkStats: function getNetworkStats(aOptions) {
this.checkPrivileges(); this.checkPrivileges();
if (aStart.constructor.name !== "Date" || if (!aOptions.start || !aOptions.end ||
aEnd.constructor.name !== "Date" || aOptions.start > aOptions.end) {
aStart > aEnd) {
throw Components.results.NS_ERROR_INVALID_ARG; throw Components.results.NS_ERROR_INVALID_ARG;
} }
let request = this.createRequest(); let request = this.createRequest();
cpmm.sendAsyncMessage("NetworkStats:Get", cpmm.sendAsyncMessage("NetworkStats:Get",
{ network: aNetwork, {data: aOptions, id: this.getRequestId(request)});
start: aStart,
end: aEnd,
manifestURL: aManifestURL,
id: this.getRequestId(request) });
return request; return request;
}, },
clearStats: function clearStats(aNetwork) { clearAllData: function clearAllData() {
this.checkPrivileges(); this.checkPrivileges();
let request = this.createRequest(); let request = this.createRequest();
cpmm.sendAsyncMessage("NetworkStats:Clear", 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)}); {id: this.getRequestId(request)});
return request; return request;
}, },
get availableNetworks() { get connectionTypes() {
this.checkPrivileges(); this.checkPrivileges();
return ObjectWrapper.wrap(cpmm.sendSyncMessage("NetworkStats:Types")[0], this._window);
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;
}, },
get sampleRate() { get sampleRate() {
this.checkPrivileges(); this.checkPrivileges();
return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0]; return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0] / 1000;
}, },
get maxStorageAge() { get maxStorageSamples() {
this.checkPrivileges(); this.checkPrivileges();
return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0]; return cpmm.sendSyncMessage("NetworkStats:MaxStorageSamples")[0];
}, },
receiveMessage: function(aMessage) { receiveMessage: function(aMessage) {
@ -235,7 +183,6 @@ NetworkStatsManager.prototype = {
break; break;
case "NetworkStats:Clear:Return": case "NetworkStats:Clear:Return":
case "NetworkStats:ClearAll:Return":
if (msg.error) { if (msg.error) {
Services.DOMRequest.fireError(req, msg.error); Services.DOMRequest.fireError(req, msg.error);
return; return;
@ -275,8 +222,7 @@ NetworkStatsManager.prototype = {
} }
this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return", this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return",
"NetworkStats:Clear:Return", "NetworkStats:Clear:Return"]);
"NetworkStats:ClearAll:Return"]);
}, },
// Called from DOMRequestIpcHelper // Called from DOMRequestIpcHelper
@ -299,6 +245,5 @@ NetworkStatsManager.prototype = {
} }
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsData, this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsData,
NetworkStatsInterface,
NetworkStats, NetworkStats,
NetworkStatsManager]); NetworkStatsManager]);

Просмотреть файл

@ -1,12 +1,9 @@
component {3b16fe17-5583-483a-b486-b64a3243221c} NetworkStatsManager.js component {3b16fe17-5583-483a-b486-b64a3243221c} NetworkStatsManager.js
contract @mozilla.org/networkStatsdata;1 {3b16fe17-5583-483a-b486-b64a3243221c} contract @mozilla.org/networkStatsdata;1 {3b16fe17-5583-483a-b486-b64a3243221c}
component {b6fc4b14-628d-4c99-bf4e-e4ed56916cbe} NetworkStatsManager.js component {6613ea55-b99c-44f9-91bf-d07da10b9b74} NetworkStatsManager.js
contract @mozilla.org/networkStats;1 {b6fc4b14-628d-4c99-bf4e-e4ed56916cbe} contract @mozilla.org/networkStats;1 {6613ea55-b99c-44f9-91bf-d07da10b9b74}
component {f540615b-d803-43ff-8200-2a9d145a5645} NetworkStatsManager.js component {87529a6c-aef6-11e1-a595-4f034275cfa6} NetworkStatsManager.js
contract @mozilla.org/networkstatsinterface;1 {f540615b-d803-43ff-8200-2a9d145a5645} contract @mozilla.org/networkStatsManager;1 {87529a6c-aef6-11e1-a595-4f034275cfa6}
component {5fbdcae6-a2cd-47b3-929f-83ac75bd4881} NetworkStatsManager.js
contract @mozilla.org/networkStatsManager;1 {5fbdcae6-a2cd-47b3-929f-83ac75bd4881}
category JavaScript-navigator-property mozNetworkStats @mozilla.org/networkStatsManager;1 category JavaScript-navigator-property mozNetworkStats @mozilla.org/networkStatsManager;1

Просмотреть файл

@ -5,11 +5,7 @@
"use strict"; "use strict";
const DEBUG = false; const DEBUG = false;
function debug(s) { function debug(s) { dump("-*- NetworkStatsService: " + s + "\n"); }
if (DEBUG) {
dump("-*- NetworkStatsService: " + s + "\n");
}
}
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; 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 TOPIC_INTERFACE_UNREGISTERED = "network-interface-unregistered";
const NET_TYPE_WIFI = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI; const NET_TYPE_WIFI = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
const NET_TYPE_MOBILE = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE; 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|. // The maximum traffic amount can be saved in the |cachedAppStats|.
const MAX_CACHED_TRAFFIC = 500 * 1000 * 1000; // 500 MB const MAX_CACHED_TRAFFIC = 500 * 1000 * 1000; // 500 MB
@ -42,19 +39,11 @@ XPCOMUtils.defineLazyServiceGetter(this, "appsService",
"@mozilla.org/AppsService;1", "@mozilla.org/AppsService;1",
"nsIAppsService"); "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 = { this.NetworkStatsService = {
init: function() { init: function() {
if (DEBUG) {
debug("Service started"); debug("Service started");
}
Services.obs.addObserver(this, "xpcom-shutdown", false); Services.obs.addObserver(this, "xpcom-shutdown", false);
Services.obs.addObserver(this, TOPIC_INTERFACE_REGISTERED, 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); this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
// Object to store network interfaces, each network interface is composed this._connectionTypes = Object.create(null);
// by a network object (network type and network Id) and a interfaceName this._connectionTypes[NET_TYPE_WIFI] = { name: "wifi",
// that contains the name of the physical interface (wlan0, rmnet0, etc.). network: Object.create(null) };
// The network type can be 0 for wifi or 1 for mobile. On the other hand, this._connectionTypes[NET_TYPE_MOBILE] = { name: "mobile",
// the network id is '0' for wifi or the iccid for mobile (SIM). network: Object.create(null) };
// 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._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", this.messages = ["NetworkStats:Get",
"NetworkStats:Clear", "NetworkStats:Clear",
"NetworkStats:ClearAll", "NetworkStats:Types",
"NetworkStats:Networks",
"NetworkStats:SampleRate", "NetworkStats:SampleRate",
"NetworkStats:MaxStorageAge"]; "NetworkStats:MaxStorageSamples"];
this.messages.forEach(function(aMsgName) { this.messages.forEach(function(msgName) {
ppmm.addMessageListener(aMsgName, this); ppmm.addMessageListener(msgName, this);
}, this); }, this);
this._db = new NetworkStatsDB(); this._db = new NetworkStatsDB(this._connectionTypes);
// Stats for all interfaces are updated periodically // Stats for all interfaces are updated periodically
this.timer.initWithCallback(this, this._db.sampleRate, this.timer.initWithCallback(this, this._db.sampleRate,
@ -116,56 +88,58 @@ this.NetworkStatsService = {
return; return;
} }
if (DEBUG) {
debug("receiveMessage " + aMessage.name); debug("receiveMessage " + aMessage.name);
}
let mm = aMessage.target; let mm = aMessage.target;
let msg = aMessage.json; let msg = aMessage.json;
switch (aMessage.name) { switch (aMessage.name) {
case "NetworkStats:Get": case "NetworkStats:Get":
this.getSamples(mm, msg); this.getStats(mm, msg);
break; break;
case "NetworkStats:Clear": case "NetworkStats:Clear":
this.clearInterfaceStats(mm, msg);
break;
case "NetworkStats:ClearAll":
this.clearDB(mm, msg); this.clearDB(mm, msg);
break; break;
case "NetworkStats:Networks": case "NetworkStats:Types":
return this.availableNetworks(); // This message is sync.
let types = [];
for (let i in this._connectionTypes) {
types.push(this._connectionTypes[i].name);
}
return types;
case "NetworkStats:SampleRate": case "NetworkStats:SampleRate":
// This message is sync. // This message is sync.
return this._db.sampleRate; return this._db.sampleRate;
case "NetworkStats:MaxStorageAge": case "NetworkStats:MaxStorageSamples":
// This message is sync. // This message is sync.
return this._db.maxStorageSamples * this._db.sampleRate; return this._db.maxStorageSamples;
} }
}, },
observe: function observe(aSubject, aTopic, aData) { observe: function observe(subject, topic, data) {
switch (aTopic) { switch (topic) {
case TOPIC_INTERFACE_REGISTERED: case TOPIC_INTERFACE_REGISTERED:
case TOPIC_INTERFACE_UNREGISTERED: case TOPIC_INTERFACE_UNREGISTERED:
// If new interface is registered (notified from NetworkManager), // If new interface is registered (notified from NetworkManager),
// the stats are updated for the new interface without waiting to // the stats are updated for the new interface without waiting to
// complete the updating period. // complete the updating period
let network = subject.QueryInterface(Ci.nsINetworkInterface);
let network = aSubject.QueryInterface(Ci.nsINetworkInterface); if (DEBUG) {
debug("Network " + network.name + " of type " + network.type + " status change"); debug("Network " + network.name + " of type " + network.type + " status change");
let netId = this.convertNetworkInterface(network);
if (!netId) {
break;
} }
if (this._connectionTypes[network.type]) {
this.updateStats(netId); this._connectionTypes[network.type].network = network;
this.updateStats(network.type);
}
break; break;
case "xpcom-shutdown": case "xpcom-shutdown":
if (DEBUG) {
debug("Service shutdown"); debug("Service shutdown");
}
this.messages.forEach(function(aMsgName) { this.messages.forEach(function(msgName) {
ppmm.removeMessageListener(aMsgName, this); ppmm.removeMessageListener(msgName, this);
}, this); }, this);
Services.obs.removeObserver(this, "xpcom-shutdown"); Services.obs.removeObserver(this, "xpcom-shutdown");
@ -186,53 +160,10 @@ this.NetworkStatsService = {
* nsITimerCallback * nsITimerCallback
* Timer triggers the update of all stats * Timer triggers the update of all stats
*/ */
notify: function(aTimer) { notify: function(timer) {
this.updateAllStats(); 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. * Function called from manager to get stats from database.
* In order to return updated stats, first is performed a call to * 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) * Then, depending on the request (stats per appId or total stats)
* it retrieve them from database and return to the manager. * it retrieve them from database and return to the manager.
*/ */
getSamples: function getSamples(mm, msg) { getStats: function getStats(mm, msg) {
let self = this; this.updateAllStats(function onStatsUpdated(aResult, aMessage) {
let network = msg.network;
let netId = this.getNetworkId(network.id, network.type);
if (!this._networks[netId]) { let data = msg.data;
mm.sendAsyncMessage("NetworkStats:Get:Return",
{ id: msg.id, error: "Invalid connectionType", result: null });
return;
}
let appId = 0; let options = { appId: 0,
let manifestURL = msg.manifestURL; connectionType: data.connectionType,
start: data.start,
end: data.end };
let manifestURL = data.manifestURL;
if (manifestURL) { if (manifestURL) {
appId = appsService.getAppLocalIdByManifestURL(manifestURL); let appId = appsService.getAppLocalIdByManifestURL(manifestURL);
if (DEBUG) {
debug("get appId: " + appId + " from manifestURL: " + manifestURL);
}
if (!appId) { if (!appId) {
mm.sendAsyncMessage("NetworkStats:Get:Return", mm.sendAsyncMessage("NetworkStats:Get:Return",
{ id: msg.id, error: "Invalid manifestURL", result: null }); { id: msg.id, error: "Invalid manifestURL", result: null });
return; return;
} }
options.appId = appId;
options.manifestURL = manifestURL;
} }
let start = new Date(msg.start); if (DEBUG) {
let end = new Date(msg.end); debug("getStats for options: " + JSON.stringify(options));
}
this.updateStats(netId, function onStatsUpdated(aResult, aMessage) { if (!options.connectionType || options.connectionType.length == 0) {
debug("getstats for network " + network.id + " of type " + network.type); this._db.findAll(function onStatsFound(error, result) {
debug("appId: " + appId + " from manifestURL: " + manifestURL);
self._db.find(function onStatsFound(aError, aResult) {
mm.sendAsyncMessage("NetworkStats:Get:Return", mm.sendAsyncMessage("NetworkStats:Get:Return",
{ id: msg.id, error: aError, result: aResult }); { id: msg.id, error: error, result: result });
}, network, start, end, appId, manifestURL); }, options);
});
},
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 });
return; return;
} }
this._db.clearInterfaceStats(network, function onDBCleared(aError, aResult) { for (let i in this._connectionTypes) {
mm.sendAsyncMessage("NetworkStats:Clear:Return", if (this._connectionTypes[i].name == options.connectionType) {
{ id: msg.id, error: aError, result: aResult }); 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) { clearDB: function clearDB(mm, msg) {
let networks = this.availableNetworks(); this._db.clear(function onDBCleared(error, result) {
this._db.clearStats(networks, function onDBCleared(aError, aResult) { mm.sendAsyncMessage("NetworkStats:Clear:Return",
mm.sendAsyncMessage("NetworkStats:ClearAll:Return", { id: msg.id, error: error, result: result });
{ id: msg.id, error: aError, result: aResult });
}); });
}, },
updateAllStats: function updateAllStats(aCallback) { updateAllStats: function updateAllStats(callback) {
// Update |cachedAppStats|. // Update |cachedAppStats|.
this.updateCachedAppStats(); this.updateCachedAppStats();
@ -318,19 +247,18 @@ this.NetworkStatsService = {
// the connection type is already in the queue it is not appended again, // 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 // else it is pushed in 'elements' array, which later will be pushed to
// the queue array. // the queue array.
for (let netId in this._networks) { for (let i in this._connectionTypes) {
lastElement = { netId: netId, lastElement = { type: i,
queueIndex: this.updateQueueIndex(netId)}; queueIndex: this.updateQueueIndex(i)};
if (lastElement.queueIndex == -1) { if (lastElement.queueIndex == -1) {
elements.push({netId: lastElement.netId, callbacks: []}); elements.push({type: lastElement.type, callbacks: []});
} }
} }
if (elements.length > 0) { if (elements.length > 0) {
// If length of elements is greater than 0, callback is set to // If length of elements is greater than 0, callback is set to
// the last element. // the last element.
elements[elements.length - 1].callbacks.push(aCallback); elements[elements.length - 1].callbacks.push(callback);
this.updateQueue = this.updateQueue.concat(elements); this.updateQueue = this.updateQueue.concat(elements);
} else { } else {
// Else, it means that all connection types are already in the queue to // 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'. // 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 // But before is checked that element is still in the queue because it can
// be processed while generating 'elements' array. // be processed while generating 'elements' array.
let element = this.updateQueue[lastElement.queueIndex];
if (aCallback && if (!this.updateQueue[lastElement.queueIndex] ||
(!element || element.netId != lastElement.netId)) { this.updateQueue[lastElement.queueIndex].type != lastElement.queueIndex) {
aCallback(); if (callback) {
callback();
}
return; 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. // Call the function that process the elements of the queue.
@ -356,14 +286,14 @@ this.NetworkStatsService = {
} }
}, },
updateStats: function updateStats(aNetId, aCallback) { updateStats: function updateStats(connectionType, callback) {
// Check if the connection is in the main queue, push a new element // 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. // 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) { if (index == -1) {
this.updateQueue.push({netId: aNetId, callbacks: [aCallback]}); this.updateQueue.push({type: connectionType, callbacks: [callback]});
} else { } else {
this.updateQueue[index].callbacks.push(aCallback); this.updateQueue[index].callbacks.push(callback);
} }
// Call the function that process the elements of the queue. // 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. * index, if it is not in the array return -1.
*/ */
updateQueueIndex: function updateQueueIndex(aNetId) { updateQueueIndex: function updateQueueIndex(type) {
return this.updateQueue.map(function(e) { return e.netId; }).indexOf(aNetId); 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. // 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. // Check if connection type is valid.
if (!this._networks[aNetId]) { if (!this._connectionTypes[connectionType]) {
if (aCallback) { if (callback) {
aCallback(false, "Invalid network " + aNetId); callback(false, "Invalid network type " + connectionType);
} }
return; return;
} }
let interfaceName = this._networks[aNetId].interfaceName; if (DEBUG) {
debug("Update stats for " + interfaceName); debug("Update stats for " + this._connectionTypes[connectionType].name);
}
// Request stats to NetworkManager, which will get stats from netd, passing // Request stats to NetworkManager, which will get stats from netd, passing
// 'networkStatsAvailable' as a callback. // 'networkStatsAvailable' as a callback.
if (interfaceName) { let networkName = this._connectionTypes[connectionType].network.name;
networkManager.getNetworkInterfaceStats(interfaceName, if (networkName) {
this.networkStatsAvailable.bind(this, aCallback, aNetId)); networkManager.getNetworkInterfaceStats(networkName,
this.networkStatsAvailable.bind(this, callback, connectionType));
return; return;
} }
if (callback) {
if (aCallback) { callback(true, "ok");
aCallback(true, "ok");
} }
}, },
/* /*
* Callback of request stats. Store stats in database. * Callback of request stats. Store stats in database.
*/ */
networkStatsAvailable: function networkStatsAvailable(aCallback, aNetId, networkStatsAvailable: function networkStatsAvailable(callback, connType, result, rxBytes, txBytes, date) {
aResult, aRxBytes, if (!result) {
aTxBytes, aDate) { if (callback) {
if (!aResult) { callback(false, "Netd IPC error");
if (aCallback) {
aCallback(false, "Netd IPC error");
} }
return; return;
} }
let stats = { appId: 0, let stats = { appId: 0,
networkId: this._networks[aNetId].network.id, connectionType: this._connectionTypes[connType].name,
networkType: this._networks[aNetId].network.type, date: date,
date: aDate, rxBytes: rxBytes,
rxBytes: aTxBytes, txBytes: txBytes };
txBytes: aRxBytes };
debug("Update stats for: " + JSON.stringify(stats)); if (DEBUG) {
debug("Update stats for " + stats.connectionType + ": rx=" + stats.rxBytes +
this._db.saveStats(stats, function onSavedStats(aError, aResult) { " tx=" + stats.txBytes + " timestamp=" + stats.date);
if (aCallback) { }
if (aError) { this._db.saveStats(stats, function onSavedStats(error, result) {
aCallback(false, aError); if (callback) {
if (error) {
callback(false, error);
return; return;
} }
aCallback(true, "OK"); callback(true, "OK");
} }
}); });
}, },
@ -477,34 +412,26 @@ this.NetworkStatsService = {
/* /*
* Function responsible for receiving per-app stats. * Function responsible for receiving per-app stats.
*/ */
saveAppStats: function saveAppStats(aAppId, aNetwork, aTimeStamp, aRxBytes, aTxBytes, aCallback) { saveAppStats: function saveAppStats(aAppId, aConnectionType, aTimeStamp, aRxBytes, aTxBytes, aCallback) {
let netId = this.convertNetworkInterface(aNetwork); if (DEBUG) {
if (!netId) { debug("saveAppStats: " + aAppId + " " + aConnectionType + " " +
if (aCallback) {
aCallback.notify(false, "Invalid network type");
}
return;
}
debug("saveAppStats: " + aAppId + " " + netId + " " +
aTimeStamp + " " + aRxBytes + " " + aTxBytes); aTimeStamp + " " + aRxBytes + " " + aTxBytes);
}
// Check if |aAppId| and |aConnectionType| are valid. // Check if |aAppId| and |aConnectionType| are valid.
if (!aAppId || !this._networks[netId]) { if (!aAppId || aConnectionType == NET_TYPE_UNKNOWN) {
debug("Invalid appId or network interface");
return; return;
} }
let stats = { appId: aAppId, let stats = { appId: aAppId,
networkId: this._networks[netId].network.id, connectionType: this._connectionTypes[aConnectionType].name,
networkType: this._networks[netId].network.type,
date: new Date(aTimeStamp), date: new Date(aTimeStamp),
rxBytes: aRxBytes, rxBytes: aRxBytes,
txBytes: aTxBytes }; txBytes: aTxBytes };
// Generate an unique key from |appId| and |connectionType|, // Generate an unique key from |appId| and |connectionType|,
// which is used to retrieve data in |cachedAppStats|. // 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. // |cachedAppStats| only keeps the data with the same date.
// If the incoming date is different from |cachedAppStatsDate|, // If the incoming date is different from |cachedAppStatsDate|,
@ -551,15 +478,19 @@ this.NetworkStatsService = {
appStats.txBytes > MAX_CACHED_TRAFFIC) { appStats.txBytes > MAX_CACHED_TRAFFIC) {
this._db.saveStats(appStats, this._db.saveStats(appStats,
function (error, result) { function (error, result) {
if (DEBUG) {
debug("Application stats inserted in indexedDB"); debug("Application stats inserted in indexedDB");
} }
}
); );
delete this.cachedAppStats[key]; delete this.cachedAppStats[key];
} }
}, },
updateCachedAppStats: function updateCachedAppStats(aCallback) { updateCachedAppStats: function updateCachedAppStats(callback) {
if (DEBUG) {
debug("updateCachedAppStats: " + this.cachedAppStatsDate); debug("updateCachedAppStats: " + this.cachedAppStatsDate);
}
let stats = Object.keys(this.cachedAppStats); let stats = Object.keys(this.cachedAppStats);
if (stats.length == 0) { if (stats.length == 0) {
@ -578,16 +509,16 @@ this.NetworkStatsService = {
if (index == stats.length - 1) { if (index == stats.length - 1) {
this.cachedAppStats = Object.create(null); this.cachedAppStats = Object.create(null);
if (!aCallback) { if (!callback) {
return; return;
} }
if (error) { if (error) {
aCallback(false, error); callback(false, error);
return; return;
} }
aCallback(true, "ok"); callback(true, "ok");
return; return;
} }
@ -603,17 +534,17 @@ this.NetworkStatsService = {
}, },
logAllRecords: function logAllRecords() { logAllRecords: function logAllRecords() {
this._db.logAllRecords(function onResult(aError, aResult) { this._db.logAllRecords(function onResult(error, result) {
if (aError) { if (error) {
debug("Error: " + aError); debug("Error: " + error);
return; return;
} }
debug("===== LOG ====="); debug("===== LOG =====");
debug("There are " + aResult.length + " items"); debug("There are " + result.length + " items");
debug(JSON.stringify(aResult)); debug(JSON.stringify(result));
}); });
}, }
}; };
NetworkStatsService.init(); NetworkStatsService.init();

Просмотреть файл

@ -29,14 +29,14 @@ NetworkStatsServiceProxy.prototype = {
* Function called in the protocol layer (HTTP, FTP, WebSocket ...etc) * Function called in the protocol layer (HTTP, FTP, WebSocket ...etc)
* to pass the per-app stats to NetworkStatsService. * to pass the per-app stats to NetworkStatsService.
*/ */
saveAppStats: function saveAppStats(aAppId, aNetwork, aTimeStamp, saveAppStats: function saveAppStats(aAppId, aConnectionType, aTimeStamp,
aRxBytes, aTxBytes, aCallback) { aRxBytes, aTxBytes, aCallback) {
if (DEBUG) { if (DEBUG) {
debug("saveAppStats: " + aAppId + " connectionType " + aNetwork.type + debug("saveAppStats: " + aAppId + " " + aConnectionType + " " +
" " + aTimeStamp + " " + aRxBytes + " " + aTxBytes); aTimeStamp + " " + aRxBytes + " " + aTxBytes);
} }
NetworkStatsService.saveAppStats(aAppId, aNetwork, aTimeStamp, NetworkStatsService.saveAppStats(aAppId, aConnectionType, aTimeStamp,
aRxBytes, aTxBytes, aCallback); aRxBytes, aTxBytes, aCallback);
}, },

Просмотреть файл

@ -167,7 +167,7 @@ TCPSocket.prototype = {
_txBytes: 0, _txBytes: 0,
_rxBytes: 0, _rxBytes: 0,
_appId: Ci.nsIScriptSecurityManager.NO_APP_ID, _appId: Ci.nsIScriptSecurityManager.NO_APP_ID,
_activeNetwork: null, _connectionType: Ci.nsINetworkInterface.NETWORK_TYPE_UNKNOWN,
#endif #endif
// Public accessors. // Public accessors.
@ -347,7 +347,7 @@ TCPSocket.prototype = {
LOG("Error: Ci.nsINetworkStatsServiceProxy service is not available."); LOG("Error: Ci.nsINetworkStatsServiceProxy service is not available.");
return; return;
} }
nssProxy.saveAppStats(this._appId, this._activeNetwork, Date.now(), nssProxy.saveAppStats(this._appId, this._connectionType, Date.now(),
this._rxBytes, this._txBytes); this._rxBytes, this._txBytes);
// Reset the counters once the statistics is saved to NetworkStatsServiceProxy. // Reset the counters once the statistics is saved to NetworkStatsServiceProxy.
@ -530,12 +530,12 @@ TCPSocket.prototype = {
that._initStream(that._binaryType); that._initStream(that._binaryType);
#ifdef MOZ_WIDGET_GONK #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 // Note that nsINetworkManager, as well as nsINetworkStatsServiceProxy, is
// Gonk-specific. // Gonk-specific.
let networkManager = Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager); let networkManager = Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
if (networkManager) { if (networkManager && networkManager.active) {
that._activeNetwork = networkManager.active; that._connectionType = networkManager.active.type;
} }
#endif #endif

Просмотреть файл

@ -12,27 +12,38 @@
<pre id="test"> <pre id="test">
<script type="application/javascript"> <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() { function test() {
// Test interfaces
checkInterface("NetworkStatsManager");
checkInterface("NetworkStats");
checkInterface("NetworkStatsData");
ok('mozNetworkStats' in navigator, "navigator.mozMozNetworkStats should exist"); ok('mozNetworkStats' in navigator, "navigator.mozMozNetworkStats should exist");
ok(navigator.mozNetworkStats, "navigator.mozNetworkStats returns an object"); ok(navigator.mozNetworkStats, "navigator.mozNetworkStats returns an object");
netStats = navigator.mozNetworkStats; netStats = navigator.mozNetworkStats;
// Test IDL attributes // Test IDL attributes
ok('availableNetworks' in netStats, ok('connectionTypes' in netStats,
"availableNetworks should be a NetworkStats attribute"); "connectionTypes should be a NetworkStats attribute");
ok(Array.isArray(netStats.availableNetworks) && netStats.availableNetworks.length > 0, ok(Array.isArray(netStats.connectionTypes) && netStats.connectionTypes.length > 0,
"availableNetworks is an array not empty."); "connectionTypes is an array not empty.");
ok('sampleRate' in netStats, ok('sampleRate' in netStats,
"sampleRate should be a NetworkStats attribute"); "sampleRate should be a NetworkStats attribute");
ok(netStats.sampleRate > 0, ok(netStats.sampleRate > 0,
"sampleRate is greater than 0."); "sampleRate is greater than 0.");
ok('maxStorageAge' in netStats, ok('maxStorageSamples' in netStats,
"maxStorageAge should be a NetworkStats attribute"); "maxStorageSamples should be a NetworkStats attribute");
ok(netStats.maxStorageAge > 0, ok(netStats.maxStorageSamples > 0,
"maxStorageAge is greater than 0."); "maxStorageSamples is greater than 0.");
// Test IDL methods // Test IDL methods
next(); next();
@ -60,114 +71,84 @@ function checkDataDates(data, start, end, sampleRate) {
ok(success, "data result has correct dates"); ok(success, "data result has correct dates");
} }
function compareNetworks(networkA, networkB) {
return (networkA.id == networkB.id &&
networkA.type == networkB.type);
}
var req; var req;
var index = -1; var index = -1;
var netStats = null; var netStats = null;
var steps = [ var steps = [
function () { function () {
// Test clearAllStats // Test clearAlldata
req = netStats.clearAllStats(); req = netStats.clearAllData();
req.onsuccess = function () { req.onsuccess = function () {
ok(true, "clearAllStats deleted the database"); ok(true, "clearAllData deleted the database");
next(); next();
}; };
req.onerror = function () { req.onerror = function () {
ok(false, "clearAllStats deleted the database"); ok(false, "clearAllData deleted the database");
} }
}, },
function () { 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 // Prepare get params
var network = netStats.availableNetworks[0]; var type = netStats.connectionTypes[0];
// Get dates // Get dates
var endDate = new Date(); var endDate = new Date();
var startDate = new Date(endDate.getTime() + 1000); var startDate = new Date(endDate.getTime() + 1000);
try { try {
netStats.getSamples(network, startDate, endDate); netStats.getNetworkStats({start: startDate, end: endDate});
} catch(ex) { } 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(); next();
return; return;
} }
ok(false, "getSamples launch exception when start is greater than end"); ok(false, "getNetworkStats launch exceptionwhen start is greater than end");
next(); next();
return; return;
}, },
function () { function () {
// Test if call getSamples with network of type different than // Test if call getNetworkStats with undefined start param launch an exception
// nsIDOMMozNetworkStatsInterface launch an exception
// Prepare get params // Prepare get params
var network = "wifi"; var type = netStats.connectionTypes[0];
var endDate = new Date(); setTimeout(function() {
var startDate = new Date(endDate.getTime() - 1000);
try { try {
netStats.getSamples(network, new Date(), new Date()); netStats.getNetworkStats({end: new Date()});
} catch(ex) { } catch(ex) {
ok(true, "getSamples launch exception if network is not " + ok(true, "getNetworkStats launch exception when start param does not exist");
"a nsIDOMMozNetworkStatsInterface");
next(); next();
return; return;
} }
ok(false, "getSamples launch exception if network is not " + ok(false, "getNetworkStats launch exception when start param does not exist");
"a nsIDOMMozNetworkStatsInterface"); }, 1000);
}, },
function () { 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 // Prepare get params
var network = netStats.availableNetworks[0]; var type = netStats.connectionTypes[0];
var endDate = new Date(); setTimeout(function() {
var startDate = new Date(endDate.getTime() - 1000);
startDate = startDate.toString();
try { try {
netStats.getSamples(network, startDate, endDate); netStats.getNetworkStats({start: new Date()});
} catch(ex) { } 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(); next();
return; 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 () { 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 // Prepare get params
var network = netStats.availableNetworks[0]; var type = netStats.connectionTypes[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 diff = 2; var diff = 2;
// Get samplerate in millis // Get samplerate in millis
var sampleRate = netStats.sampleRate; var sampleRate = netStats.sampleRate * 1000;
// Get date with samplerate's precision // Get date with samplerate's precision
var offset = new Date().getTimezoneOffset() * 60 * 1000; var offset = new Date().getTimezoneOffset() * 60 * 1000;
var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate) 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; var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
// Launch request // Launch request
req = netStats.getSamples(network, startDate, endDate); req = netStats.getNetworkStats({start: startDate, end: endDate, connectionType: type});
req.onsuccess = function () { req.onsuccess = function () {
ok(true, "Get system stats request ok"); ok(true, "Get system stats request ok");
ok(req.result.manifestURL == null, "manifestURL should be null"); 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.start.getTime() == startDate.getTime(), "starts should be equals");
ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals"); ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
var data = req.result.data; var data = req.result.data;
@ -192,16 +173,123 @@ var steps = [
next(); next();
}; };
req.onerror = function () { req.onerror = function () {
ok(false, "Get stats failure!"); ok(false, "Get system stats for a connectionType failure!");
} }
}, },
function () { 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 // Prepare get params
var network = netStats.availableNetworks[0];
var diff = 2; var diff = 2;
// Get samplerate in millis // 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 endDate = new Date();
var startDate = new Date(endDate.getTime() - (sampleRate * diff)); var startDate = new Date(endDate.getTime() - (sampleRate * diff));
// Calculate the number of samples that should be returned based on the // 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; Math.floor(startDate.getTime() / (sampleRate)) * sampleRate) / sampleRate + 1;
// Launch request // Launch request
req = netStats.getSamples(network, startDate, endDate); req = netStats.getNetworkStats({start: startDate, end: endDate, connectionType: type});
req.onsuccess = function () { 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(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.start.getTime() == startDate.getTime(), "starts should be equals");
ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals"); ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
var data = req.result.data; var data = req.result.data;
@ -225,20 +313,40 @@ var steps = [
next(); next();
}; };
req.onerror = function () { req.onerror = function () {
ok(false, "Get stats failure!"); ok(false, "Get system stats for a connectionType failure!");
} }
}, },
function () { function () {
// Test clearStats ok(true, "Get system stats for all connectionTypes and dates not adapted to samplerate");
var network = netStats.availableNetworks[0]; // 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 () { 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(); next();
}; };
req.onerror = function () { req.onerror = function () {
ok(false, "clearStats deleted the database"); ok(false, "Get system stats for all connectionType failure!");
} }
}, },
function () { function () {

Просмотреть файл

@ -12,7 +12,7 @@
<pre id="test"> <pre id="test">
<script type="application/javascript"> <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. // does not work in content.
SpecialPowers.setBoolPref("dom.mozNetworkStats.enabled", true); SpecialPowers.setBoolPref("dom.mozNetworkStats.enabled", true);
@ -22,12 +22,12 @@ ok('mozNetworkStats' in navigator, "navigator.mozNetworkStats should be accessib
var error; var error;
try { try {
navigator.mozNetworkStats.availableNetworks; navigator.mozNetworkStats.connectionTypes;
ok(false, "Accessing navigator.mozNetworkStats.availableNetworks should have thrown!"); ok(false, "Accessing navigator.mozNetworkStats.connectionTypes should have thrown!");
} catch (ex) { } catch (ex) {
error = ex; error = ex;
} }
ok(error, "Got an exception accessing navigator.mozNetworkStats.availableNetworks"); ok(error, "Got an exception accessing navigator.mozNetworkStats.connectionTypes");
</script> </script>
</pre> </pre>

Просмотреть файл

@ -5,7 +5,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/NetworkStatsDB.jsm"); Cu.import("resource://gre/modules/NetworkStatsDB.jsm");
const netStatsDb = new NetworkStatsDB(); const netStatsDb = new NetworkStatsDB(this);
function filterTimestamp(date) { function filterTimestamp(date) {
var sampleRate = netStatsDb.sampleRate; var sampleRate = netStatsDb.sampleRate;
@ -13,15 +13,6 @@ function filterTimestamp(date) {
return Math.floor((date.getTime() - offset) / sampleRate) * sampleRate; 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() { add_test(function test_sampleRate() {
var sampleRate = netStatsDb.sampleRate; var sampleRate = netStatsDb.sampleRate;
do_check_true(sampleRate > 0); do_check_true(sampleRate > 0);
@ -98,26 +89,15 @@ add_test(function test_fillResultSamples_noEmptyData() {
}); });
add_test(function test_clear() { add_test(function test_clear() {
var networks = getNetworks(); netStatsDb.clear(function (error, result) {
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) {
do_check_eq(error, null); do_check_eq(error, null);
run_next_test(); run_next_test();
}); });
}); });
add_test(function test_internalSaveStats_singleSample() { add_test(function test_internalSaveStats_singleSample() {
var networks = getNetworks();
var stats = {appId: 0, var stats = {appId: 0,
network: [networks[0].id, networks[0].type], connectionType: "wifi",
timestamp: Date.now(), timestamp: Date.now(),
rxBytes: 0, rxBytes: 0,
txBytes: 0, txBytes: 0,
@ -133,7 +113,7 @@ add_test(function test_internalSaveStats_singleSample() {
do_check_eq(error, null); do_check_eq(error, null);
do_check_eq(result.length, 1); do_check_eq(result.length, 1);
do_check_eq(result[0].appId, stats.appId); 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].timestamp, stats.timestamp);
do_check_eq(result[0].rxBytes, stats.rxBytes); do_check_eq(result[0].rxBytes, stats.rxBytes);
do_check_eq(result[0].txBytes, stats.txBytes); do_check_eq(result[0].txBytes, stats.txBytes);
@ -145,18 +125,14 @@ add_test(function test_internalSaveStats_singleSample() {
}); });
add_test(function test_internalSaveStats_arraySamples() { add_test(function test_internalSaveStats_arraySamples() {
var networks = getNetworks(); netStatsDb.clear(function (error, result) {
netStatsDb.clearStats(networks, function (error, result) {
do_check_eq(error, null); do_check_eq(error, null);
var network = [networks[0].id, networks[0].type];
var samples = 2; var samples = 2;
var stats = []; var stats = [];
for (var i = 0; i < samples; i++) { for (var i = 0; i < samples; i++) {
stats.push({appId: 0, stats.push({appId: 0,
network: network, connectionType: "wifi",
timestamp: Date.now() + (10 * i), timestamp: Date.now() + (10 * i),
rxBytes: 0, rxBytes: 0,
txBytes: 0, txBytes: 0,
@ -171,16 +147,12 @@ add_test(function test_internalSaveStats_arraySamples() {
netStatsDb.logAllRecords(function(error, result) { netStatsDb.logAllRecords(function(error, result) {
do_check_eq(error, null); 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); do_check_eq(result.length, samples);
var success = true; var success = true;
for (var i = 1; i < samples; i++) { for (var i = 0; i < samples; i++) {
if (result[i].appId != stats[i].appId || 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].timestamp != stats[i].timestamp ||
result[i].rxBytes != stats[i].rxBytes || result[i].rxBytes != stats[i].rxBytes ||
result[i].txBytes != stats[i].txBytes || result[i].txBytes != stats[i].txBytes ||
@ -198,23 +170,20 @@ add_test(function test_internalSaveStats_arraySamples() {
}); });
add_test(function test_internalRemoveOldStats() { add_test(function test_internalRemoveOldStats() {
var networks = getNetworks(); netStatsDb.clear(function (error, result) {
netStatsDb.clearStats(networks, function (error, result) {
do_check_eq(error, null); do_check_eq(error, null);
var network = [networks[0].id, networks[0].type];
var samples = 10; var samples = 10;
var stats = []; var stats = [];
for (var i = 0; i < samples - 1; i++) { for (var i = 0; i < samples - 1; i++) {
stats.push({appId: 0, stats.push({appId: 0,
network: network, timestamp: Date.now() + (10 * i), connectionType: "wifi", timestamp: Date.now() + (10 * i),
rxBytes: 0, txBytes: 0, rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234}); rxTotalBytes: 1234, txTotalBytes: 1234});
} }
stats.push({appId: 0, stats.push({appId: 0,
network: network, timestamp: Date.now() + (10 * samples), connectionType: "wifi", timestamp: Date.now() + (10 * samples),
rxBytes: 0, txBytes: 0, rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234}); rxTotalBytes: 1234, txTotalBytes: 1234});
@ -222,7 +191,7 @@ add_test(function test_internalRemoveOldStats() {
netStatsDb._saveStats(txn, store, stats); netStatsDb._saveStats(txn, store, stats);
var date = stats[stats.length -1].timestamp var date = stats[stats.length -1].timestamp
+ (netStatsDb.sampleRate * netStatsDb.maxStorageSamples - 1) - 1; + (netStatsDb.sampleRate * netStatsDb.maxStorageSamples - 1) - 1;
netStatsDb._removeOldStats(txn, store, 0, network, date); netStatsDb._removeOldStats(txn, store, 0, "wifi", date);
}, function(error, result) { }, function(error, result) {
do_check_eq(error, null); do_check_eq(error, null);
@ -236,14 +205,14 @@ add_test(function test_internalRemoveOldStats() {
}); });
}); });
function processSamplesDiff(networks, lastStat, newStat, callback) { function processSamplesDiff(lastStat, newStat, callback) {
netStatsDb.clearStats(networks, function (error, result){ netStatsDb.clear(function (error, result){
do_check_eq(error, null); do_check_eq(error, null);
netStatsDb.dbNewTxn("readwrite", function(txn, store) { netStatsDb.dbNewTxn("readwrite", function(txn, store) {
netStatsDb._saveStats(txn, store, lastStat); netStatsDb._saveStats(txn, store, lastStat);
}, function(error, result) { }, function(error, result) {
netStatsDb.dbNewTxn("readwrite", function(txn, store) { 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) { request.onsuccess = function onsuccess(event) {
let cursor = event.target.result; let cursor = event.target.result;
do_check_neq(cursor, null); do_check_neq(cursor, null);
@ -261,26 +230,22 @@ function processSamplesDiff(networks, lastStat, newStat, callback) {
} }
add_test(function test_processSamplesDiffSameSample() { add_test(function test_processSamplesDiffSameSample() {
var networks = getNetworks();
var network = [networks[0].id, networks[0].type];
var sampleRate = netStatsDb.sampleRate; var sampleRate = netStatsDb.sampleRate;
var date = filterTimestamp(new Date()); var date = filterTimestamp(new Date());
var lastStat = {appId: 0, var lastStat = {appId: 0,
network: network, timestamp: date, connectionType: "wifi", timestamp: date,
rxBytes: 0, txBytes: 0, rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234}; rxTotalBytes: 1234, txTotalBytes: 1234};
var newStat = {appId: 0, var newStat = {appId: 0,
network: network, timestamp: date, connectionType: "wifi", timestamp: date,
rxBytes: 0, txBytes: 0, rxBytes: 0, txBytes: 0,
rxTotalBytes: 2234, txTotalBytes: 2234}; 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.length, 1);
do_check_eq(result[0].appId, newStat.appId); 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].timestamp, newStat.timestamp);
do_check_eq(result[0].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes); do_check_eq(result[0].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
do_check_eq(result[0].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes); do_check_eq(result[0].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
@ -291,26 +256,22 @@ add_test(function test_processSamplesDiffSameSample() {
}); });
add_test(function test_processSamplesDiffNextSample() { add_test(function test_processSamplesDiffNextSample() {
var networks = getNetworks();
var network = [networks[0].id, networks[0].type];
var sampleRate = netStatsDb.sampleRate; var sampleRate = netStatsDb.sampleRate;
var date = filterTimestamp(new Date()); var date = filterTimestamp(new Date());
var lastStat = {appId: 0, var lastStat = {appId: 0,
network: network, timestamp: date, connectionType: "wifi", timestamp: date,
rxBytes: 0, txBytes: 0, rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234}; rxTotalBytes: 1234, txTotalBytes: 1234};
var newStat = {appId: 0, var newStat = {appId: 0,
network: network, timestamp: date + sampleRate, connectionType: "wifi", timestamp: date + sampleRate,
rxBytes: 0, txBytes: 0, rxBytes: 0, txBytes: 0,
rxTotalBytes: 500, txTotalBytes: 500}; 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.length, 2);
do_check_eq(result[1].appId, newStat.appId); 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].timestamp, newStat.timestamp);
do_check_eq(result[1].rxBytes, newStat.rxTotalBytes); do_check_eq(result[1].rxBytes, newStat.rxTotalBytes);
do_check_eq(result[1].txBytes, newStat.txTotalBytes); do_check_eq(result[1].txBytes, newStat.txTotalBytes);
@ -321,25 +282,23 @@ add_test(function test_processSamplesDiffNextSample() {
}); });
add_test(function test_processSamplesDiffSamplesLost() { add_test(function test_processSamplesDiffSamplesLost() {
var networks = getNetworks();
var network = [networks[0].id, networks[0].type];
var samples = 5; var samples = 5;
var sampleRate = netStatsDb.sampleRate; var sampleRate = netStatsDb.sampleRate;
var date = filterTimestamp(new Date()); var date = filterTimestamp(new Date());
var lastStat = {appId: 0, var lastStat = {appId: 0,
network: network, timestamp: date, connectionType: "wifi", timestamp: date,
rxBytes: 0, txBytes: 0, rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234}; rxTotalBytes: 1234, txTotalBytes: 1234};
var newStat = {appId: 0, var newStat = {appId: 0,
network: network, timestamp: date + (sampleRate * samples), connectionType: "wifi", timestamp: date + (sampleRate * samples),
rxBytes: 0, txBytes: 0, rxBytes: 0, txBytes: 0,
rxTotalBytes: 2234, txTotalBytes: 2234}; 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.length, samples + 1);
do_check_eq(result[0].appId, newStat.appId); 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].timestamp, newStat.timestamp);
do_check_eq(result[samples].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes); do_check_eq(result[samples].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
do_check_eq(result[samples].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes); do_check_eq(result[samples].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
@ -350,17 +309,13 @@ add_test(function test_processSamplesDiffSamplesLost() {
}); });
add_test(function test_saveStats() { add_test(function test_saveStats() {
var networks = getNetworks();
var network = [networks[0].id, networks[0].type];
var stats = {appId: 0, var stats = {appId: 0,
networkId: networks[0].id, connectionType: "wifi",
networkType: networks[0].type,
date: new Date(), date: new Date(),
rxBytes: 2234, rxBytes: 2234,
txBytes: 2234}; txBytes: 2234};
netStatsDb.clearStats(networks, function (error, result) { netStatsDb.clear(function (error, result) {
do_check_eq(error, null); do_check_eq(error, null);
netStatsDb.saveStats(stats, function(error, result) { netStatsDb.saveStats(stats, function(error, result) {
do_check_eq(error, null); do_check_eq(error, null);
@ -368,7 +323,7 @@ add_test(function test_saveStats() {
do_check_eq(error, null); do_check_eq(error, null);
do_check_eq(result.length, 1); do_check_eq(result.length, 1);
do_check_eq(result[0].appId, stats.appId); 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); let timestamp = filterTimestamp(stats.date);
do_check_eq(result[0].timestamp, timestamp); do_check_eq(result[0].timestamp, timestamp);
do_check_eq(result[0].rxBytes, 0); do_check_eq(result[0].rxBytes, 0);
@ -382,44 +337,35 @@ add_test(function test_saveStats() {
}); });
add_test(function test_saveAppStats() { add_test(function test_saveAppStats() {
var networks = getNetworks();
var network = [networks[0].id, networks[0].type];
var stats = {appId: 1, var stats = {appId: 1,
networkId: networks[0].id, connectionType: "wifi",
networkType: networks[0].type,
date: new Date(), date: new Date(),
rxBytes: 2234, rxBytes: 2234,
txBytes: 2234}; txBytes: 2234};
netStatsDb.clearStats(networks, function (error, result) { netStatsDb.clear(function (error, result) {
do_check_eq(error, null); do_check_eq(error, null);
netStatsDb.saveStats(stats, function(error, result) { netStatsDb.saveStats(stats, function(error, result) {
do_check_eq(error, null); do_check_eq(error, null);
netStatsDb.logAllRecords(function(error, result) { netStatsDb.logAllRecords(function(error, result) {
do_check_eq(error, null); do_check_eq(error, null);
// The clear function clears all records of the datbase but do_check_eq(result.length, 1);
// inserts a new element for each [appId, connectionId, connectionType] do_check_eq(result[0].appId, stats.appId);
// record to keep the track of rxTotalBytes / txTotalBytes. do_check_eq(result[0].connectionType, stats.connectionType);
// 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));
let timestamp = filterTimestamp(stats.date); let timestamp = filterTimestamp(stats.date);
do_check_eq(result[1].timestamp, timestamp); do_check_eq(result[0].timestamp, timestamp);
do_check_eq(result[1].rxBytes, stats.rxBytes); do_check_eq(result[0].rxBytes, stats.rxBytes);
do_check_eq(result[1].txBytes, stats.txBytes); do_check_eq(result[0].txBytes, stats.txBytes);
do_check_eq(result[1].rxTotalBytes, 0); do_check_eq(result[0].rxTotalBytes, 0);
do_check_eq(result[1].txTotalBytes, 0); do_check_eq(result[0].txTotalBytes, 0);
run_next_test(); run_next_test();
}); });
}); });
}); });
}); });
function prepareFind(network, stats, callback) { function prepareFind(stats, callback) {
netStatsDb.clearStats(network, function (error, result) { netStatsDb.clear(function (error, result) {
do_check_eq(error, null); do_check_eq(error, null);
netStatsDb.dbNewTxn("readwrite", function(txn, store) { netStatsDb.dbNewTxn("readwrite", function(txn, store) {
netStatsDb._saveStats(txn, store, stats); netStatsDb._saveStats(txn, store, stats);
@ -430,11 +376,6 @@ function prepareFind(network, stats, callback) {
} }
add_test(function test_find () { 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 samples = 5;
var sampleRate = netStatsDb.sampleRate; var sampleRate = netStatsDb.sampleRate;
var start = Date.now(); var start = Date.now();
@ -443,39 +384,46 @@ add_test(function test_find () {
start = new Date(start - sampleRate); start = new Date(start - sampleRate);
var stats = []; var stats = [];
for (var i = 0; i < samples; i++) { for (var i = 0; i < samples; i++) {
stats.push({ appId: appId, stats.push({appId: 0,
network: networkWifi, timestamp: saveDate + (sampleRate * i), connectionType: "wifi", timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10, rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0}); rxTotalBytes: 0, txTotalBytes: 0});
stats.push({ appId: appId, stats.push({appId: 0,
network: networkMobile, timestamp: saveDate + (sampleRate * i), connectionType: "mobile", timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10, rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0}); rxTotalBytes: 0, txTotalBytes: 0});
} }
prepareFind(networks[0], stats, function(error, result) { prepareFind(stats, function(error, result) {
do_check_eq(error, null); do_check_eq(error, null);
netStatsDb.find(function (error, result) { netStatsDb.find(function (error, result) {
do_check_eq(error, null); do_check_eq(error, null);
do_check_eq(result.network.id, networks[0].id); do_check_eq(result.connectionType, "wifi");
do_check_eq(result.network.type, networks[0].type);
do_check_eq(result.start.getTime(), start.getTime()); do_check_eq(result.start.getTime(), start.getTime());
do_check_eq(result.end.getTime(), end.getTime()); do_check_eq(result.end.getTime(), end.getTime());
do_check_eq(result.data.length, samples + 1); do_check_eq(result.data.length, samples + 1);
do_check_eq(result.data[0].rxBytes, null); do_check_eq(result.data[0].rxBytes, null);
do_check_eq(result.data[1].rxBytes, 0); do_check_eq(result.data[1].rxBytes, 0);
do_check_eq(result.data[samples].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(); 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 () { 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 samples = 5;
var sampleRate = netStatsDb.sampleRate; var sampleRate = netStatsDb.sampleRate;
var start = Date.now(); var start = Date.now();
@ -485,62 +433,68 @@ add_test(function test_findAppStats () {
var stats = []; var stats = [];
for (var i = 0; i < samples; i++) { for (var i = 0; i < samples; i++) {
stats.push({appId: 1, stats.push({appId: 1,
network: networkWifi, timestamp: saveDate + (sampleRate * i), connectionType: "wifi", timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10, rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0}); rxTotalBytes: 0, txTotalBytes: 0});
stats.push({appId: 1, stats.push({appId: 1,
network: networkMobile, timestamp: saveDate + (sampleRate * i), connectionType: "mobile", timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10, rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0}); rxTotalBytes: 0, txTotalBytes: 0});
} }
prepareFind(networks[0], stats, function(error, result) { prepareFind(stats, function(error, result) {
do_check_eq(error, null); do_check_eq(error, null);
netStatsDb.find(function (error, result) { netStatsDb.find(function (error, result) {
do_check_eq(error, null); do_check_eq(error, null);
do_check_eq(result.network.id, networks[0].id); do_check_eq(result.connectionType, "wifi");
do_check_eq(result.network.type, networks[0].type);
do_check_eq(result.start.getTime(), start.getTime()); do_check_eq(result.start.getTime(), start.getTime());
do_check_eq(result.end.getTime(), end.getTime()); do_check_eq(result.end.getTime(), end.getTime());
do_check_eq(result.data.length, samples + 1); do_check_eq(result.data.length, samples + 1);
do_check_eq(result.data[0].rxBytes, null); do_check_eq(result.data[0].rxBytes, null);
do_check_eq(result.data[1].rxBytes, 0); do_check_eq(result.data[1].rxBytes, 0);
do_check_eq(result.data[samples].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(); 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 () { 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 saveDate = filterTimestamp(new Date());
var cached = Object.create(null); var cached = Object.create(null);
cached['1wifi'] = { appId: 1, date: new Date(), cached['1wifi'] = {appId: 1,
networkId: networkWifi.id, networkType: networkWifi.type, connectionType: "wifi", date: new Date(),
rxBytes: 0, txBytes: 10}; rxBytes: 0, txBytes: 10};
cached['1mobile'] = { appId: 1, date: new Date(), cached['1mobile'] = {appId: 1,
networkId: networkMobile.id, networkType: networkMobile.type, connectionType: "mobile", date: new Date(),
rxBytes: 0, txBytes: 10}; rxBytes: 0, txBytes: 10};
cached['2wifi'] = { appId: 2, date: new Date(), cached['2wifi'] = {appId: 2,
networkId: networkWifi.id, networkType: networkWifi.type, connectionType: "wifi", date: new Date(),
rxBytes: 0, txBytes: 10}; rxBytes: 0, txBytes: 10};
cached['2mobile'] = { appId: 2, date: new Date(), cached['2mobile'] = {appId: 2,
networkId: networkMobile.id, networkType: networkMobile.type, connectionType: "mobile", date: new Date(),
rxBytes: 0, txBytes: 10}; rxBytes: 0, txBytes: 10};
let keys = Object.keys(cached); let keys = Object.keys(cached);
let index = 0; let index = 0;
networks.push(networkMobile); netStatsDb.clear(function (error, result) {
netStatsDb.clearStats(networks, function (error, result) {
do_check_eq(error, null); do_check_eq(error, null);
netStatsDb.saveStats(cached[keys[index]], netStatsDb.saveStats(cached[keys[index]],
function callback(error, result) { function callback(error, result) {
@ -548,17 +502,10 @@ add_test(function test_saveMultipleAppStats () {
if (index == keys.length - 1) { if (index == keys.length - 1) {
netStatsDb.logAllRecords(function(error, result) { 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(error, null);
do_check_eq(result.length, 4); do_check_eq(result.length, 4);
do_check_eq(result[0].appId, 1); 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].rxBytes, 0);
do_check_eq(result[0].txBytes, 10); do_check_eq(result[0].txBytes, 10);
run_next_test(); run_next_test();

Просмотреть файл

@ -4,56 +4,48 @@
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
add_test(function test_clearDB() { add_test(function test_clearDB() {
var networks = NetworkStatsService.availableNetworks(); NetworkStatsService._db.clear(function onDBCleared(error, result) {
NetworkStatsService._db.clearStats(networks, function onDBCleared(error, result) {
do_check_eq(result, null); do_check_eq(result, null);
run_next_test(); run_next_test();
}); });
}); });
function getNetworkId() {
var network = (NetworkStatsService.availableNetworks())[0];
return NetworkStatsService.getNetworkId(network.id, network.type);
}
add_test(function test_networkStatsAvailable_ok() { add_test(function test_networkStatsAvailable_ok() {
var netId = getNetworkId();
NetworkStatsService.networkStatsAvailable(function (success, msg) { NetworkStatsService.networkStatsAvailable(function (success, msg) {
do_check_eq(success, true); do_check_eq(success, true);
run_next_test(); 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() { add_test(function test_networkStatsAvailable_failure() {
var netId = getNetworkId();
NetworkStatsService.networkStatsAvailable(function (success, msg) { NetworkStatsService.networkStatsAvailable(function (success, msg) {
do_check_eq(success, false); do_check_eq(success, false);
run_next_test(); 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) { NetworkStatsService.update(-1, function (success, msg) {
do_check_eq(success, false); do_check_eq(success, false);
do_check_eq(msg, "Invalid network -1"); do_check_eq(msg, "Invalid network type -1");
run_next_test(); run_next_test();
}); });
}); });
add_test(function test_update() { add_test(function test_update() {
var netId = getNetworkId(); NetworkStatsService.update(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, function (success, msg) {
NetworkStatsService.update(netId, function (success, msg) {
do_check_eq(success, true); do_check_eq(success, true);
run_next_test(); run_next_test();
}); });
}); });
add_test(function test_updateQueueIndex() { add_test(function test_updateQueueIndex() {
NetworkStatsService.updateQueue = [{netId: 0, callbacks: null}, NetworkStatsService.updateQueue = [{type: 0, callbacks: null},
{netId: 1, callbacks: null}, {type: 1, callbacks: null},
{netId: 2, callbacks: null}, {type: 2, callbacks: null},
{netId: 3, callbacks: null}, {type: 3, callbacks: null},
{netId: 4, callbacks: null}]; {type: 4, callbacks: null}];
var index = NetworkStatsService.updateQueueIndex(3); var index = NetworkStatsService.updateQueueIndex(3);
do_check_eq(index, 3); do_check_eq(index, 3);
index = NetworkStatsService.updateQueueIndex(10); index = NetworkStatsService.updateQueueIndex(10);
@ -71,8 +63,7 @@ add_test(function test_updateAllStats() {
}); });
add_test(function test_updateStats_ok() { add_test(function test_updateStats_ok() {
var netId = getNetworkId(); NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, function(success, msg){
NetworkStatsService.updateStats(netId, function(success, msg){
do_check_eq(success, true); do_check_eq(success, true);
run_next_test(); run_next_test();
}); });
@ -86,20 +77,15 @@ add_test(function test_updateStats_failure() {
}); });
add_test(function test_queue() { 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 // to enable netd async requests
var network = {id: "1234", type: Ci.nsIDOMMozNetworkStatsManager.MOBILE}; NetworkStatsService._connectionTypes[Ci.nsINetworkInterface.NETWORK_TYPE_WIFI]
var netId1 = NetworkStatsService.getNetworkId(network.id, network.type); .network.name = 'wlan0';
NetworkStatsService._networks[netId1] = { network: network, NetworkStatsService._connectionTypes[Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE]
interfaceName: "net1" }; .network.name = 'rmnet0';
network = {id: "5678", type: Ci.nsIDOMMozNetworkStatsManager.MOBILE}; NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI);
var netId2 = NetworkStatsService.getNetworkId(network.id, network.type); NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE);
NetworkStatsService._networks[netId2] = { network: network,
interfaceName: "net2" };
NetworkStatsService.updateStats(netId1);
NetworkStatsService.updateStats(netId2);
do_check_eq(NetworkStatsService.updateQueue.length, 2); do_check_eq(NetworkStatsService.updateQueue.length, 2);
do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 1); do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 1);
@ -107,8 +93,8 @@ add_test(function test_queue() {
return; return;
}; };
NetworkStatsService.updateStats(netId1, callback); NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, callback);
NetworkStatsService.updateStats(netId2, callback); NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, callback);
do_check_eq(NetworkStatsService.updateQueue.length, 2); do_check_eq(NetworkStatsService.updateQueue.length, 2);
do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 2); do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 2);

Просмотреть файл

@ -9,66 +9,33 @@ XPCOMUtils.defineLazyServiceGetter(this, "nssProxy",
"@mozilla.org/networkstatsServiceProxy;1", "@mozilla.org/networkstatsServiceProxy;1",
"nsINetworkStatsServiceProxy"); "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() { add_test(function test_saveAppStats() {
var cachedAppStats = NetworkStatsService.cachedAppStats; var cachedAppStats = NetworkStatsService.cachedAppStats;
var timestamp = NetworkStatsService.cachedAppStatsDate.getTime(); var timestamp = NetworkStatsService.cachedAppStatsDate.getTime();
var samples = 5; 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); do_check_eq(Object.keys(cachedAppStats).length, 0);
for (var i = 0; i < samples; i++) { 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 key1 = 1 + 'wifi';
var key2 = 1 + mobileNetId; var key2 = 1 + 'mobile';
do_check_eq(Object.keys(cachedAppStats).length, 2); do_check_eq(Object.keys(cachedAppStats).length, 2);
do_check_eq(cachedAppStats[key1].appId, 1); do_check_eq(cachedAppStats[key1].appId, 1);
do_check_eq(cachedAppStats[key1].networkId, wifi.id); do_check_eq(cachedAppStats[key1].connectionType, 'wifi');
do_check_eq(cachedAppStats[key1].networkType, wifi.type);
do_check_eq(new Date(cachedAppStats[key1].date).getTime() / 1000, do_check_eq(new Date(cachedAppStats[key1].date).getTime() / 1000,
Math.floor(timestamp / 1000)); Math.floor(timestamp / 1000));
do_check_eq(cachedAppStats[key1].rxBytes, 50); do_check_eq(cachedAppStats[key1].rxBytes, 50);
do_check_eq(cachedAppStats[key1].txBytes, 100); do_check_eq(cachedAppStats[key1].txBytes, 100);
do_check_eq(cachedAppStats[key2].appId, 1); do_check_eq(cachedAppStats[key2].appId, 1);
do_check_eq(cachedAppStats[key2].networkId, mobile.id); do_check_eq(cachedAppStats[key2].connectionType, 'mobile');
do_check_eq(cachedAppStats[key2].networkType, mobile.type);
do_check_eq(new Date(cachedAppStats[key2].date).getTime() / 1000, do_check_eq(new Date(cachedAppStats[key2].date).getTime() / 1000,
Math.floor(timestamp / 1000)); Math.floor(timestamp / 1000));
do_check_eq(cachedAppStats[key2].rxBytes, 50); do_check_eq(cachedAppStats[key2].rxBytes, 50);
@ -80,11 +47,7 @@ add_test(function test_saveAppStats() {
add_test(function test_saveAppStatsWithDifferentDates() { add_test(function test_saveAppStatsWithDifferentDates() {
var today = NetworkStatsService.cachedAppStatsDate; var today = NetworkStatsService.cachedAppStatsDate;
var tomorrow = new Date(today.getTime() + (24 * 60 * 60 * 1000)); var tomorrow = new Date(today.getTime() + (24 * 60 * 60 * 1000));
var key = 1 + 'wifi';
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);
NetworkStatsService.updateCachedAppStats( NetworkStatsService.updateCachedAppStats(
function (success, msg) { function (success, msg) {
@ -92,20 +55,21 @@ add_test(function test_saveAppStatsWithDifferentDates() {
do_check_eq(Object.keys(NetworkStatsService.cachedAppStats).length, 0); 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 = { var saveAppStatsCb = {
notify: function notify(success, message) { notify: function notify(success, message) {
do_check_eq(success, true); do_check_eq(success, true);
var cachedAppStats = NetworkStatsService.cachedAppStats; 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(Object.keys(cachedAppStats).length, 1);
do_check_eq(cachedAppStats[key].appId, 2); do_check_eq(cachedAppStats[key].appId, 2);
do_check_eq(cachedAppStats[key].networkId, mobile.id); do_check_eq(cachedAppStats[key].connectionType, 'mobile');
do_check_eq(cachedAppStats[key].networkType, mobile.type);
do_check_eq(new Date(cachedAppStats[key].date).getTime() / 1000, do_check_eq(new Date(cachedAppStats[key].date).getTime() / 1000,
Math.floor(tomorrow.getTime() / 1000)); Math.floor(tomorrow.getTime() / 1000));
do_check_eq(cachedAppStats[key].rxBytes, 30); 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() { add_test(function test_saveAppStatsWithMaxCachedTraffic() {
var timestamp = NetworkStatsService.cachedAppStatsDate.getTime(); var timestamp = NetworkStatsService.cachedAppStatsDate.getTime();
var maxtraffic = NetworkStatsService.maxCachedTraffic; var maxtraffic = NetworkStatsService.maxCachedTraffic;
var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
NetworkStatsService.updateCachedAppStats( NetworkStatsService.updateCachedAppStats(
function (success, msg) { function (success, msg) {
@ -132,11 +96,13 @@ add_test(function test_saveAppStatsWithMaxCachedTraffic() {
var cachedAppStats = NetworkStatsService.cachedAppStats; var cachedAppStats = NetworkStatsService.cachedAppStats;
do_check_eq(Object.keys(cachedAppStats).length, 0); 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); 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); do_check_eq(Object.keys(cachedAppStats).length, 0);
@ -149,9 +115,5 @@ function run_test() {
Cu.import("resource://gre/modules/NetworkStatsService.jsm"); 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(); run_next_test();
} }

Просмотреть файл

@ -77,7 +77,7 @@ class nsGeolocationRequest
void Shutdown(); void Shutdown();
void SendLocation(nsIDOMGeoPosition* location); void SendLocation(nsIDOMGeoPosition* location);
bool WantsHighAccuracy() {return !mShutdown && mOptions && mOptions->mEnableHighAccuracy;} bool WantsHighAccuracy() {return mOptions && mOptions->mEnableHighAccuracy;}
void SetTimeoutTimer(); void SetTimeoutTimer();
nsIPrincipal* GetPrincipal(); nsIPrincipal* GetPrincipal();
@ -445,7 +445,7 @@ nsGeolocationRequest::Allow()
maximumAge = mOptions->mMaximumAge; maximumAge = mOptions->mMaximumAge;
} }
} }
gs->UpdateAccuracy(WantsHighAccuracy()); gs->SetHigherAccuracy(mOptions && mOptions->mEnableHighAccuracy);
bool canUseCache = lastPosition && maximumAge > 0 && bool canUseCache = lastPosition && maximumAge > 0 &&
(PRTime(PR_Now() / PR_USEC_PER_MSEC) - maximumAge <= (PRTime(PR_Now() / PR_USEC_PER_MSEC) - maximumAge <=
@ -585,12 +585,12 @@ nsGeolocationRequest::Shutdown()
mTimeoutTimer = nullptr; mTimeoutTimer = nullptr;
} }
// If there are no other high accuracy requests, the geolocation service will // This should happen last, to ensure that this request isn't taken into consideration
// notify the provider to switch to the default accuracy. // when deciding whether existing requests still require high accuracy.
if (mOptions && mOptions->mEnableHighAccuracy) { if (mOptions && mOptions->mEnableHighAccuracy) {
nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService(); nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
if (gs) { if (gs) {
gs->UpdateAccuracy(); gs->SetHigherAccuracy(false);
} }
} }
} }
@ -901,9 +901,9 @@ nsGeolocationService::HighAccuracyRequested()
} }
void void
nsGeolocationService::UpdateAccuracy(bool aForceHigh) nsGeolocationService::SetHigherAccuracy(bool aEnable)
{ {
bool highRequired = aForceHigh || HighAccuracyRequested(); bool highRequired = aEnable || HighAccuracyRequested();
if (XRE_GetProcessType() == GeckoProcessType_Content) { if (XRE_GetProcessType() == GeckoProcessType_Content) {
ContentChild* cpc = ContentChild::GetSingleton(); ContentChild* cpc = ContentChild::GetSingleton();
@ -1061,7 +1061,6 @@ Geolocation::Shutdown()
if (mService) { if (mService) {
mService->RemoveLocator(this); mService->RemoveLocator(this);
mService->UpdateAccuracy();
} }
mService = nullptr; mService = nullptr;

Просмотреть файл

@ -85,8 +85,8 @@ public:
// create, or reinitalize the callback timer // create, or reinitalize the callback timer
void SetDisconnectTimer(); void SetDisconnectTimer();
// Update the accuracy and notify the provider if changed // request higher accuracy, if possible
void UpdateAccuracy(bool aForceHigh = false); void SetHigherAccuracy(bool aEnable);
bool HighAccuracyRequested(); bool HighAccuracyRequested();
private: private:

Просмотреть файл

@ -73,9 +73,6 @@ function WifiGeoPositionProvider() {
this.timer = null; this.timer = null;
this.hasSeenWiFi = false; this.hasSeenWiFi = false;
this.started = false; this.started = false;
// this is only used when logging is enabled, to debug interactions with the
// geolocation service
this.highAccuracy = false;
} }
WifiGeoPositionProvider.prototype = { WifiGeoPositionProvider.prototype = {
@ -135,12 +132,10 @@ WifiGeoPositionProvider.prototype = {
}, },
setHighAccuracy: function(enable) { setHighAccuracy: function(enable) {
this.highAccuracy = enable;
LOG("setting highAccuracy to " + (this.highAccuracy?"TRUE":"FALSE"));
}, },
onChange: function(accessPoints) { onChange: function(accessPoints) {
LOG("onChange called, highAccuracy = " + (this.highAccuracy?"TRUE":"FALSE")); LOG("onChange called");
this.hasSeenWiFi = true; this.hasSeenWiFi = true;
let url = Services.urlFormatter.formatURLPref("geo.wifi.uri"); 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" skip-if = os == "android"
[test_geolocation_timeout_wrap.js] [test_geolocation_timeout_wrap.js]
skip-if = os == "mac" || os == "android" 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"); "reference div should disappear when it becomes display:none");
} }
function myOnStopFrame(aRequest) { function myOnStopFrame() {
gOnStopFrameCounter++; gOnStopFrameCounter++;
ok(true, "myOnStopFrame called"); ok(true, "myOnStopFrame called");
let currentSnapshot = snapshotWindow(window, false); let currentSnapshot = snapshotWindow(window, false);
@ -64,7 +64,8 @@ function myOnStopFrame(aRequest) {
"at call #" + gOnStopFrameCounter + " to onStopFrame"); "at call #" + gOnStopFrameCounter + " to onStopFrame");
cleanUpAndFinish(); cleanUpAndFinish();
} }
setTimeout(function() { myOnStopFrame(0, 0); }, 1000); else
setTimeout(myOnStopFrame, 1);
} }
function failTest() { function failTest() {
@ -80,8 +81,6 @@ function cleanUpAndFinish() {
if (gIsTestFinished) { if (gIsTestFinished) {
return; return;
} }
let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
imgLoadingContent.removeObserver(gMyDecoderObserver);
SimpleTest.finish(); SimpleTest.finish();
gIsTestFinished = true; gIsTestFinished = true;
} }
@ -89,19 +88,12 @@ function cleanUpAndFinish() {
function main() { function main() {
takeReferenceSnapshot(); 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 // We want to test the cold loading behavior, so clear cache in case an
// earlier test got our image in there already. // earlier test got our image in there already.
clearImageCache(); clearImageCache();
setTimeout(myOnStopFrame, 1);
// kick off image-loading! myOnStopFrame handles the rest. // kick off image-loading! myOnStopFrame handles the rest.
gImg.setAttribute("src", "lime-anim-100x100.svg"); gImg.setAttribute("src", "lime-anim-100x100.svg");

Просмотреть файл

@ -50,11 +50,7 @@ struct jsid;
typedef ptrdiff_t jsid; typedef ptrdiff_t jsid;
#endif #endif
#ifdef WIN32 typedef char16_t jschar;
typedef wchar_t jschar;
#else
typedef uint16_t jschar;
#endif
namespace JS { namespace JS {

Просмотреть файл

@ -4,6 +4,16 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # 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 TOPLEVEL_BUILD := 1
run_for_side_effects := $(shell echo "MAKE: $(MAKE)") run_for_side_effects := $(shell echo "MAKE: $(MAKE)")

Просмотреть файл

@ -32,6 +32,7 @@
#include "assembler/wtf/Assertions.h" #include "assembler/wtf/Assertions.h"
#include "assembler/wtf/VMTags.h" #include "assembler/wtf/VMTags.h"
#include "js/Utility.h"
namespace JSC { namespace JSC {
@ -42,7 +43,14 @@ size_t ExecutableAllocator::determinePageSize()
ExecutablePool::Allocation ExecutableAllocator::systemAlloc(size_t n) 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) if (allocation == MAP_FAILED)
allocation = NULL; allocation = NULL;
ExecutablePool::Allocation alloc = { reinterpret_cast<char*>(allocation), n }; 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't handle pathes with spaces properly in mddepend.pl, but
# we can assume that anything in a path with spaces is a system # we can assume that anything in a path with spaces is a system
# header and throw it away. # header and throw it away.
dep = normcase(dep)
if ' ' not in dep: if ' ' not in dep:
rule.add_dependencies([normcase(dep)]) rule.add_dependencies([dep])
else: else:
# Make sure we preserve the relevant output from cl. mozprocess # Make sure we preserve the relevant output from cl. mozprocess
# swallows the newline delimiter, so we need to re-add it. # swallows the newline delimiter, so we need to re-add it.

Просмотреть файл

@ -632,7 +632,7 @@ endif
PWD := $(CURDIR) PWD := $(CURDIR)
endif 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 # 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 # 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. # of single-line commands, we continue to provide INSTALL for general use.
@ -691,7 +691,7 @@ else
IS_LANGUAGE_REPACK = 1 IS_LANGUAGE_REPACK = 1
endif 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 ifdef relativesrcdir
LOCALE_SRCDIR ?= $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir)) LOCALE_SRCDIR ?= $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir))
@ -742,7 +742,7 @@ ifdef MOZ_DEBUG
JAVAC_FLAGS += -g JAVAC_FLAGS += -g
endif 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 is the subdirectory where dependency files are stored
MDDEPDIR := .deps MDDEPDIR := .deps

Просмотреть файл

@ -16,7 +16,7 @@ $(error Do not include functions.mk twice!)
endif endif
INCLUDED_FUNCTIONS_MK = 1 INCLUDED_FUNCTIONS_MK = 1
core_abspath = $(if $(findstring :,$(1)),$(1),$(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1))) core_abspath = $(error core_abspath is unsupported, use $$(abspath) instead)
core_realpath = $(if $(realpath $(1)),$(realpath $(1)),$(call core_abspath,$(1))) 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) AC_DEFINE(XP_UNIX)
fi fi
AC_ARG_ENABLE(threadsafe, JS_THREADSAFE=1
[ --enable-threadsafe Enable support for multiple threads.], MOZ_ARG_DISABLE_BOOL(threadsafe,
[if test "x$enableval" = "xyes"; then [ --disable-threadsafe Disable support for multiple threads.],
JS_THREADSAFE= )
if test -n "$JS_THREADSAFE"; then
AC_DEFINE(JS_THREADSAFE) AC_DEFINE(JS_THREADSAFE)
fi],) fi
if test "$MOZ_DEBUG"; then if test "$MOZ_DEBUG"; then
AC_DEFINE(MOZ_REFLOW_PERF) AC_DEFINE(MOZ_REFLOW_PERF)

Просмотреть файл

@ -253,8 +253,8 @@ assertEq(f(-1,false), 0);
assertEq(f(-5,false), 1); 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 (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; 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); 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(4088, 2.5), 2);
assertEq(f(4096, 3.8), 0); 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 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)); var f = asmLink(code, this, null, new ArrayBuffer(4096));
assertEq(f(0, 1.3), 1.3); assertEq(f(0, 1.3), 1.3);

Просмотреть файл

@ -3,6 +3,8 @@
gcparam("maxBytes", gcparam("gcBytes") + 1024); gcparam("maxBytes", gcparam("gcBytes") + 1024);
test(); test();
function test() { function test() {
var upvar = "";
function f() { upvar += ""; }
test(); test();
eval(''); eval('');
} }

Просмотреть файл

@ -5,6 +5,8 @@ evaluate("gcparam(\"maxBytes\", gcparam(\"gcBytes\") + 4*1024);");
evaluate("\ evaluate("\
function testDontEnum(F) { \ function testDontEnum(F) { \
function test() {\ function test() {\
var upvar = \"\";\
function f() { upvar += \"\"; }\
typeof (new test(\"1\")) != 'function'\ typeof (new test(\"1\")) != 'function'\
}\ }\
test();\ 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, Signed,
Unsigned, Unsigned,
Intish, Intish,
Void, Void
Unknown
}; };
private: private:
@ -414,7 +413,7 @@ class Type
} }
bool isIntish() const { bool isIntish() const {
return isInt() || which_ == Intish || which_ == Unknown; return isInt() || which_ == Intish;
} }
bool isDouble() const { bool isDouble() const {
@ -422,7 +421,7 @@ class Type
} }
bool isDoublish() const { bool isDoublish() const {
return isDouble() || which_ == Doublish || which_ == Unknown; return isDouble() || which_ == Doublish;
} }
bool isVoid() const { bool isVoid() const {
@ -449,7 +448,6 @@ class Type
case Intish: case Intish:
return MIRType_Int32; return MIRType_Int32;
case Void: case Void:
case Unknown:
return MIRType_None; return MIRType_None;
} }
MOZ_ASSUME_UNREACHABLE("Invalid Type"); MOZ_ASSUME_UNREACHABLE("Invalid Type");
@ -465,7 +463,6 @@ class Type
case Unsigned: return "unsigned"; case Unsigned: return "unsigned";
case Intish: return "intish"; case Intish: return "intish";
case Void: return "void"; case Void: return "void";
case Unknown: return "unknown";
} }
MOZ_ASSUME_UNREACHABLE("Invalid Type"); MOZ_ASSUME_UNREACHABLE("Invalid Type");
} }
@ -3947,14 +3944,14 @@ CheckCoerceToInt(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *
if (!CheckExpr(f, operand, &operandDef, &operandType)) if (!CheckExpr(f, operand, &operandDef, &operandType))
return false; return false;
if (operandType.isDouble()) { if (operandType.isDoublish()) {
*def = f.unary<MTruncateToInt32>(operandDef); *def = f.unary<MTruncateToInt32>(operandDef);
*type = Type::Signed; *type = Type::Signed;
return true; return true;
} }
if (!operandType.isIntish()) 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; *def = operandDef;
*type = Type::Signed; *type = Type::Signed;
@ -4160,29 +4157,20 @@ CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *typ
if (numAddOrSub > (1<<20)) if (numAddOrSub > (1<<20))
return f.fail(expr, "too many + or - without intervening coercion"); return f.fail(expr, "too many + or - without intervening coercion");
if (expr->isKind(PNK_ADD)) {
if (lhsType.isInt() && rhsType.isInt()) { if (lhsType.isInt() && rhsType.isInt()) {
*def = f.binary<MAdd>(lhsDef, rhsDef, MIRType_Int32); *def = expr->isKind(PNK_ADD)
*type = Type::Intish; ? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Int32)
} else if (lhsType.isDouble() && rhsType.isDouble()) { : f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32);
*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);
*type = Type::Intish; *type = Type::Intish;
} else if (lhsType.isDoublish() && rhsType.isDoublish()) { } 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; *type = Type::Double;
} else { } 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()); lhsType.toChars(), rhsType.toChars());
} }
}
if (numAddOrSubOut) if (numAddOrSubOut)
*numAddOrSubOut = numAddOrSub; *numAddOrSubOut = numAddOrSub;

Просмотреть файл

@ -8845,6 +8845,11 @@ DoInstanceOfFallback(JSContext *cx, ICInstanceOf_Fallback *stub,
RootedObject obj(cx, &rhs.toObject()); 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; bool cond = false;
if (!HasInstance(cx, obj, lhs, &cond)) if (!HasInstance(cx, obj, lhs, &cond))
return false; return false;

Просмотреть файл

@ -6056,15 +6056,17 @@ CodeGenerator::addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Regi
bool bool
CodeGenerator::addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex, CodeGenerator::addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex,
Register temp, FloatRegister tempFloat, ValueOperand index, Register temp, FloatRegister tempFloat, ValueOperand index,
ConstantOrRegister value, bool strict) ConstantOrRegister value, bool strict, bool guardHoles)
{ {
switch (gen->info().executionMode()) { switch (gen->info().executionMode()) {
case SequentialExecution: { 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)); return addCache(ins, allocateCache(cache));
} }
case ParallelExecution: { 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)); return addCache(ins, allocateCache(cache));
} }
default: default:
@ -6218,7 +6220,7 @@ CodeGenerator::visitSetElementCacheV(LSetElementCacheV *ins)
ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetElementCacheV::Value)); ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetElementCacheV::Value));
return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value, return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value,
ins->mir()->strict()); ins->mir()->strict(), ins->mir()->guardHoles());
} }
bool bool
@ -6237,7 +6239,7 @@ CodeGenerator::visitSetElementCacheT(LSetElementCacheT *ins)
value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(tmp)); value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(tmp));
return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value, 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); typedef bool (*SetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, HandleValue);

Просмотреть файл

@ -343,7 +343,7 @@ class CodeGenerator : public CodeGeneratorSpecific
bool needsTypeBarrier); bool needsTypeBarrier);
bool addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex, Register temp, bool addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex, Register temp,
FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value, FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
bool strict); bool strict, bool guardHoles);
bool checkForAbortPar(LInstruction *lir); bool checkForAbortPar(LInstruction *lir);
bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr); bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);

Просмотреть файл

@ -211,16 +211,25 @@ IsPhiObservable(MPhi *phi, Observability observe)
break; break;
} }
// If the Phi is of the |this| value, it must always be observable.
uint32_t slot = phi->slot(); uint32_t slot = phi->slot();
CompileInfo &info = phi->block()->info(); 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; return true;
// If the Phi is one of the formal argument, and we are using an argument // 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. // 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. // 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(); uint32_t first = info.firstArgSlot();
if (first <= slot && slot - first < info.nargs()) { if (first <= slot && slot - first < info.nargs()) {
// If arguments obj aliases formals, then the arg slots will never be used. // 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; 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. // 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->add(ins);
current->push(value); current->push(value);

Просмотреть файл

@ -1554,17 +1554,19 @@ GetPropertyIC::tryAttachProxy(JSContext *cx, IonScript *ion, HandleObject obj,
static void static void
GenerateProxyClassGuards(MacroAssembler &masm, Register object, Register scratchReg, 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, // Ensure that the incoming object has one of the magic class pointers, i.e,
// that it is one of an ObjectProxy, FunctionProxy, or OuterWindowProxy. // that it is one of an ObjectProxy, FunctionProxy, or OuterWindowProxy.
// This is equivalent to obj->is<ProxyObject>(). // This is equivalent to obj->is<ProxyObject>().
masm.branchTestObjClass(Assembler::Equal, object, scratchReg, masm.branchTestObjClass(Assembler::Equal, object, scratchReg,
CallableProxyClassPtr, success); CallableProxyClassPtr, &success);
masm.branchTestObjClass(Assembler::Equal, object, scratchReg, masm.branchTestObjClass(Assembler::Equal, object, scratchReg,
UncallableProxyClassPtr, success); UncallableProxyClassPtr, &success);
masm.branchTestObjClass(Assembler::NotEqual, object, scratchReg, masm.branchTestObjClass(Assembler::NotEqual, object, scratchReg,
OuterWindowProxyClassPtr, failures); OuterWindowProxyClassPtr, failures);
masm.bind(&success);
} }
bool bool
@ -1592,9 +1594,7 @@ GetPropertyIC::tryAttachGenericProxy(JSContext *cx, IonScript *ion, HandleObject
masm.setFramePushed(ion->frameSize()); masm.setFramePushed(ion->frameSize());
Label proxySuccess; GenerateProxyClassGuards(masm, object(), scratchReg, &failures);
GenerateProxyClassGuards(masm, object(), scratchReg, &failures, &proxySuccess);
masm.bind(&proxySuccess);
// Ensure that the incoming object is not a DOM proxy, so that we can get to // Ensure that the incoming object is not a DOM proxy, so that we can get to
// the specialized stubs // the specialized stubs
@ -2179,12 +2179,12 @@ SetPropertyIC::attachGenericProxy(JSContext *cx, IonScript *ion, void *returnAdd
Register scratch = regSet.takeGeneral(); Register scratch = regSet.takeGeneral();
masm.push(scratch); 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 // Remove the DOM proxies. They'll take care of themselves so this stub doesn't
// catch too much. // catch too much. The failure case is actually Equal. Fall through to the failure code.
masm.branchTestProxyHandlerFamily(Assembler::Equal, object(), scratch, masm.branchTestProxyHandlerFamily(Assembler::NotEqual, object(), scratch,
GetDOMProxyHandlerFamily(), &proxyFailures); GetDOMProxyHandlerFamily(), &proxySuccess);
masm.bind(&proxyFailures); masm.bind(&proxyFailures);
masm.pop(scratch); masm.pop(scratch);
@ -3466,14 +3466,17 @@ IsTypedArrayElementSetInlineable(JSObject *obj, const Value &idval, const Value
static bool static bool
GenerateSetDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher, GenerateSetDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
JSObject *obj, const Value &idval, Register object, ValueOperand indexVal, JSObject *obj, const Value &idval, bool guardHoles, Register object,
ConstantOrRegister value, Register tempToUnboxIndex, Register temp) ValueOperand indexVal, ConstantOrRegister value, Register tempToUnboxIndex,
Register temp)
{ {
JS_ASSERT(obj->isNative()); JS_ASSERT(obj->isNative());
JS_ASSERT(idval.isInt32()); JS_ASSERT(idval.isInt32());
Label failures; 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. // Guard object is a dense array.
Shape *shape = obj->lastProperty(); Shape *shape = obj->lastProperty();
@ -3495,6 +3498,14 @@ GenerateSetDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttac
// Compute the location of the element. // Compute the location of the element.
BaseIndex target(elements, index, TimesEight); 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. // Guard that we can increase the initialized length.
Address capacity(elements, ObjectElements::offsetOfCapacity()); Address capacity(elements, ObjectElements::offsetOfCapacity());
masm.branch32(Assembler::BelowOrEqual, capacity, index, &outOfBounds); 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); masm.branch32(Assembler::Below, initLength, index, &outOfBounds);
// if (initLength == index) // if (initLength == index)
Label markElem, postBarrier;
masm.branch32(Assembler::NotEqual, initLength, index, &markElem); masm.branch32(Assembler::NotEqual, initLength, index, &markElem);
{ {
// Increase initialize length. // Increase initialize length.
@ -3524,20 +3534,22 @@ GenerateSetDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttac
masm.jump(&postBarrier); masm.jump(&postBarrier);
} }
// else // else
{
// Mark old element.
masm.bind(&markElem); 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); masm.bind(&postBarrier);
Register postBarrierScratch = elements; Register postBarrierScratch = elements;
if (masm.maybeCallPostBarrier(object, value, postBarrierScratch)) if (masm.maybeCallPostBarrier(object, value, postBarrierScratch))
masm.loadPtr(Address(object, JSObject::offsetOfElements()), elements); masm.loadPtr(Address(object, JSObject::offsetOfElements()), elements);
// Store the value. // Store the value.
if (guardHoles)
masm.branchTestMagic(Assembler::Equal, target, &failures);
masm.storeConstantOrRegister(value, target); masm.storeConstantOrRegister(value, target);
} }
attacher.jumpRejoin(masm); attacher.jumpRejoin(masm);
@ -3556,14 +3568,18 @@ SetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, c
MacroAssembler masm(cx); MacroAssembler masm(cx);
RepatchStubAppender attacher(*this); RepatchStubAppender attacher(*this);
if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval, if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval,
object(), index(), value(), guardHoles(), object(), index(),
tempToUnboxIndex(), temp())) value(), tempToUnboxIndex(),
temp()))
{ {
return false; return false;
} }
setHasDenseStub(); 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 static bool
@ -3704,13 +3720,18 @@ SetElementParIC::attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObjec
MacroAssembler masm(cx); MacroAssembler masm(cx);
DispatchStubPrepender attacher(*this); DispatchStubPrepender attacher(*this);
if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval, if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval,
object(), index(), value(), guardHoles(), object(), index(),
tempToUnboxIndex(), temp())) value(), tempToUnboxIndex(),
temp()))
{ {
return false; 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 bool

Просмотреть файл

@ -808,13 +808,14 @@ class SetElementIC : public RepatchIonCache
ValueOperand index_; ValueOperand index_;
ConstantOrRegister value_; ConstantOrRegister value_;
bool strict_; bool strict_;
bool guardHoles_;
bool hasDenseStub_ : 1; bool hasDenseStub_ : 1;
public: public:
SetElementIC(Register object, Register tempToUnboxIndex, Register temp, SetElementIC(Register object, Register tempToUnboxIndex, Register temp,
FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value, FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
bool strict) bool strict, bool guardHoles)
: object_(object), : object_(object),
tempToUnboxIndex_(tempToUnboxIndex), tempToUnboxIndex_(tempToUnboxIndex),
temp_(temp), temp_(temp),
@ -822,6 +823,7 @@ class SetElementIC : public RepatchIonCache
index_(index), index_(index),
value_(value), value_(value),
strict_(strict), strict_(strict),
guardHoles_(guardHoles),
hasDenseStub_(false) hasDenseStub_(false)
{ {
} }
@ -851,6 +853,9 @@ class SetElementIC : public RepatchIonCache
bool strict() const { bool strict() const {
return strict_; return strict_;
} }
bool guardHoles() const {
return guardHoles_;
}
bool hasDenseStub() const { bool hasDenseStub() const {
return hasDenseStub_; return hasDenseStub_;
@ -1182,18 +1187,20 @@ class SetElementParIC : public ParallelIonCache
ValueOperand index_; ValueOperand index_;
ConstantOrRegister value_; ConstantOrRegister value_;
bool strict_; bool strict_;
bool guardHoles_;
public: public:
SetElementParIC(Register object, Register tempToUnboxIndex, Register temp, SetElementParIC(Register object, Register tempToUnboxIndex, Register temp,
FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value, FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
bool strict) bool strict, bool guardHoles)
: object_(object), : object_(object),
tempToUnboxIndex_(tempToUnboxIndex), tempToUnboxIndex_(tempToUnboxIndex),
temp_(temp), temp_(temp),
tempFloat_(tempFloat), tempFloat_(tempFloat),
index_(index), index_(index),
value_(value), value_(value),
strict_(strict) strict_(strict),
guardHoles_(guardHoles)
{ {
} }
@ -1226,6 +1233,9 @@ class SetElementParIC : public ParallelIonCache
bool strict() const { bool strict() const {
return strict_; return strict_;
} }
bool guardHoles() const {
return guardHoles_;
}
bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval); bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval);
bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr); 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()) { if (name && object->singleton() && object->singleton()->isNative()) {
Shape *shape = object->singleton()->nativeLookup(cx, name); Shape *shape = object->singleton()->nativeLookup(cx, name);
if (shape && if (shape &&
shape->hasSlot() &&
shape->hasDefaultGetter() && shape->hasDefaultGetter() &&
object->singleton()->nativeGetSlot(shape->slot()).isUndefined()) object->singleton()->nativeGetSlot(shape->slot()).isUndefined())
{ {

Просмотреть файл

@ -7135,10 +7135,13 @@ class MSetElementCache
public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> > public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
{ {
bool strict_; 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), : MSetElementInstruction(obj, index, value),
strict_(strict) strict_(strict),
guardHoles_(guardHoles)
{ {
} }
@ -7146,13 +7149,16 @@ class MSetElementCache
INSTRUCTION_HEADER(SetElementCache); INSTRUCTION_HEADER(SetElementCache);
static MSetElementCache *New(MDefinition *obj, MDefinition *index, MDefinition *value, static MSetElementCache *New(MDefinition *obj, MDefinition *index, MDefinition *value,
bool strict) { bool strict, bool guardHoles) {
return new MSetElementCache(obj, index, value, strict); return new MSetElementCache(obj, index, value, strict, guardHoles);
} }
bool strict() const { bool strict() const {
return strict_; return strict_;
} }
bool guardHoles() const {
return guardHoles_;
}
TypePolicy *typePolicy() { TypePolicy *typePolicy() {
return this; return this;

Просмотреть файл

@ -10,4 +10,6 @@
#include "js/RequiredDefines.h" #include "js/RequiredDefines.h"
#include "mozilla/Char16.h"
#endif /* js_confdefs_h */ #endif /* js_confdefs_h */

Просмотреть файл

@ -50,8 +50,6 @@ BEGIN_TEST(testChromeBuffer)
if (!JS_AddNamedObjectRoot(cx, &trusted_glob, "trusted-global")) if (!JS_AddNamedObjectRoot(cx, &trusted_glob, "trusted-global"))
return false; return false;
if (!JS_AddNamedObjectRoot(cx, &trusted_fun, "trusted-function"))
return false;
JSFunction *fun; JSFunction *fun;
@ -70,6 +68,8 @@ BEGIN_TEST(testChromeBuffer)
"trusted", 1, &paramName, bytes, strlen(bytes), "trusted", 1, &paramName, bytes, strlen(bytes),
"", 0)); "", 0));
trusted_fun = JS_GetFunctionObject(fun); trusted_fun = JS_GetFunctionObject(fun);
if (!JS_AddNamedObjectRoot(cx, &trusted_fun, "trusted-function"))
return false;
} }
JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun)); 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 /* 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, * 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/. */ * 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 /* 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, * 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/. */ * 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), rtcp_state_(MP_CONNECTING),
main_thread_(main_thread), main_thread_(main_thread),
sts_thread_(sts_thread), sts_thread_(sts_thread),
transport_(new PipelineTransport(this)),
rtp_send_srtp_(), rtp_send_srtp_(),
rtcp_send_srtp_(), rtcp_send_srtp_(),
rtp_recv_srtp_(), rtp_recv_srtp_(),
@ -102,6 +102,8 @@ class MediaPipeline : public sigslot::has_slots<> {
if (!rtcp_transport_) { if (!rtcp_transport_) {
rtcp_transport_ = rtp_transport; rtcp_transport_ = rtp_transport;
} }
// PipelineTransport() will access this->sts_thread_; moved here for safety
transport_ = new PipelineTransport(this);
} }
virtual ~MediaPipeline(); virtual ~MediaPipeline();
@ -517,7 +519,7 @@ class MediaPipelineReceiveVideo : public MediaPipelineReceive {
MediaPipelineReceive(pc, main_thread, sts_thread, MediaPipelineReceive(pc, main_thread, sts_thread,
stream, track_id, conduit, rtp_transport, stream, track_id, conduit, rtp_transport,
rtcp_transport), rtcp_transport),
renderer_(new PipelineRenderer(this)), renderer_(new PipelineRenderer(MOZ_THIS_IN_INITIALIZER_LIST())),
listener_(new PipelineListener(stream->AsSourceStream(), track_id)) { 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 * 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 * 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". * 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 #ifdef _MSC_VER
@ -39,10 +36,26 @@
* typedef from wchar_t. * typedef from wchar_t.
*/ */
# define MOZ_CHAR16_IS_NOT_WCHAR # 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 #else
# error "Char16.h requires C++11 (or something like it) for UTF-16 support." # error "Char16.h requires C++11 (or something like it) for UTF-16 support."
#endif #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. * 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 * 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) #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(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(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('A')) == 2, "Is char literal 16 bits?");
static_assert(sizeof(MOZ_UTF16("")[0]) == 2, "Is string char 16 bits?"); static_assert(sizeof(MOZ_UTF16("")[0]) == 2, "Is string char 16 bits?");
#endif
#endif /* mozilla_Char16_h */ #endif /* mozilla_Char16_h */

Просмотреть файл

@ -14,9 +14,6 @@
* the WeakPtrs to it and allows the WeakReference to live beyond the lifetime * the WeakPtrs to it and allows the WeakReference to live beyond the lifetime
* of 'Foo'. * 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 * The overhead of WeakPtr is that accesses to 'Foo' becomes an additional
* dereference, and an additional heap allocated pointer sized object shared * dereference, and an additional heap allocated pointer sized object shared
* between all of the WeakPtrs. * between all of the WeakPtrs.
@ -63,7 +60,6 @@
#define mozilla_WeakPtr_h #define mozilla_WeakPtr_h
#include "mozilla/Assertions.h" #include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/NullPtr.h" #include "mozilla/NullPtr.h"
#include "mozilla/RefPtr.h" #include "mozilla/RefPtr.h"
#include "mozilla/TypeTraits.h" #include "mozilla/TypeTraits.h"
@ -76,8 +72,8 @@ template <typename T, class WeakReference> class SupportsWeakPtrBase;
namespace detail { namespace detail {
// This can live beyond the lifetime of the class derived from SupportsWeakPtrBase. // This can live beyond the lifetime of the class derived from SupportsWeakPtrBase.
template<class T, RefCountAtomicity Atomicity> template<class T>
class WeakReference : public RefCounted<WeakReference<T, Atomicity>, Atomicity> class WeakReference : public ::mozilla::RefCounted<WeakReference<T> >
{ {
public: public:
explicit WeakReference(T* p) : ptr(p) {} explicit WeakReference(T* p) : ptr(p) {}
@ -86,8 +82,8 @@ class WeakReference : public RefCounted<WeakReference<T, Atomicity>, Atomicity>
} }
private: private:
friend class WeakPtrBase<T, WeakReference>; friend class WeakPtrBase<T, WeakReference<T> >;
friend class SupportsWeakPtrBase<T, WeakReference>; friend class SupportsWeakPtrBase<T, WeakReference<T> >;
void detach() { void detach() {
ptr = nullptr; ptr = nullptr;
} }
@ -121,30 +117,10 @@ class SupportsWeakPtrBase
}; };
template <typename T> template <typename T>
class SupportsWeakPtr class SupportsWeakPtr : public SupportsWeakPtrBase<T, detail::WeakReference<T> >
: public SupportsWeakPtrBase<T, detail::WeakReference<T, detail::NonAtomicRefCount> >
{ {
}; };
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> template <typename T, class WeakReference>
class WeakPtrBase class WeakPtrBase
{ {
@ -177,9 +153,9 @@ class WeakPtrBase
}; };
template <typename T> 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: public:
WeakPtr(const WeakPtr<T>& o) : Base(o) {} WeakPtr(const WeakPtr<T>& o) : Base(o) {}
WeakPtr(const Base& 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 # 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)))) 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)) SYNCSTRINGSPATH = $(abspath $(call MERGE_FILE,sync_strings.dtd))
STRINGSPATH = $(call core_abspath,$(call MERGE_FILE,android_strings.dtd)) STRINGSPATH = $(abspath $(call MERGE_FILE,android_strings.dtd))
ifeq (,$(XPI_NAME)) 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 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 endif
$(warnIfEmpty,AB_CD) # todo: $(errorIfEmpty ) $(warnIfEmpty,AB_CD) # todo: $(errorIfEmpty )
@ -32,7 +32,7 @@ chrome-%::
@$(MAKE) $(dir-res-values)-$(AB_rCD)/strings.xml AB_CD=$* @$(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 # 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 ifdef LOCALE_MERGEDIR
BOOKMARKSPATH = $(firstword \ BOOKMARKSPATH = $(firstword \
@ -40,7 +40,7 @@ BOOKMARKSPATH = $(firstword \
$(wildcard $(MOBILE_LOCALE_SRCDIR)/profile/bookmarks.inc ) \ $(wildcard $(MOBILE_LOCALE_SRCDIR)/profile/bookmarks.inc ) \
$(topsrcdir)/mobile/locales/en-US/profile/bookmarks.inc ) $(topsrcdir)/mobile/locales/en-US/profile/bookmarks.inc )
else else
BOOKMARKSPATH = $(call core_abspath,$(MOBILE_LOCALE_SRCDIR)/profile/bookmarks.inc) BOOKMARKSPATH = $(abspath $(MOBILE_LOCALE_SRCDIR)/profile/bookmarks.inc)
endif endif
# Determine the ../res/values[-*]/ path # Determine the ../res/values[-*]/ path

Просмотреть файл

@ -2,7 +2,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # 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) DEFINES += -DBIN_SUFFIX=$(BIN_SUFFIX)

Просмотреть файл

@ -25,6 +25,19 @@
#define __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS
#endif #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, * Force-include hunspell_alloc_hooks.h and hunspell_fopen_hooks.h for hunspell,
* so that we don't need to modify them directly. * so that we don't need to modify them directly.

Просмотреть файл

@ -53,6 +53,7 @@
#include <algorithm> #include <algorithm>
#ifdef MOZ_WIDGET_GONK #ifdef MOZ_WIDGET_GONK
#include "nsINetworkManager.h"
#include "nsINetworkStatsServiceProxy.h" #include "nsINetworkStatsServiceProxy.h"
#endif #endif
@ -966,6 +967,7 @@ WebSocketChannel::WebSocketChannel() :
mCountRecv(0), mCountRecv(0),
mCountSent(0), mCountSent(0),
mAppId(0), mAppId(0),
mConnectionType(NETWORK_NO_TYPE),
mIsInBrowser(false) mIsInBrowser(false)
{ {
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread"); NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
@ -1080,9 +1082,9 @@ WebSocketChannel::BeginOpen()
NS_GetAppInfo(localChannel, &mAppId, &mIsInBrowser); NS_GetAppInfo(localChannel, &mAppId, &mIsInBrowser);
} }
// obtain active network // obtain active connection type
if (mAppId != NECKO_NO_APP_ID) { if (mAppId != NECKO_NO_APP_ID) {
GetActiveNetwork(); GetConnectionType(&mConnectionType);
} }
rv = localChannel->AsyncOpen(this, mHttpChannel); rv = localChannel->AsyncOpen(this, mHttpChannel);
@ -3272,7 +3274,7 @@ WebSocketChannel::OnDataAvailable(nsIRequest *aRequest,
} }
nsresult nsresult
WebSocketChannel::GetActiveNetwork() WebSocketChannel::GetConnectionType(int32_t *type)
{ {
#ifdef MOZ_WIDGET_GONK #ifdef MOZ_WIDGET_GONK
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
@ -3281,11 +3283,15 @@ WebSocketChannel::GetActiveNetwork()
nsCOMPtr<nsINetworkManager> networkManager = do_GetService("@mozilla.org/network/manager;1", &result); nsCOMPtr<nsINetworkManager> networkManager = do_GetService("@mozilla.org/network/manager;1", &result);
if (NS_FAILED(result) || !networkManager) { if (NS_FAILED(result) || !networkManager) {
mActiveNetwork = nullptr; *type = NETWORK_NO_TYPE;
return NS_ERROR_UNEXPECTED;
} }
result = networkManager->GetActive(getter_AddRefs(mActiveNetwork)); nsCOMPtr<nsINetworkInterface> networkInterface;
result = networkManager->GetActive(getter_AddRefs(networkInterface));
if (networkInterface) {
result = networkInterface->GetType(type);
}
return NS_OK; return NS_OK;
#else #else
@ -3297,8 +3303,9 @@ nsresult
WebSocketChannel::SaveNetworkStats(bool enforce) WebSocketChannel::SaveNetworkStats(bool enforce)
{ {
#ifdef MOZ_WIDGET_GONK #ifdef MOZ_WIDGET_GONK
// Check if the active network and app id are valid. // Check if the connection type and app id are valid.
if(!mActiveNetwork || mAppId == NECKO_NO_APP_ID) { if(mConnectionType == NETWORK_NO_TYPE ||
mAppId == NECKO_NO_APP_ID) {
return NS_OK; return NS_OK;
} }
@ -3322,7 +3329,7 @@ WebSocketChannel::SaveNetworkStats(bool enforce)
return rv; return rv;
} }
mNetworkStatsServiceProxy->SaveAppStats(mAppId, mActiveNetwork, PR_Now() / 1000, mNetworkStatsServiceProxy->SaveAppStats(mAppId, mConnectionType, PR_Now() / 1000,
mCountRecv, mCountSent, nullptr); mCountRecv, mCountSent, nullptr);
// Reset the counters after saving. // Reset the counters after saving.

Просмотреть файл

@ -18,10 +18,6 @@
#include "nsIHttpChannelInternal.h" #include "nsIHttpChannelInternal.h"
#include "BaseWebSocketChannel.h" #include "BaseWebSocketChannel.h"
#ifdef MOZ_WIDGET_GONK
#include "nsINetworkManager.h"
#endif
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsString.h" #include "nsString.h"
#include "nsDeque.h" #include "nsDeque.h"
@ -258,17 +254,16 @@ private:
// These members are used for network per-app metering (bug 855949) // These members are used for network per-app metering (bug 855949)
// Currently, they are only available on gonk. // Currently, they are only available on gonk.
public: public:
const static int32_t NETWORK_NO_TYPE = -1; // default conntection type
const static uint64_t NETWORK_STATS_THRESHOLD = 65536; const static uint64_t NETWORK_STATS_THRESHOLD = 65536;
private: private:
uint64_t mCountRecv; uint64_t mCountRecv;
uint64_t mCountSent; uint64_t mCountSent;
uint32_t mAppId; uint32_t mAppId;
int32_t mConnectionType;
bool mIsInBrowser; bool mIsInBrowser;
#ifdef MOZ_WIDGET_GONK nsresult GetConnectionType(int32_t *);
nsCOMPtr<nsINetworkInterface> mActiveNetwork;
#endif
nsresult GetActiveNetwork();
nsresult SaveNetworkStats(bool); nsresult SaveNetworkStats(bool);
void CountRecvBytes(uint64_t recvBytes) void CountRecvBytes(uint64_t recvBytes)
{ {

Просмотреть файл

@ -87,7 +87,7 @@ NSS_EXTRA_DLLS += freebl_64int_3
NSS_EXTRA_DLLS += freebl_64fpu_3 NSS_EXTRA_DLLS += freebl_64fpu_3
endif endif
ABS_DIST := $(call core_abspath,$(DIST)) ABS_DIST := $(abspath $(DIST))
ifeq ($(HOST_OS_ARCH),WINNT) ifeq ($(HOST_OS_ARCH),WINNT)
ifdef CYGDRIVE_MOUNT ifdef CYGDRIVE_MOUNT
ABS_DIST := $(shell cygpath -w $(ABS_DIST) | sed -e 's|\\|/|g') 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/coreconf $(NSS_SRCDIR)/security
cp -Rp $(topsrcdir)/security/dbm $(NSS_SRCDIR)/security cp -Rp $(topsrcdir)/security/dbm $(NSS_SRCDIR)/security
cp -Rp $(topsrcdir)/dbm $(NSS_SRCDIR) 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 else
NSS_SRCDIR = $(topsrcdir) NSS_SRCDIR = $(topsrcdir)
endif endif
@ -450,7 +450,7 @@ endif # MOZ_FOLD_LIBS
ifeq ($(NSINSTALL_PY),$(NSINSTALL)) ifeq ($(NSINSTALL_PY),$(NSINSTALL))
DEFAULT_GMAKE_FLAGS += PYTHON='$(PYTHON)' 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)' DEFAULT_GMAKE_FLAGS += NSINSTALL='$$(PYTHON) $$(NSINSTALL_PY)'
else else
DEFAULT_GMAKE_FLAGS += NSINSTALL='$(NSINSTALL)' DEFAULT_GMAKE_FLAGS += NSINSTALL='$(NSINSTALL)'

Просмотреть файл

@ -73,12 +73,6 @@ getSiteKey(const nsACString & hostName, uint16_t port,
key.AppendInt(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 */ /* SSM_UserCertChoice: enum for cert choice info */
typedef enum {ASK, AUTO} SSM_UserCertChoice; typedef enum {ASK, AUTO} SSM_UserCertChoice;
@ -1205,13 +1199,13 @@ nsSSLIOLayerPoll(PRFileDesc * fd, int16_t in_flags, int16_t *out_flags)
} }
nsSSLIOLayerHelpers::nsSSLIOLayerHelpers() nsSSLIOLayerHelpers::nsSSLIOLayerHelpers()
: mutex("nsSSLIOLayerHelpers.mutex") : mRenegoUnrestrictedSites(nullptr)
, mRenegoUnrestrictedSites(nullptr)
, mTreatUnsafeNegotiationAsBroken(false) , mTreatUnsafeNegotiationAsBroken(false)
, mWarnLevelMissingRFC5746(1) , mWarnLevelMissingRFC5746(1)
, mTLSIntoleranceInfo(16) , mTLSIntoleranceInfo(16)
, mFalseStartRequireNPN(true) , mFalseStartRequireNPN(true)
, mFalseStartRequireForwardSecrecy(false) , mFalseStartRequireForwardSecrecy(false)
, mutex("nsSSLIOLayerHelpers.mutex")
{ {
} }

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше