зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to fx-team
This commit is contained in:
Коммит
0f7453cae3
2
CLOBBER
2
CLOBBER
|
@ -18,4 +18,4 @@
|
||||||
# Modifying this file will now automatically clobber the buildbot machines \o/
|
# 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 = false;
|
||||||
|
if (height == 0) {
|
||||||
|
height = 1;
|
||||||
mZero = true;
|
mZero = true;
|
||||||
mWidth = 1;
|
|
||||||
mHeight = 1;
|
|
||||||
} else {
|
|
||||||
mZero = false;
|
|
||||||
mWidth = width;
|
|
||||||
mHeight = height;
|
|
||||||
}
|
}
|
||||||
|
if (width == 0) {
|
||||||
|
width = 1;
|
||||||
|
mZero = true;
|
||||||
|
}
|
||||||
|
mWidth = width;
|
||||||
|
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,34 +7,40 @@
|
||||||
<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");
|
||||||
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)
|
||||||
|
{
|
||||||
|
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,31 +16,27 @@
|
||||||
|
|
||||||
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()
|
||||||
{
|
{
|
||||||
nsAutoString baseURI;
|
if (mSourceDocument) {
|
||||||
txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
|
nsAutoString baseURI;
|
||||||
|
txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
|
||||||
|
|
||||||
txLoadedDocumentEntry* entry = GetEntry(baseURI);
|
txLoadedDocumentEntry* entry = GetEntry(baseURI);
|
||||||
if (entry) {
|
if (entry) {
|
||||||
delete entry->mDocument.forget();
|
delete entry->mDocument.forget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
readonly attribute long sampleRate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimum time in milliseconds between samples stored in the database.
|
* Maximum number of samples stored in the database per connection type.
|
||||||
*/
|
*/
|
||||||
readonly attribute long sampleRate;
|
readonly attribute long maxStorageSamples;
|
||||||
|
|
||||||
/**
|
|
||||||
* Time in milliseconds recorded by the API until present time. All samples
|
|
||||||
* older than maxStorageAge from now are deleted.
|
|
||||||
*/
|
|
||||||
readonly attribute long long maxStorageAge;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 });
|
||||||
db.deleteObjectStore(STORE_NAME);
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
objectStore = db.createObjectStore(STORE_NAME, { keyPath: ["appId", "network", "timestamp"] });
|
// Copy the data from the original objectStore to the new objectStore.
|
||||||
objectStore.createIndex("appId", "appId", { unique: false });
|
objectStore = aTransaction.objectStore(STORE_NAME);
|
||||||
objectStore.createIndex("network", "network", { unique: false });
|
objectStore.openCursor().onsuccess = function(event) {
|
||||||
objectStore.createIndex("networkType", "networkType", { unique: false });
|
let cursor = event.target.result;
|
||||||
objectStore.createIndex("timestamp", "timestamp", { unique: false });
|
if (!cursor) {
|
||||||
objectStore.createIndex("rxBytes", "rxBytes", { unique: false });
|
// Delete the original object store.
|
||||||
objectStore.createIndex("txBytes", "txBytes", { unique: false });
|
db.deleteObjectStore(STORE_NAME);
|
||||||
objectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false });
|
return;
|
||||||
objectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false });
|
}
|
||||||
|
|
||||||
debug("Created object stores and indexes for version 2");
|
let oldStats = cursor.value;
|
||||||
|
let newStats = { appId: 0,
|
||||||
|
connectionType: oldStats.connectionType,
|
||||||
|
timestamp: oldStats.timestamp,
|
||||||
|
rxBytes: oldStats.rxBytes,
|
||||||
|
txBytes: oldStats.txBytes,
|
||||||
|
rxTotalBytes: oldStats.rxTotalBytes,
|
||||||
|
txTotalBytes: oldStats.txTotalBytes };
|
||||||
|
this._saveStats(aTransaction, newObjectStore, newStats);
|
||||||
|
cursor.continue();
|
||||||
|
}.bind(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
importData: function importData(aStats) {
|
|
||||||
let stats = { appId: aStats.appId,
|
|
||||||
network: [aStats.networkId, aStats.networkType],
|
|
||||||
timestamp: aStats.timestamp,
|
|
||||||
rxBytes: aStats.rxBytes,
|
|
||||||
txBytes: aStats.txBytes,
|
|
||||||
rxTotalBytes: aStats.rxTotalBytes,
|
|
||||||
txTotalBytes: aStats.txTotalBytes };
|
|
||||||
|
|
||||||
return stats;
|
|
||||||
},
|
|
||||||
|
|
||||||
exportData: function exportData(aStats) {
|
|
||||||
let stats = { appId: aStats.appId,
|
|
||||||
networkId: aStats.network[0],
|
|
||||||
networkType: aStats.network[1],
|
|
||||||
timestamp: aStats.timestamp,
|
|
||||||
rxBytes: aStats.rxBytes,
|
|
||||||
txBytes: aStats.txBytes,
|
|
||||||
rxTotalBytes: aStats.rxTotalBytes,
|
|
||||||
txTotalBytes: aStats.txTotalBytes };
|
|
||||||
|
|
||||||
return stats;
|
|
||||||
},
|
|
||||||
|
|
||||||
normalizeDate: function normalizeDate(aDate) {
|
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: (stats.appId == 0) ? 0 : stats.rxBytes,
|
||||||
rxBytes: (aStats.appId == 0) ? 0 : aStats.rxBytes,
|
txBytes: (stats.appId == 0) ? 0 : stats.txBytes,
|
||||||
txBytes: (aStats.appId == 0) ? 0 : aStats.txBytes,
|
rxTotalBytes: (stats.appId == 0) ? stats.rxBytes : 0,
|
||||||
rxTotalBytes: (aStats.appId == 0) ? aStats.rxBytes : 0,
|
txTotalBytes: (stats.appId == 0) ? stats.txBytes : 0 };
|
||||||
txTotalBytes: (aStats.appId == 0) ? aStats.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) {
|
store.clear();
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
||||||
debug("Service started");
|
if (DEBUG) {
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("receiveMessage " + aMessage.name);
|
if (DEBUG) {
|
||||||
|
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 (this._connectionTypes[network.type]) {
|
||||||
if (!netId) {
|
this._connectionTypes[network.type].network = network;
|
||||||
break;
|
this.updateStats(network.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateStats(netId);
|
|
||||||
break;
|
break;
|
||||||
case "xpcom-shutdown":
|
case "xpcom-shutdown":
|
||||||
debug("Service shutdown");
|
if (DEBUG) {
|
||||||
|
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,
|
||||||
if (manifestURL) {
|
start: data.start,
|
||||||
appId = appsService.getAppLocalIdByManifestURL(manifestURL);
|
end: data.end };
|
||||||
|
|
||||||
if (!appId) {
|
let manifestURL = data.manifestURL;
|
||||||
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
if (manifestURL) {
|
||||||
{ id: msg.id, error: "Invalid manifestURL", result: null });
|
let appId = appsService.getAppLocalIdByManifestURL(manifestURL);
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("get appId: " + appId + " from manifestURL: " + manifestURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!appId) {
|
||||||
|
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||||
|
{ id: msg.id, error: "Invalid manifestURL", result: null });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.appId = appId;
|
||||||
|
options.manifestURL = manifestURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
debug("getStats for options: " + JSON.stringify(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.connectionType || options.connectionType.length == 0) {
|
||||||
|
this._db.findAll(function onStatsFound(error, result) {
|
||||||
|
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||||
|
{ id: msg.id, error: error, result: result });
|
||||||
|
}, options);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let start = new Date(msg.start);
|
for (let i in this._connectionTypes) {
|
||||||
let end = new Date(msg.end);
|
if (this._connectionTypes[i].name == options.connectionType) {
|
||||||
|
this._db.find(function onStatsFound(error, result) {
|
||||||
|
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||||
|
{ id: msg.id, error: error, result: result });
|
||||||
|
}, options);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.updateStats(netId, function onStatsUpdated(aResult, aMessage) {
|
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||||
debug("getstats for network " + network.id + " of type " + network.type);
|
{ id: msg.id, error: "Invalid connectionType", result: null });
|
||||||
debug("appId: " + appId + " from manifestURL: " + manifestURL);
|
|
||||||
|
|
||||||
self._db.find(function onStatsFound(aError, aResult) {
|
}.bind(this));
|
||||||
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
|
||||||
{ id: msg.id, error: aError, result: aResult });
|
|
||||||
}, network, start, end, appId, manifestURL);
|
|
||||||
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
clearInterfaceStats: function clearInterfaceStats(mm, msg) {
|
|
||||||
let network = msg.network;
|
|
||||||
let netId = this.getNetworkId(network.id, network.type);
|
|
||||||
|
|
||||||
debug("clear stats for network " + network.id + " of type " + network.type);
|
|
||||||
|
|
||||||
if (!this._networks[netId]) {
|
|
||||||
mm.sendAsyncMessage("NetworkStats:Clear:Return",
|
|
||||||
{ id: msg.id, error: "Invalid networkType", result: null });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._db.clearInterfaceStats(network, function onDBCleared(aError, aResult) {
|
|
||||||
mm.sendAsyncMessage("NetworkStats:Clear:Return",
|
|
||||||
{ id: msg.id, error: aError, result: aResult });
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
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) {
|
aTimeStamp + " " + aRxBytes + " " + aTxBytes);
|
||||||
aCallback.notify(false, "Invalid network type");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("saveAppStats: " + aAppId + " " + netId + " " +
|
|
||||||
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) {
|
||||||
debug("Application stats inserted in indexedDB");
|
if (DEBUG) {
|
||||||
|
debug("Application stats inserted in indexedDB");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
delete this.cachedAppStats[key];
|
delete this.cachedAppStats[key];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateCachedAppStats: function updateCachedAppStats(aCallback) {
|
updateCachedAppStats: function updateCachedAppStats(callback) {
|
||||||
debug("updateCachedAppStats: " + this.cachedAppStatsDate);
|
if (DEBUG) {
|
||||||
|
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,15 +29,15 @@ 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);
|
||||||
},
|
},
|
||||||
|
|
||||||
classID : NETWORKSTATSSERVICEPROXY_CID,
|
classID : NETWORKSTATSSERVICEPROXY_CID,
|
||||||
|
|
|
@ -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,34 +12,45 @@
|
||||||
<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();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkDataDates(data, start, end, sampleRate) {
|
function checkDataDates(data, start, end, sampleRate){
|
||||||
var offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
var offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||||
start = Math.floor((start.getTime() - offset) / sampleRate) * sampleRate + offset;
|
start = Math.floor((start.getTime() - offset) / sampleRate) * sampleRate + offset;
|
||||||
end = Math.floor((end.getTime() - offset) / sampleRate) * sampleRate + offset;
|
end = Math.floor((end.getTime() - offset) / sampleRate) * sampleRate + offset;
|
||||||
|
@ -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 {
|
||||||
|
netStats.getNetworkStats({end: new Date()});
|
||||||
|
} catch(ex) {
|
||||||
|
ok(true, "getNetworkStats launch exception when start param does not exist");
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
ok(false, "getNetworkStats launch exception when start param does not exist");
|
||||||
netStats.getSamples(network, new Date(), new Date());
|
}, 1000);
|
||||||
} catch(ex) {
|
|
||||||
ok(true, "getSamples launch exception if network is not " +
|
|
||||||
"a nsIDOMMozNetworkStatsInterface");
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ok(false, "getSamples launch exception if network is not " +
|
|
||||||
"a nsIDOMMozNetworkStatsInterface");
|
|
||||||
},
|
},
|
||||||
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);
|
try {
|
||||||
startDate = startDate.toString();
|
netStats.getNetworkStats({start: new Date()});
|
||||||
|
} catch(ex) {
|
||||||
|
ok(true, "getNetworkStats launch exception when end param does not exist");
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
ok(false, "getNetworkStats launch exception when end param does not exist");
|
||||||
netStats.getSamples(network, startDate, endDate);
|
}, 1000);
|
||||||
} catch(ex) {
|
|
||||||
ok(true, "getSamples launch exception when start param is not a Date");
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ok(false, "getSamples launch exception when start param is not a Date");
|
|
||||||
},
|
},
|
||||||
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,31 +89,20 @@ 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,
|
||||||
|
connectionType: "wifi",
|
||||||
var stats = { appId: 0,
|
timestamp: Date.now(),
|
||||||
network: [networks[0].id, networks[0].type],
|
rxBytes: 0,
|
||||||
timestamp: Date.now(),
|
txBytes: 0,
|
||||||
rxBytes: 0,
|
rxTotalBytes: 1234,
|
||||||
txBytes: 0,
|
txTotalBytes: 1234};
|
||||||
rxTotalBytes: 1234,
|
|
||||||
txTotalBytes: 1234 };
|
|
||||||
|
|
||||||
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
||||||
netStatsDb._saveStats(txn, store, stats);
|
netStatsDb._saveStats(txn, store, stats);
|
||||||
|
@ -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,23 +125,19 @@ 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,
|
||||||
rxTotalBytes: 1234,
|
rxTotalBytes: 1234,
|
||||||
txTotalBytes: 1234 });
|
txTotalBytes: 1234});
|
||||||
}
|
}
|
||||||
|
|
||||||
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
||||||
|
@ -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,31 +170,28 @@ 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});
|
||||||
|
|
||||||
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
|
||||||
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,
|
||||||
|
connectionType: "wifi", timestamp: date,
|
||||||
|
rxBytes: 0, txBytes: 0,
|
||||||
|
rxTotalBytes: 1234, txTotalBytes: 1234};
|
||||||
|
|
||||||
var lastStat = { appId: 0,
|
var newStat = {appId: 0,
|
||||||
network: network, timestamp: date,
|
connectionType: "wifi", timestamp: date,
|
||||||
rxBytes: 0, txBytes: 0,
|
rxBytes: 0, txBytes: 0,
|
||||||
rxTotalBytes: 1234, txTotalBytes: 1234 };
|
rxTotalBytes: 2234, txTotalBytes: 2234};
|
||||||
|
|
||||||
var newStat = { appId: 0,
|
processSamplesDiff(lastStat, newStat, function(result) {
|
||||||
network: network, timestamp: date,
|
|
||||||
rxBytes: 0, txBytes: 0,
|
|
||||||
rxTotalBytes: 2234, txTotalBytes: 2234 };
|
|
||||||
|
|
||||||
processSamplesDiff(networks, 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,
|
||||||
|
connectionType: "wifi", timestamp: date,
|
||||||
|
rxBytes: 0, txBytes: 0,
|
||||||
|
rxTotalBytes: 1234, txTotalBytes: 1234};
|
||||||
|
|
||||||
var lastStat = { appId: 0,
|
var newStat = {appId: 0,
|
||||||
network: network, timestamp: date,
|
connectionType: "wifi", timestamp: date + sampleRate,
|
||||||
rxBytes: 0, txBytes: 0,
|
rxBytes: 0, txBytes: 0,
|
||||||
rxTotalBytes: 1234, txTotalBytes: 1234 };
|
rxTotalBytes: 500, txTotalBytes: 500};
|
||||||
|
|
||||||
var newStat = { appId: 0,
|
processSamplesDiff(lastStat, newStat, function(result) {
|
||||||
network: network, timestamp: date + sampleRate,
|
|
||||||
rxBytes: 0, txBytes: 0,
|
|
||||||
rxTotalBytes: 500, txTotalBytes: 500 };
|
|
||||||
|
|
||||||
processSamplesDiff(networks, 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 stats = {appId: 0,
|
||||||
var network = [networks[0].id, networks[0].type];
|
connectionType: "wifi",
|
||||||
|
date: new Date(),
|
||||||
|
rxBytes: 2234,
|
||||||
|
txBytes: 2234};
|
||||||
|
|
||||||
var stats = { appId: 0,
|
netStatsDb.clear(function (error, result) {
|
||||||
networkId: networks[0].id,
|
|
||||||
networkType: networks[0].type,
|
|
||||||
date: new Date(),
|
|
||||||
rxBytes: 2234,
|
|
||||||
txBytes: 2234};
|
|
||||||
|
|
||||||
netStatsDb.clearStats(networks, 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 stats = {appId: 1,
|
||||||
var network = [networks[0].id, networks[0].type];
|
connectionType: "wifi",
|
||||||
|
date: new Date(),
|
||||||
|
rxBytes: 2234,
|
||||||
|
txBytes: 2234};
|
||||||
|
|
||||||
var stats = { appId: 1,
|
netStatsDb.clear(function (error, result) {
|
||||||
networkId: networks[0].id,
|
|
||||||
networkType: networks[0].type,
|
|
||||||
date: new Date(),
|
|
||||||
rxBytes: 2234,
|
|
||||||
txBytes: 2234};
|
|
||||||
|
|
||||||
netStatsDb.clearStats(networks, 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);
|
||||||
run_next_test();
|
|
||||||
}, networks[0], start, end, appId);
|
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();
|
||||||
|
}, {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();
|
||||||
|
@ -484,63 +432,69 @@ add_test(function test_findAppStats () {
|
||||||
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: 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);
|
||||||
run_next_test();
|
|
||||||
}, networks[0], start, end, 1);
|
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();
|
||||||
|
}, {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.],
|
||||||
AC_DEFINE(JS_THREADSAFE)
|
JS_THREADSAFE= )
|
||||||
fi],)
|
if test -n "$JS_THREADSAFE"; then
|
||||||
|
AC_DEFINE(JS_THREADSAFE)
|
||||||
|
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,28 +4157,19 @@ 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 = expr->isKind(PNK_ADD)
|
||||||
*def = f.binary<MAdd>(lhsDef, rhsDef, MIRType_Int32);
|
? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Int32)
|
||||||
*type = Type::Intish;
|
: f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32);
|
||||||
} else if (lhsType.isDouble() && rhsType.isDouble()) {
|
*type = Type::Intish;
|
||||||
*def = f.binary<MAdd>(lhsDef, rhsDef, MIRType_Double);
|
} else if (lhsType.isDoublish() && rhsType.isDoublish()) {
|
||||||
*type = Type::Double;
|
*def = expr->isKind(PNK_ADD)
|
||||||
} else {
|
? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Double)
|
||||||
return f.failf(expr, "operands to + must both be int or double, got %s and %s",
|
: f.binary<MSub>(lhsDef, rhsDef, MIRType_Double);
|
||||||
lhsType.toChars(), rhsType.toChars());
|
*type = Type::Double;
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (lhsType.isInt() && rhsType.isInt()) {
|
return f.failf(expr, "operands to +/- must both be int or doublish, got %s and %s",
|
||||||
*def = f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32);
|
lhsType.toChars(), rhsType.toChars());
|
||||||
*type = Type::Intish;
|
|
||||||
} else if (lhsType.isDoublish() && rhsType.isDoublish()) {
|
|
||||||
*def = f.binary<MSub>(lhsDef, rhsDef, MIRType_Double);
|
|
||||||
*type = Type::Double;
|
|
||||||
} else {
|
|
||||||
return f.failf(expr, "operands to - must both be int or doublish, got %s and %s",
|
|
||||||
lhsType.toChars(), rhsType.toChars());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numAddOrSubOut)
|
if (numAddOrSubOut)
|
||||||
|
|
|
@ -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,49 +3498,58 @@ 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);
|
||||||
|
|
||||||
// Guard that we can increase the initialized length.
|
// If TI cannot help us deal with HOLES by preventing indexed properties
|
||||||
Address capacity(elements, ObjectElements::offsetOfCapacity());
|
// on the prototype chain, we have to be very careful to check for ourselves
|
||||||
masm.branch32(Assembler::BelowOrEqual, capacity, index, &outOfBounds);
|
// to avoid stomping on what should be a setter call. Start by only allowing things
|
||||||
|
// within the initialized length.
|
||||||
|
if (guardHoles) {
|
||||||
|
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
|
||||||
|
masm.branch32(Assembler::BelowOrEqual, initLength, index, &outOfBounds);
|
||||||
|
} else {
|
||||||
|
// Guard that we can increase the initialized length.
|
||||||
|
Address capacity(elements, ObjectElements::offsetOfCapacity());
|
||||||
|
masm.branch32(Assembler::BelowOrEqual, capacity, index, &outOfBounds);
|
||||||
|
|
||||||
// Guard on the initialized length.
|
// Guard on the initialized length.
|
||||||
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
|
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
|
||||||
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.
|
Int32Key newLength(index);
|
||||||
Int32Key newLength(index);
|
masm.bumpKey(&newLength, 1);
|
||||||
masm.bumpKey(&newLength, 1);
|
masm.storeKey(newLength, initLength);
|
||||||
masm.storeKey(newLength, initLength);
|
|
||||||
|
|
||||||
// Increase length if needed.
|
// Increase length if needed.
|
||||||
Label bumpedLength;
|
Label bumpedLength;
|
||||||
Address length(elements, ObjectElements::offsetOfLength());
|
Address length(elements, ObjectElements::offsetOfLength());
|
||||||
masm.branch32(Assembler::AboveOrEqual, length, index, &bumpedLength);
|
masm.branch32(Assembler::AboveOrEqual, length, index, &bumpedLength);
|
||||||
masm.storeKey(newLength, length);
|
masm.storeKey(newLength, length);
|
||||||
masm.bind(&bumpedLength);
|
masm.bind(&bumpedLength);
|
||||||
|
|
||||||
// Restore the index.
|
// Restore the index.
|
||||||
masm.bumpKey(&newLength, -1);
|
masm.bumpKey(&newLength, -1);
|
||||||
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.bind(&postBarrier);
|
masm.callPreBarrier(target, MIRType_Value);
|
||||||
|
|
||||||
|
// Call post barrier if necessary, and recalculate elements pointer if it got clobbered.
|
||||||
|
if (!guardHoles)
|
||||||
|
masm.bind(&postBarrier);
|
||||||
Register postBarrierScratch = elements;
|
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, ¶mName, bytes, strlen(bytes),
|
"trusted", 1, ¶mName, 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/. */
|
||||||
|
@ -148,15 +149,15 @@ nsresult MediaPipeline::TransportReady_s(TransportFlow *flow) {
|
||||||
// failure. bug 852665.
|
// failure. bug 852665.
|
||||||
if (*state != MP_CONNECTING) {
|
if (*state != MP_CONNECTING) {
|
||||||
MOZ_MTLOG(ML_ERROR, "Transport ready for flow in wrong state:" <<
|
MOZ_MTLOG(ML_ERROR, "Transport ready for flow in wrong state:" <<
|
||||||
description_ << ": " << (rtcp ? "rtcp" : "rtp"));
|
description_ << ": " << (rtcp ? "rtcp" : "rtp"));
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult res;
|
nsresult res;
|
||||||
|
|
||||||
MOZ_MTLOG(ML_DEBUG, "Transport ready for pipeline " <<
|
MOZ_MTLOG(ML_DEBUG, "Transport ready for pipeline " <<
|
||||||
static_cast<void *>(this) << " flow " << description_ << ": " <<
|
static_cast<void *>(this) << " flow " << description_ << ": " <<
|
||||||
(rtcp ? "rtcp" : "rtp"));
|
(rtcp ? "rtcp" : "rtp"));
|
||||||
|
|
||||||
// Now instantiate the SRTP objects
|
// Now instantiate the SRTP objects
|
||||||
TransportLayerDtls *dtls = static_cast<TransportLayerDtls *>(
|
TransportLayerDtls *dtls = static_cast<TransportLayerDtls *>(
|
||||||
|
|
|
@ -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();
|
||||||
|
@ -145,7 +147,7 @@ class MediaPipeline : public sigslot::has_slots<> {
|
||||||
// Implement the TransportInterface functions
|
// Implement the TransportInterface functions
|
||||||
PipelineTransport(MediaPipeline *pipeline)
|
PipelineTransport(MediaPipeline *pipeline)
|
||||||
: pipeline_(pipeline),
|
: pipeline_(pipeline),
|
||||||
sts_thread_(pipeline->sts_thread_) {}
|
sts_thread_(pipeline->sts_thread_) {}
|
||||||
|
|
||||||
void Detach() { pipeline_ = NULL; }
|
void Detach() { pipeline_ = NULL; }
|
||||||
MediaPipeline *pipeline() const { return pipeline_; }
|
MediaPipeline *pipeline() const { return pipeline_; }
|
||||||
|
@ -183,12 +185,12 @@ class MediaPipeline : public sigslot::has_slots<> {
|
||||||
|
|
||||||
Direction direction_;
|
Direction direction_;
|
||||||
RefPtr<MediaStream> stream_; // A pointer to the stream we are servicing.
|
RefPtr<MediaStream> stream_; // A pointer to the stream we are servicing.
|
||||||
// Written on the main thread.
|
// Written on the main thread.
|
||||||
// Used on STS and MediaStreamGraph threads.
|
// Used on STS and MediaStreamGraph threads.
|
||||||
TrackID track_id_; // The track on the stream.
|
TrackID track_id_; // The track on the stream.
|
||||||
// Written and used as the stream_;
|
// Written and used as the stream_;
|
||||||
RefPtr<MediaSessionConduit> conduit_; // Our conduit. Written on the main
|
RefPtr<MediaSessionConduit> conduit_; // Our conduit. Written on the main
|
||||||
// thread. Read on STS thread.
|
// thread. Read on STS thread.
|
||||||
|
|
||||||
// The transport objects. Read/written on STS thread.
|
// The transport objects. Read/written on STS thread.
|
||||||
RefPtr<TransportFlow> rtp_transport_;
|
RefPtr<TransportFlow> rtp_transport_;
|
||||||
|
@ -385,10 +387,10 @@ class MediaPipelineTransmit : public MediaPipeline {
|
||||||
const MediaSegment& media);
|
const MediaSegment& media);
|
||||||
|
|
||||||
virtual void ProcessAudioChunk(AudioSessionConduit *conduit,
|
virtual void ProcessAudioChunk(AudioSessionConduit *conduit,
|
||||||
TrackRate rate, AudioChunk& chunk);
|
TrackRate rate, AudioChunk& chunk);
|
||||||
#ifdef MOZILLA_INTERNAL_API
|
#ifdef MOZILLA_INTERNAL_API
|
||||||
virtual void ProcessVideoChunk(VideoSessionConduit *conduit,
|
virtual void ProcessVideoChunk(VideoSessionConduit *conduit,
|
||||||
TrackRate rate, VideoChunk& chunk);
|
TrackRate rate, VideoChunk& chunk);
|
||||||
#endif
|
#endif
|
||||||
RefPtr<MediaSessionConduit> conduit_;
|
RefPtr<MediaSessionConduit> conduit_;
|
||||||
volatile bool active_;
|
volatile bool active_;
|
||||||
|
@ -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")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче