Merge mozilla-central to fx-team

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

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

@ -18,4 +18,4 @@
# Modifying this file will now automatically clobber the buildbot machines \o/
#
Bug 872934 - Clobber needed for webidl updates for style sheet change events. Again and again.
Bug 895047 - Clobber needed for touching js/src/js-confdefs.h.in

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

@ -7,8 +7,9 @@ ifndef .PYMAKE
ifeq (,$(MAKE_VERSION))
$(error GNU Make is required)
endif
ifeq (,$(filter-out 3.78 3.79,$(MAKE_VERSION)))
$(error GNU Make 3.80 or higher is required)
make_min_ver := 3.81
ifneq ($(make_min_ver),$(firstword $(sort $(make_min_ver) $(MAKE_VERSION))))
$(error GNU Make $(make_min_ver) or higher is required)
endif
endif

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

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

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

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

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

@ -9,13 +9,18 @@ DEFINES += \
-DB2G_NAME=L\"$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)\" \
-DGAIA_PATH=L\"$(subst /,\\\\,$(GAIA_PATH))\" \
$(NULL)
GAIA_MAKE=make
else # Non-windows machines use the same wrapper program
CSRCS = run-b2g.c
DEFINES += \
-DB2G_NAME=\"$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)\" \
-DGAIA_PATH=\"$(GAIA_PATH)\" \
$(NULL)
endif
ifdef .PYMAKE
# For use of GNU make in pymake builds.
GAIA_MAKE=$(GMAKE)
else
GAIA_MAKE=$(MAKE)
endif
@ -27,6 +32,6 @@ GENERATED_DIRS += $(DIST)/bin/$(GAIA_PATH)
include $(topsrcdir)/config/rules.mk
libs::
$(GAIA_MAKE) -j1 -C $(GAIADIR) clean
$(GAIA_MAKE) -j1 -C $(GAIADIR) profile
+$(GAIA_MAKE) -j1 -C $(GAIADIR) clean
+$(GAIA_MAKE) -j1 -C $(GAIADIR) profile
(cd $(GAIADIR)/profile && tar $(TAR_CREATE_FLAGS) - .) | (cd $(abspath $(DIST))/bin/$(GAIA_PATH) && tar -xf -)

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

@ -3,7 +3,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DISTROEXT = $(call core_abspath,$(FINAL_TARGET))/distribution/extensions
DISTROEXT = $(abspath $(FINAL_TARGET))/distribution/extensions
include $(topsrcdir)/config/config.mk
@ -34,7 +34,7 @@ include $(topsrcdir)/config/rules.mk
$(all_xpis): $(DISTROEXT)/%.xpi: $(call mkdir_deps,$(DISTROEXT)) libs-%
cd $* && \
$(ZIP) -r9XD $@ * -x \*.in -x \*.mkdir.done
cd $(call core_abspath,$(srcdir)/$*) && \
cd $(abspath $(srcdir)/$*) && \
$(ZIP) -r9XD $@ * -x \*.in -x \*.mkdir.done
.PHONY: $(all_xpis:.xpi=)

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

@ -5,7 +5,7 @@
include $(topsrcdir)/config/config.mk
abs_srcdir = $(call core_abspath,$(srcdir))
abs_srcdir = $(abspath $(srcdir))
CHROME_DEPS += $(abs_srcdir)/content/overrides/app-license.html

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

@ -155,6 +155,54 @@ function basicNotification() {
}
}
function errorNotification() {
var self = this;
this.browser = gBrowser.selectedBrowser;
this.id = "test-notification-" + gTestIndex;
this.message = "This is popup notification " + this.id + " from test " + gTestIndex;
this.anchorID = null;
this.mainAction = {
label: "Main Action",
accessKey: "M",
callback: function () {
self.mainActionClicked = true;
throw new Error("Oops!");
}
};
this.secondaryActions = [
{
label: "Secondary Action",
accessKey: "S",
callback: function () {
self.secondaryActionClicked = true;
throw new Error("Oops!");
}
}
];
this.options = {
eventCallback: function (eventName) {
switch (eventName) {
case "dismissed":
self.dismissalCallbackTriggered = true;
break;
case "showing":
self.showingCallbackTriggered = true;
break;
case "shown":
self.shownCallbackTriggered = true;
break;
case "removed":
self.removedCallbackTriggered = true;
break;
}
}
};
this.addOptions = function(options) {
for (let [name, value] in Iterator(options))
self.options[name] = value;
}
}
var wrongBrowserNotificationObject = new basicNotification();
var wrongBrowserNotification;
@ -831,7 +879,69 @@ var tests = [
});
}
},
{ // Test #29 - Existing popup notification shouldn't disappear when adding a dismissed notification
{ // Test #29 - Popup Notifications should catch exceptions from callbacks
run: function () {
let callbackCount = 0;
this.testNotif1 = new basicNotification();
this.testNotif1.message += " 1";
showNotification(this.testNotif1);
this.testNotif1.options.eventCallback = function (eventName) {
info("notifyObj1.options.eventCallback: " + eventName);
if (eventName == "dismissed") {
throw new Error("Oops 1!");
if (++callbackCount == 2) {
executeSoon(goNext);
}
}
};
this.testNotif2 = new basicNotification();
this.testNotif2.message += " 2";
this.testNotif2.id += "-2";
this.testNotif2.options.eventCallback = function (eventName) {
info("notifyObj2.options.eventCallback: " + eventName);
if (eventName == "dismissed") {
throw new Error("Oops 2!");
if (++callbackCount == 2) {
executeSoon(goNext);
}
}
};
showNotification(this.testNotif2);
},
onShown: function (popup) {
is(popup.childNodes.length, 2, "two notifications are shown");
dismissNotification(popup);
},
onHidden: function () {}
},
{ // Test #30 - Popup Notifications main actions should catch exceptions from callbacks
run: function () {
this.testNotif = new errorNotification();
showNotification(this.testNotif);
},
onShown: function (popup) {
checkPopup(popup, this.testNotif);
triggerMainCommand(popup);
},
onHidden: function (popup) {
ok(this.testNotif.mainActionClicked, "main action has been triggered");
}
},
{ // Test #31 - Popup Notifications secondary actions should catch exceptions from callbacks
run: function () {
this.testNotif = new errorNotification();
showNotification(this.testNotif);
},
onShown: function (popup) {
checkPopup(popup, this.testNotif);
triggerSecondaryCommand(popup, 0);
},
onHidden: function (popup) {
ok(this.testNotif.secondaryActionClicked, "secondary action has been triggered");
}
},
{ // Test #32 - Existing popup notification shouldn't disappear when adding a dismissed notification
run: function () {
this.notifyObj1 = new basicNotification();
this.notifyObj1.id += "_1";
@ -862,7 +972,7 @@ var tests = [
this.notification2.remove();
}
},
{ // Test #30 - Showing should be able to modify the popup data
{ // Test #33 - Showing should be able to modify the popup data
run: function() {
this.notifyObj = new basicNotification();
var normalCallback = this.notifyObj.options.eventCallback;

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

@ -8,7 +8,7 @@ browser_path := \"$(browser_path)\"
_PROFILE_DIR = $(TARGET_DEPTH)/_profile/pgo
ABSOLUTE_TOPSRCDIR = $(call core_abspath,$(MOZILLA_DIR))
ABSOLUTE_TOPSRCDIR = $(abspath $(MOZILLA_DIR))
_CERTS_SRC_DIR = $(ABSOLUTE_TOPSRCDIR)/build/pgo/certs
AUTOMATION_PPARGS = \

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

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

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

@ -19,14 +19,12 @@ topsrcdir = $(TOPSRCDIR)
DEPTH = $(OBJDIR)
include $(OBJDIR)/config/autoconf.mk
core_abspath = $(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1))
DIST = $(OBJDIR)/dist
postflight_all:
mkdir -p $(DIST_UNI)/$(MOZ_PKG_APPNAME)
rm -f $(DIST_ARCH_2)/universal
ln -s $(call core_abspath,$(DIST_UNI)) $(DIST_ARCH_2)/universal
ln -s $(abspath $(DIST_UNI)) $(DIST_ARCH_2)/universal
# Stage a package for buildsymbols to be happy. Doing so in OBJDIR_ARCH_1
# actually does a universal staging with both OBJDIR_ARCH_1 and OBJDIR_ARCH_2.
$(MAKE) -C $(OBJDIR_ARCH_1)/$(MOZ_BUILD_APP)/installer \

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

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

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

@ -43,13 +43,11 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;
import android.os.Environment;
public class WatcherService extends Service
{
private final String prgVersion = "Watcher Version 1.16";
private final String prgVersion = "Watcher Version 1.17";
private final String LOGTAG = "Watcher";
String sErrorPrefix = "##Installer Error## ";
@ -135,16 +133,16 @@ public class WatcherService extends Service
try {
if (nStayOn != 0) {
if (!Settings.System.putInt(getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB)) {
doToast("Screen couldn't be set to Always On [stay on while plugged in]");
Log.e(LOGTAG, "Screen couldn't be set to Always On [stay on while plugged in]");
}
}
} catch (Exception e) {
e.printStackTrace();
String sExcept = e.getMessage();
doToast("Screen couldn't be set to Always On [exception " + sExcept + "]");
Log.e(LOGTAG, "Screen couldn't be set to Always On [exception " + sExcept + "]");
}
doToast("WatcherService created");
Log.i(LOGTAG, "WatcherService created");
}
public String GetIniData(String sSection, String sKey, String sFile, String sDefault)
@ -228,7 +226,7 @@ public class WatcherService extends Service
{
if (!this.bStartedTimer)
{
doToast("WatcherService started");
Log.i(LOGTAG, "WatcherService started");
myTimer = new Timer();
Date startSchedule = new Date(System.currentTimeMillis() + lDelay);
myTimer.schedule(new MyTime(), startSchedule, lPeriod);
@ -237,11 +235,11 @@ public class WatcherService extends Service
}
else
{
doToast("WatcherService unknown command");
Log.w(LOGTAG, "WatcherService unknown command");
}
}
else
doToast("WatcherService created");
Log.w(LOGTAG, "WatcherService intent had null command");
}
public void writeVersion() {
@ -280,7 +278,7 @@ public class WatcherService extends Service
@Override
public void onDestroy() {
super.onDestroy();
doToast("WatcherService destroyed");
Log.i(LOGTAG, "WatcherService destroyed");
if (pwl != null)
pwl.release();
stopForegroundCompat(R.string.foreground_service_started);
@ -447,14 +445,6 @@ public class WatcherService extends Service
}
}
public void doToast(String sMsg)
{
Log.i(LOGTAG, sMsg);
Toast toast = Toast.makeText(this, sMsg, Toast.LENGTH_LONG);
toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL, 0, 100);
toast.show();
}
public void CheckMem()
{
System.gc();

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

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

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

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

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

@ -128,7 +128,7 @@ GARBAGE += \
ifndef CROSS_COMPILE
ifdef USE_ELF_DYNSTR_GC
elf-dynstr-gc: elf-dynstr-gc.c $(GLOBAL_DEPS) $(call mkdir_deps,$(MDDEPDIR))
$(CC) $(COMPILE_CFLAGS) $(GLIB_CFLAGS) -o $@ $< $(LDFLAGS) $(GLIB_LIBS)
$(CC) $(COMPILE_CFLAGS) $(GLIB_CFLAGS) -DELFDYNSTRGC_BUILD -o $@ $< $(LDFLAGS) $(GLIB_LIBS)
endif
endif

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

@ -13,7 +13,7 @@ ifndef JAVA_CLASSPATH
endif
# DEBUG_JARSIGNER always debug signs.
DEBUG_JARSIGNER=$(PYTHON) $(call core_abspath,$(topsrcdir)/mobile/android/debug_sign_tool.py) \
DEBUG_JARSIGNER=$(PYTHON) $(abspath $(topsrcdir)/mobile/android/debug_sign_tool.py) \
--keytool=$(KEYTOOL) \
--jarsigner=$(JARSIGNER) \
$(NULL)

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

@ -632,7 +632,7 @@ endif
PWD := $(CURDIR)
endif
NSINSTALL_PY := $(PYTHON) $(call core_abspath,$(topsrcdir)/config/nsinstall.py)
NSINSTALL_PY := $(PYTHON) $(abspath $(topsrcdir)/config/nsinstall.py)
# For Pymake, wherever we use nsinstall.py we're also going to try to make it
# a native command where possible. Since native commands can't be used outside
# of single-line commands, we continue to provide INSTALL for general use.
@ -691,7 +691,7 @@ else
IS_LANGUAGE_REPACK = 1
endif
EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/$(1)/en-US,$(call core_realpath,$(L10NBASEDIR))/$(AB_CD)/$(subst /locales,,$(1)))
EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/$(1)/en-US,$(or $(realpath $(L10NBASEDIR)),$(abspath $(L10NBASEDIR)))/$(AB_CD)/$(subst /locales,,$(1)))
ifdef relativesrcdir
LOCALE_SRCDIR ?= $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir))
@ -742,7 +742,7 @@ ifdef MOZ_DEBUG
JAVAC_FLAGS += -g
endif
CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py)
CREATE_PRECOMPLETE_CMD = $(PYTHON) $(abspath $(topsrcdir)/config/createprecomplete.py)
# MDDEPDIR is the subdirectory where dependency files are stored
MDDEPDIR := .deps

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

@ -16,7 +16,7 @@ $(error Do not include functions.mk twice!)
endif
INCLUDED_FUNCTIONS_MK = 1
core_abspath = $(if $(findstring :,$(1)),$(1),$(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1)))
core_realpath = $(if $(realpath $(1)),$(realpath $(1)),$(call core_abspath,$(1)))
core_abspath = $(error core_abspath is unsupported, use $$(abspath) instead)
core_realpath = $(error core_realpath is unsupported)
core_winabspath = $(firstword $(subst /, ,$(call core_abspath,$(1)))):$(subst $(space),,$(patsubst %,\\%,$(wordlist 2,$(words $(subst /, ,$(call core_abspath,$(1)))), $(strip $(subst /, ,$(call core_abspath,$(1)))))))
core_winabspath = $(error core_winabspath is unsupported)

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

@ -13,7 +13,7 @@ $(error config/nspr/Makefile.in is not compatible with MOZ_NATIVE_NSPR)
endif
# Copy NSPR to the SDK
ABS_DIST = $(call core_abspath,$(DIST))
ABS_DIST = $(abspath $(DIST))
ifdef MOZ_FOLD_LIBS
# Trick the nspr build system into not building shared libraries.

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

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta charset="UTF-8">
<script>
function start() {
var doc = document.getElementsByTagName("iframe")[0].contentDocument;
var vid = doc.getElementsByTagName("video")[0];
function runnable() {
// The doc.write forces us to recreate doc's body.
doc.write("Hello, world");
doc.body.appendChild(vid);
document.documentElement.removeAttribute("class");
}
doc.open();
setTimeout(runnable, 0);
}
</script>
</head>
<body onload='start()'>
<iframe src="data:text/html,<meta charset=UTF-8><body><video src=http://localhost:8080/ controls=true loop=true autoplay=true autobuffer=false></video>"></iframe>
</body>
</html>

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

@ -133,5 +133,6 @@ load 841205.html
load 844404.html
load 847127.html
load 849601.html
skip-if(Android) load 851353-1.html
load 863950.html
load 864448.html

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

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

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

@ -506,6 +506,7 @@ support-files =
[test_bug814576.html]
[test_bug819051.html]
[test_bug820909.html]
[test_bug840098.html]
[test_bug868999.html]
[test_bug869000.html]
[test_bug869002.html]

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

@ -0,0 +1,34 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=840098
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 840098</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=840098">Mozilla Bug 840098</a>
<p id="display"></p>
<div id="content" style="display: none">
<div id="foo"></div>
</div>
<marquee id="m">Hello</marquee>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 840098 **/
var anonymousNode = document.getElementById("m").outerDiv;
try {
document.implementation.createDocument("", "", null).adoptNode(anonymousNode);
ok(false, "shouldn't be able to adopt the root of an anonymous subtree");
} catch (e) {
is(e.name, "NotSupportedError", "threw the correct type of error");
}
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,13 @@
<script>
o0 = document.createElement('canvas');
(document.body || document.documentElement).appendChild(o0);
o1 = o0.getContext('2d');
o2 = document.createElement('img');
//o2.src = "image2.png";
o3 = o1.createImageData(0.7409945472006207, 0.8815588599260801);
o1.save();
o1.mozCurrentTransform = [0.18777365986904448, 4, 4, -2048, 3, 32];
o0.width = 0.52;
o1.putImageData(o3, -32, -0.16596290333335356);
o0.toBlob(function() {}, "video/mp4", 16);
</script>

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

@ -17,3 +17,4 @@ skip-if(Android||B2G) load 780392-1.html # bug 833371 for B2G
skip-if(Android||B2G) load 789933-1.html # bug 833371
load 794463-1.html
load 802926-1.html
load 916128-1.html

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

@ -14,8 +14,8 @@
#include "mozilla/RefPtr.h"
#define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
{ 0x8b8da863, 0xd151, 0x4014, \
{ 0x8b, 0xdc, 0x62, 0xb5, 0x0d, 0xc0, 0x2b, 0x62 } }
{ 0x9a6a5bdf, 0x1261, 0x4057, \
{ 0x85, 0xcc, 0xaf, 0x97, 0x6c, 0x36, 0x99, 0xa9 } }
class gfxContext;
class gfxASurface;
@ -71,6 +71,9 @@ public:
GraphicsFilter aFilter,
uint32_t aFlags = RenderFlagPremultAlpha) = 0;
// Creates an image buffer. Returns null on failure.
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) = 0;
// Gives you a stream containing the image represented by this context.
// The format is given in aMimeTime, for example "image/png".
//

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

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

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

@ -22,6 +22,7 @@
#include "mozilla/gfx/Rect.h"
#include "mozilla/gfx/2D.h"
#include "gfx2DGlue.h"
#include "imgIEncoder.h"
class nsXULElement;
@ -460,6 +461,8 @@ public:
friend class CanvasRenderingContext2DUserData;
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
protected:
nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
uint32_t aWidth, uint32_t aHeight,

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

@ -0,0 +1,330 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxImageSurface.h"
#include "ImageEncoder.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
namespace mozilla {
namespace dom {
class EncodingCompleteEvent : public nsRunnable
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
EncodingCompleteEvent(nsIScriptContext* aScriptContext,
nsIThread* aEncoderThread,
FileCallback& aCallback)
: mImgSize(0)
, mType()
, mImgData(nullptr)
, mScriptContext(aScriptContext)
, mEncoderThread(aEncoderThread)
, mCallback(&aCallback)
, mFailed(false)
{}
virtual ~EncodingCompleteEvent() {}
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
mozilla::ErrorResult rv;
if (!mFailed) {
nsRefPtr<nsDOMMemoryFile> blob =
new nsDOMMemoryFile(mImgData, mImgSize, mType);
if (mScriptContext) {
JSContext* jsContext = mScriptContext->GetNativeContext();
if (jsContext) {
JS_updateMallocCounter(jsContext, mImgSize);
}
}
mCallback->Call(blob, rv);
}
// These members aren't thread-safe. We're making sure that they're being
// released on the main thread here. Otherwise, they could be getting
// released by EncodingRunnable's destructor on the encoding thread
// (bug 916128).
mScriptContext = nullptr;
mCallback = nullptr;
mEncoderThread->Shutdown();
return rv.ErrorCode();
}
void SetMembers(void* aImgData, uint64_t aImgSize, const nsAutoString& aType)
{
mImgData = aImgData;
mImgSize = aImgSize;
mType = aType;
}
void SetFailed()
{
mFailed = true;
}
private:
uint64_t mImgSize;
nsAutoString mType;
void* mImgData;
nsCOMPtr<nsIScriptContext> mScriptContext;
nsCOMPtr<nsIThread> mEncoderThread;
nsRefPtr<FileCallback> mCallback;
bool mFailed;
};
NS_IMPL_ISUPPORTS1(EncodingCompleteEvent, nsIRunnable);
class EncodingRunnable : public nsRunnable
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
EncodingRunnable(const nsAString& aType,
const nsAString& aOptions,
uint8_t* aImageBuffer,
imgIEncoder* aEncoder,
EncodingCompleteEvent* aEncodingCompleteEvent,
int32_t aFormat,
const nsIntSize aSize,
bool aUsingCustomOptions)
: mType(aType)
, mOptions(aOptions)
, mImageBuffer(aImageBuffer)
, mEncoder(aEncoder)
, mEncodingCompleteEvent(aEncodingCompleteEvent)
, mFormat(aFormat)
, mSize(aSize)
, mUsingCustomOptions(aUsingCustomOptions)
{}
virtual ~EncodingRunnable() {}
nsresult ProcessImageData(uint64_t* aImgSize, void** aImgData)
{
nsCOMPtr<nsIInputStream> stream;
nsresult rv = ImageEncoder::ExtractDataInternal(mType,
mOptions,
mImageBuffer,
mFormat,
mSize,
nullptr,
getter_AddRefs(stream),
mEncoder);
// If there are unrecognized custom parse options, we should fall back to
// the default values for the encoder without any options at all.
if (rv == NS_ERROR_INVALID_ARG && mUsingCustomOptions) {
rv = ImageEncoder::ExtractDataInternal(mType,
EmptyString(),
mImageBuffer,
mFormat,
mSize,
nullptr,
getter_AddRefs(stream),
mEncoder);
}
NS_ENSURE_SUCCESS(rv, rv);
rv = stream->Available(aImgSize);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(*aImgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
rv = NS_ReadInputStreamToBuffer(stream, aImgData, *aImgSize);
NS_ENSURE_SUCCESS(rv, rv);
return rv;
}
NS_IMETHOD Run()
{
uint64_t imgSize;
void* imgData = nullptr;
nsresult rv = ProcessImageData(&imgSize, &imgData);
if (NS_FAILED(rv)) {
mEncodingCompleteEvent->SetFailed();
} else {
mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
}
rv = NS_DispatchToMainThread(mEncodingCompleteEvent, NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
// Better to leak than to crash.
mEncodingCompleteEvent.forget();
return rv;
}
return rv;
}
private:
nsAutoString mType;
nsAutoString mOptions;
nsAutoArrayPtr<uint8_t> mImageBuffer;
nsCOMPtr<imgIEncoder> mEncoder;
nsRefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
int32_t mFormat;
const nsIntSize mSize;
bool mUsingCustomOptions;
};
NS_IMPL_ISUPPORTS1(EncodingRunnable, nsIRunnable)
/* static */
nsresult
ImageEncoder::ExtractData(nsAString& aType,
const nsAString& aOptions,
const nsIntSize aSize,
nsICanvasRenderingContextInternal* aContext,
nsIInputStream** aStream)
{
nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
if (!encoder) {
return NS_IMAGELIB_ERROR_NO_ENCODER;
}
return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize, aContext,
aStream, encoder);
}
/* static */
nsresult
ImageEncoder::ExtractDataAsync(nsAString& aType,
const nsAString& aOptions,
bool aUsingCustomOptions,
uint8_t* aImageBuffer,
int32_t aFormat,
const nsIntSize aSize,
nsICanvasRenderingContextInternal* aContext,
nsIScriptContext* aScriptContext,
FileCallback& aCallback)
{
nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
if (!encoder) {
return NS_IMAGELIB_ERROR_NO_ENCODER;
}
nsCOMPtr<nsIThread> encoderThread;
nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<EncodingCompleteEvent> completeEvent =
new EncodingCompleteEvent(aScriptContext, encoderThread, aCallback);
nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
aOptions,
aImageBuffer,
encoder,
completeEvent,
aFormat,
aSize,
aUsingCustomOptions);
return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
}
/*static*/ nsresult
ImageEncoder::GetInputStream(int32_t aWidth,
int32_t aHeight,
uint8_t* aImageBuffer,
int32_t aFormat,
imgIEncoder* aEncoder,
const PRUnichar* aEncoderOptions,
nsIInputStream** aStream)
{
nsresult rv =
aEncoder->InitFromData(aImageBuffer,
aWidth * aHeight * 4, aWidth, aHeight, aWidth * 4,
aFormat,
nsDependentString(aEncoderOptions));
NS_ENSURE_SUCCESS(rv, rv);
return CallQueryInterface(aEncoder, aStream);
}
/* static */
nsresult
ImageEncoder::ExtractDataInternal(const nsAString& aType,
const nsAString& aOptions,
uint8_t* aImageBuffer,
int32_t aFormat,
const nsIntSize aSize,
nsICanvasRenderingContextInternal* aContext,
nsIInputStream** aStream,
imgIEncoder* aEncoder)
{
nsCOMPtr<nsIInputStream> imgStream;
// get image bytes
nsresult rv;
if (aImageBuffer) {
rv = ImageEncoder::GetInputStream(
aSize.width,
aSize.height,
aImageBuffer,
aFormat,
aEncoder,
nsPromiseFlatString(aOptions).get(),
getter_AddRefs(imgStream));
} else if (aContext) {
NS_ConvertUTF16toUTF8 encoderType(aType);
rv = aContext->GetInputStream(encoderType.get(),
nsPromiseFlatString(aOptions).get(),
getter_AddRefs(imgStream));
} else {
// no context, so we have to encode an empty image
// note that if we didn't have a current context, the spec says we're
// supposed to just return transparent black pixels of the canvas
// dimensions.
nsRefPtr<gfxImageSurface> emptyCanvas =
new gfxImageSurface(gfxIntSize(aSize.width, aSize.height),
gfxImageFormatARGB32);
if (emptyCanvas->CairoStatus()) {
return NS_ERROR_INVALID_ARG;
}
rv = aEncoder->InitFromData(emptyCanvas->Data(),
aSize.width * aSize.height * 4,
aSize.width,
aSize.height,
aSize.width * 4,
imgIEncoder::INPUT_FORMAT_HOSTARGB,
aOptions);
if (NS_SUCCEEDED(rv)) {
imgStream = do_QueryInterface(aEncoder);
}
}
NS_ENSURE_SUCCESS(rv, rv);
imgStream.forget(aStream);
return rv;
}
/* static */
already_AddRefed<imgIEncoder>
ImageEncoder::GetImageEncoder(nsAString& aType)
{
// Get an image encoder for the media type.
nsCString encoderCID("@mozilla.org/image/encoder;2?type=");
NS_ConvertUTF16toUTF8 encoderType(aType);
encoderCID += encoderType;
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
if (!encoder && aType != NS_LITERAL_STRING("image/png")) {
// Unable to create an encoder instance of the specified type. Falling back
// to PNG.
aType.AssignLiteral("image/png");
nsCString PNGEncoderCID("@mozilla.org/image/encoder;2?type=image/png");
encoder = do_CreateInstance(PNGEncoderCID.get());
}
return encoder.forget();
}
} // namespace dom
} // namespace mozilla

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

@ -0,0 +1,92 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ImageEncoder_h
#define ImageEncoder_h
#include "imgIEncoder.h"
#include "nsDOMFile.h"
#include "nsError.h"
#include "mozilla/dom/HTMLCanvasElementBinding.h"
#include "nsLayoutUtils.h"
#include "nsNetUtil.h"
#include "nsSize.h"
class nsICanvasRenderingContextInternal;
namespace mozilla {
namespace dom {
class EncodingRunnable;
class ImageEncoder
{
public:
// Extracts data synchronously and gives you a stream containing the image
// represented by aContext. aType may change to "image/png" if we had to fall
// back to a PNG encoder. A return value of NS_OK implies successful data
// extraction. If there are any unrecognized custom parse options in
// aOptions, NS_ERROR_INVALID_ARG will be returned. When encountering this
// error it is usual to call this function again without any options at all.
static nsresult ExtractData(nsAString& aType,
const nsAString& aOptions,
const nsIntSize aSize,
nsICanvasRenderingContextInternal* aContext,
nsIInputStream** aStream);
// Extracts data asynchronously. aType may change to "image/png" if we had to
// fall back to a PNG encoder. aOptions are the options to be passed to the
// encoder and aUsingCustomOptions specifies whether custom parse options were
// used (i.e. by using -moz-parse-options). If there are any unrecognized
// custom parse options, we fall back to the default values for the encoder
// without any options at all. A return value of NS_OK only implies
// successful dispatching of the extraction step to the encoding thread.
static nsresult ExtractDataAsync(nsAString& aType,
const nsAString& aOptions,
bool aUsingCustomOptions,
uint8_t* aImageBuffer,
int32_t aFormat,
const nsIntSize aSize,
nsICanvasRenderingContextInternal* aContext,
nsIScriptContext* aScriptContext,
FileCallback& aCallback);
// Gives you a stream containing the image represented by aImageBuffer.
// The format is given in aFormat, for example
// imgIEncoder::INPUT_FORMAT_HOSTARGB.
static nsresult GetInputStream(int32_t aWidth,
int32_t aHeight,
uint8_t* aImageBuffer,
int32_t aFormat,
imgIEncoder* aEncoder,
const PRUnichar* aEncoderOptions,
nsIInputStream** aStream);
private:
// When called asynchronously, aContext is null.
static nsresult
ExtractDataInternal(const nsAString& aType,
const nsAString& aOptions,
uint8_t* aImageBuffer,
int32_t aFormat,
const nsIntSize aSize,
nsICanvasRenderingContextInternal* aContext,
nsIInputStream** aStream,
imgIEncoder* aEncoder);
// Creates and returns an encoder instance of the type specified in aType.
// aType may change to "image/png" if no instance of the original type could
// be created and we had to fall back to a PNG encoder. A return value of
// NULL should be interpreted as NS_IMAGELIB_ERROR_NO_ENCODER and aType is
// undefined in this case.
static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
friend class EncodingRunnable;
};
} // namespace dom
} // namespace mozilla
#endif // ImageEncoder_h

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

@ -22,5 +22,6 @@ INCLUDES += \
-I$(srcdir)/../../html/content/src \
-I$(srcdir)/../../../js/xpconnect/src \
-I$(srcdir)/../../../dom/base \
-I$(srcdir)/../../../image/src \
-I$(topsrcdir)/content/xul/content/src \
$(NULL)

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

@ -27,7 +27,7 @@
#include "nsIVariant.h"
#include "imgIEncoder.h"
#include "ImageEncoder.h"
#include "gfxContext.h"
#include "gfxPattern.h"
@ -381,8 +381,10 @@ WebGLContext::SetDimensions(int32_t width, int32_t height)
return NS_OK;
// Zero-sized surfaces can cause problems.
if (width == 0 || height == 0) {
if (width == 0) {
width = 1;
}
if (height == 0) {
height = 1;
}
@ -733,6 +735,54 @@ void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
}
}
void
WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
{
*aImageBuffer = nullptr;
*aFormat = 0;
nsRefPtr<gfxImageSurface> imgsurf =
new gfxImageSurface(gfxIntSize(mWidth, mHeight),
gfxImageFormatARGB32);
if (!imgsurf || imgsurf->CairoStatus()) {
return;
}
nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
if (!ctx || ctx->HasError()) {
return;
}
// Use Render() to make sure that appropriate y-flip gets applied
uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
nsresult rv = Render(ctx, GraphicsFilter::FILTER_NEAREST, flags);
if (NS_FAILED(rv)) {
return;
}
int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
if (!mOptions.premultipliedAlpha) {
// We need to convert to INPUT_FORMAT_RGBA, otherwise
// we are automatically considered premult, and unpremult'd.
// Yes, it is THAT silly.
// Except for different lossy conversions by color,
// we could probably just change the label, and not change the data.
gfxUtils::ConvertBGRAtoRGBA(imgsurf);
format = imgIEncoder::INPUT_FORMAT_RGBA;
}
static const fallible_t fallible = fallible_t();
uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
if (!imageBuffer) {
return;
}
memcpy(imageBuffer, imgsurf->Data(), mWidth * mHeight * 4);
*aImageBuffer = imageBuffer;
*aFormat = format;
}
NS_IMETHODIMP
WebGLContext::GetInputStream(const char* aMimeType,
const PRUnichar* aEncoderOptions,
@ -742,48 +792,22 @@ WebGLContext::GetInputStream(const char* aMimeType,
if (!gl)
return NS_ERROR_FAILURE;
nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
gfxImageFormatARGB32);
if (surf->CairoStatus() != 0)
uint8_t* imageBuffer = nullptr;
int32_t format = 0;
GetImageBuffer(&imageBuffer, &format);
if (!imageBuffer) {
return NS_ERROR_FAILURE;
nsRefPtr<gfxContext> tmpcx = new gfxContext(surf);
// Use Render() to make sure that appropriate y-flip gets applied
uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
nsresult rv = Render(tmpcx, GraphicsFilter::FILTER_NEAREST, flags);
if (NS_FAILED(rv))
return rv;
const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
nsAutoArrayPtr<char> conid(new char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
strcpy(conid, encoderPrefix);
strcat(conid, aMimeType);
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
if (!encoder)
return NS_ERROR_FAILURE;
int format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
if (!mOptions.premultipliedAlpha) {
// We need to convert to INPUT_FORMAT_RGBA, otherwise
// we are automatically considered premult, and unpremult'd.
// Yes, it is THAT silly.
// Except for different lossy conversions by color,
// we could probably just change the label, and not change the data.
gfxUtils::ConvertBGRAtoRGBA(surf);
format = imgIEncoder::INPUT_FORMAT_RGBA;
}
rv = encoder->InitFromData(surf->Data(),
mWidth * mHeight * 4,
mWidth, mHeight,
surf->Stride(),
format,
nsDependentString(aEncoderOptions));
NS_ENSURE_SUCCESS(rv, rv);
nsCString enccid("@mozilla.org/image/encoder;2?type=");
enccid += aMimeType;
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
if (!encoder) {
return NS_ERROR_FAILURE;
}
return CallQueryInterface(encoder, aStream);
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
encoder, aEncoderOptions, aStream);
}
NS_IMETHODIMP

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

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

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

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

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

@ -7,42 +7,43 @@
<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<script>
var gCompares = 0;
function compareAsync(file, canvas, type)
function compareAsync(file, canvas, type, callback)
{
++gCompares;
var reader = new FileReader();
reader.onload =
function(e) {
is(e.target.result, canvas.toDataURL(type),
"<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
if (--gCompares == 0) {
SimpleTest.finish();
}
callback(canvas);
};
reader.readAsDataURL(file);
}
function test1(canvas)
{
var pngfile = canvas.mozGetAsFile("foo.png");
is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
compareAsync(pngfile, canvas, "image/png", test2);
is(pngfile.name, "foo.png", "File name should be what we passed in");
}
function test2(canvas)
{
var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
is(jpegfile.type, "image/jpeg",
"When a valid type is specified that should be returned");
compareAsync(jpegfile, canvas, "image/jpeg", SimpleTest.finish);
is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(function () {
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
var pngfile = canvas.mozGetAsFile("foo.png");
is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
compareAsync(pngfile, canvas, "image/png");
is(pngfile.name, "foo.png", "File name should be what we passed in");
var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
is(jpegfile.type, "image/jpeg",
"When a valid type is specified that should be returned");
compareAsync(jpegfile, canvas, "image/jpeg");
is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
test1(canvas);
});
</script>

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

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

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

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

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

@ -5,11 +5,11 @@
#include "mozilla/dom/HTMLCanvasElement.h"
#include "Layers.h"
#include "imgIEncoder.h"
#include "ImageEncoder.h"
#include "jsapi.h"
#include "jsfriendapi.h"
#include "gfxImageSurface.h"
#include "Layers.h"
#include "mozilla/Base64.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
@ -23,6 +23,7 @@
#include "nsContentUtils.h"
#include "nsDisplayList.h"
#include "nsDOMFile.h"
#include "nsDOMJSUtils.h"
#include "nsFrameManager.h"
#include "nsIScriptSecurityManager.h"
#include "nsITimer.h"
@ -46,29 +47,6 @@ namespace {
typedef mozilla::dom::HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement
HTMLImageOrCanvasOrVideoElement;
class ToBlobRunnable : public nsRunnable
{
public:
ToBlobRunnable(mozilla::dom::FileCallback& aCallback,
nsIDOMBlob* aBlob)
: mCallback(&aCallback),
mBlob(aBlob)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
NS_IMETHOD Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mozilla::ErrorResult rv;
mCallback->Call(mBlob, rv);
return rv.ErrorCode();
}
private:
nsRefPtr<mozilla::dom::FileCallback> mCallback;
nsCOMPtr<nsIDOMBlob> mBlob;
};
} // anonymous namespace
namespace mozilla {
@ -369,10 +347,10 @@ HTMLCanvasElement::MozFetchAsStream(nsIInputStreamCallback *aCallback,
return NS_ERROR_FAILURE;
nsresult rv;
bool fellBackToPNG = false;
nsCOMPtr<nsIInputStream> inputData;
rv = ExtractData(aType, EmptyString(), getter_AddRefs(inputData), fellBackToPNG);
nsAutoString type(aType);
rv = ExtractData(type, EmptyString(), getter_AddRefs(inputData));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAsyncInputStream> asyncData = do_QueryInterface(inputData, &rv);
@ -404,68 +382,15 @@ HTMLCanvasElement::GetMozPrintCallback() const
}
nsresult
HTMLCanvasElement::ExtractData(const nsAString& aType,
HTMLCanvasElement::ExtractData(nsAString& aType,
const nsAString& aOptions,
nsIInputStream** aStream,
bool& aFellBackToPNG)
nsIInputStream** aStream)
{
// note that if we don't have a current context, the spec says we're
// supposed to just return transparent black pixels of the canvas
// dimensions.
nsRefPtr<gfxImageSurface> emptyCanvas;
nsIntSize size = GetWidthHeight();
if (!mCurrentContext) {
emptyCanvas = new gfxImageSurface(gfxIntSize(size.width, size.height), gfxImageFormatARGB32);
if (emptyCanvas->CairoStatus()) {
return NS_ERROR_INVALID_ARG;
}
}
nsresult rv;
// get image bytes
nsCOMPtr<nsIInputStream> imgStream;
NS_ConvertUTF16toUTF8 encoderType(aType);
try_again:
if (mCurrentContext) {
rv = mCurrentContext->GetInputStream(encoderType.get(),
nsPromiseFlatString(aOptions).get(),
getter_AddRefs(imgStream));
} else {
// no context, so we have to encode the empty image we created above
nsCString enccid("@mozilla.org/image/encoder;2?type=");
enccid += encoderType;
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get(), &rv);
if (NS_SUCCEEDED(rv) && encoder) {
rv = encoder->InitFromData(emptyCanvas->Data(),
size.width * size.height * 4,
size.width,
size.height,
size.width * 4,
imgIEncoder::INPUT_FORMAT_HOSTARGB,
aOptions);
if (NS_SUCCEEDED(rv)) {
imgStream = do_QueryInterface(encoder);
}
} else {
rv = NS_ERROR_FAILURE;
}
}
if (NS_FAILED(rv) && !aFellBackToPNG) {
// Try image/png instead.
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
aFellBackToPNG = true;
encoderType.AssignLiteral("image/png");
goto try_again;
}
NS_ENSURE_SUCCESS(rv, rv);
imgStream.forget(aStream);
return NS_OK;
return ImageEncoder::ExtractData(aType,
aOptions,
GetSize(),
mCurrentContext,
aStream);
}
nsresult
@ -516,8 +441,6 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
const JS::Value& aEncoderOptions,
nsAString& aDataURL)
{
bool fallbackToPNG = false;
nsIntSize size = GetWidthHeight();
if (size.height == 0 || size.width == 0) {
aDataURL = NS_LITERAL_STRING("data:,");
@ -538,23 +461,18 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
}
nsCOMPtr<nsIInputStream> stream;
rv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
rv = ExtractData(type, params, getter_AddRefs(stream));
// If there are unrecognized custom parse options, we should fall back to
// the default values for the encoder without any options at all.
if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
fallbackToPNG = false;
rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
}
NS_ENSURE_SUCCESS(rv, rv);
// build data URL string
if (fallbackToPNG)
aDataURL = NS_LITERAL_STRING("data:image/png;base64,");
else
aDataURL = NS_LITERAL_STRING("data:") + type +
NS_LITERAL_STRING(";base64,");
aDataURL = NS_LITERAL_STRING("data:") + type + NS_LITERAL_STRING(";base64,");
uint64_t count;
rv = stream->Available(&count);
@ -564,7 +482,6 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
return Base64EncodeInputStream(stream, aDataURL, (uint32_t)count, aDataURL.Length());
}
// XXXkhuey the encoding should be off the main thread, but we're lazy.
void
HTMLCanvasElement::ToBlob(JSContext* aCx,
FileCallback& aCallback,
@ -597,58 +514,35 @@ HTMLCanvasElement::ToBlob(JSContext* aCx,
#ifdef DEBUG
if (mCurrentContext) {
// We disallow canvases of width or height zero, and set them to 1, so
// we will have a discrepancy with the sizes of the canvas and the context.
// That discrepancy is OK, the rest are not.
nsIntSize elementSize = GetWidthHeight();
MOZ_ASSERT(elementSize.width == mCurrentContext->GetWidth());
MOZ_ASSERT(elementSize.height == mCurrentContext->GetHeight());
MOZ_ASSERT(elementSize.width == mCurrentContext->GetWidth() ||
(elementSize.width == 0 && mCurrentContext->GetWidth() == 1));
MOZ_ASSERT(elementSize.height == mCurrentContext->GetHeight() ||
(elementSize.height == 0 && mCurrentContext->GetHeight() == 1));
}
#endif
bool fallbackToPNG = false;
nsCOMPtr<nsIScriptContext> scriptContext =
GetScriptContextFromJSContext(nsContentUtils::GetCurrentJSContext());
nsCOMPtr<nsIInputStream> stream;
aRv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
// If there are unrecognized custom parse options, we should fall back to
// the default values for the encoder without any options at all.
if (aRv.ErrorCode() == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
fallbackToPNG = false;
aRv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
uint8_t* imageBuffer = nullptr;
int32_t format = 0;
if (mCurrentContext) {
mCurrentContext->GetImageBuffer(&imageBuffer, &format);
}
if (aRv.Failed()) {
return;
}
if (fallbackToPNG) {
type.AssignLiteral("image/png");
}
uint64_t imgSize;
aRv = stream->Available(&imgSize);
if (aRv.Failed()) {
return;
}
if (imgSize > UINT32_MAX) {
aRv.Throw(NS_ERROR_FILE_TOO_BIG);
return;
}
void* imgData = nullptr;
aRv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
if (aRv.Failed()) {
return;
}
// The DOMFile takes ownership of the buffer
nsRefPtr<nsDOMMemoryFile> blob =
new nsDOMMemoryFile(imgData, imgSize, type);
JSContext* cx = nsContentUtils::GetCurrentJSContext();
if (cx) {
JS_updateMallocCounter(cx, imgSize);
}
nsRefPtr<ToBlobRunnable> runnable = new ToBlobRunnable(aCallback, blob);
aRv = NS_DispatchToCurrentThread(runnable);
aRv = ImageEncoder::ExtractDataAsync(type,
params,
usingCustomParseOptions,
imageBuffer,
format,
GetSize(),
mCurrentContext,
scriptContext,
aCallback);
}
already_AddRefed<nsIDOMFile>
@ -682,17 +576,10 @@ HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
const nsAString& aType,
nsIDOMFile** aResult)
{
bool fallbackToPNG = false;
nsCOMPtr<nsIInputStream> stream;
nsresult rv = ExtractData(aType, EmptyString(), getter_AddRefs(stream),
fallbackToPNG);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString type(aType);
if (fallbackToPNG) {
type.AssignLiteral("image/png");
}
nsresult rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
NS_ENSURE_SUCCESS(rv, rv);
uint64_t imgSize;
rv = stream->Available(&imgSize);

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

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

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

@ -16,31 +16,27 @@
const int32_t txExecutionState::kMaxRecursionDepth = 20000;
nsresult txLoadedDocumentsHash::init(txXPathNode* aSourceDocument)
void
txLoadedDocumentsHash::init(txXPathNode* aSourceDocument)
{
mSourceDocument = aSourceDocument;
nsAutoString baseURI;
txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
txLoadedDocumentEntry* entry = PutEntry(baseURI);
if (!entry) {
return NS_ERROR_FAILURE;
}
entry->mDocument = mSourceDocument;
return NS_OK;
PutEntry(baseURI)->mDocument = mSourceDocument;
}
txLoadedDocumentsHash::~txLoadedDocumentsHash()
{
nsAutoString baseURI;
txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
if (mSourceDocument) {
nsAutoString baseURI;
txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
txLoadedDocumentEntry* entry = GetEntry(baseURI);
if (entry) {
delete entry->mDocument.forget();
txLoadedDocumentEntry* entry = GetEntry(baseURI);
if (entry) {
delete entry->mDocument.forget();
}
}
}
@ -118,14 +114,7 @@ txExecutionState::init(const txXPathNode& aNode,
mOutputHandler->startDocument();
// Set up loaded-documents-hash
nsAutoPtr<txXPathNode> document(txXPathNodeUtils::getOwnerDocument(aNode));
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
rv = mLoadedDocuments.init(document);
NS_ENSURE_SUCCESS(rv, rv);
// loaded-documents-hash owns this now
document.forget();
mLoadedDocuments.init(txXPathNodeUtils::getOwnerDocument(aNode));
// Init members
rv = mKeyHash.init();

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

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

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

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

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

@ -4,8 +4,6 @@
#include "nsISupports.idl"
interface nsIDOMMozNetworkStatsInterface;
[scriptable, builtinclass, uuid(3b16fe17-5583-483a-b486-b64a3243221c)]
interface nsIDOMMozNetworkStatsData : nsISupports
{
@ -14,7 +12,7 @@ interface nsIDOMMozNetworkStatsData : nsISupports
readonly attribute jsval date; // Date.
};
[scriptable, builtinclass, uuid(b6fc4b14-628d-4c99-bf4e-e4ed56916cbe)]
[scriptable, builtinclass, uuid(6613ea55-b99c-44f9-91bf-d07da10b9b74)]
interface nsIDOMMozNetworkStats : nsISupports
{
/**
@ -24,12 +22,13 @@ interface nsIDOMMozNetworkStats : nsISupports
readonly attribute DOMString manifestURL;
/**
* Network the returned data belongs to.
* Can be 'mobile', 'wifi' or null.
* If null, stats for both mobile and wifi are returned.
*/
readonly attribute nsIDOMMozNetworkStatsInterface network;
readonly attribute DOMString connectionType;
/**
* Stats for a network.
* Stats for connectionType
*/
readonly attribute jsval data; // array of NetworkStatsData.
// one element per day.

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

@ -6,65 +6,57 @@
interface nsIDOMDOMRequest;
/**
* Represents a data interface for which the manager is recording statistics.
*/
[scriptable, uuid(f540615b-d803-43ff-8200-2a9d145a5645)]
interface nsIDOMMozNetworkStatsInterface : nsISupports
dictionary NetworkStatsOptions
{
readonly attribute long type;
/**
* Id value is '0' for wifi or the iccid for mobile (SIM).
* Connection type used to filter which network stats will be returned:
* 'mobile', 'wifi' or null.
* If null, stats for both mobile and wifi are returned.
*
* Manifest URL used to retrieve network stats per app.
* If null, system stats (regardless of the app) are returned.
*/
readonly attribute DOMString id;
DOMString connectionType;
DOMString manifestURL;
jsval start; // date
jsval end; // date
};
[scriptable, uuid(5fbdcae6-a2cd-47b3-929f-83ac75bd4881)]
[scriptable, uuid(87529a6c-aef6-11e1-a595-4f034275cfa6)]
interface nsIDOMMozNetworkStatsManager : nsISupports
{
/**
* Constants for known interface types.
*/
const long WIFI = 0;
const long MOBILE = 1;
/**
* Find samples between two dates start and end, both included.
* Query network statistics.
*
* If manifestURL is provided, per-app usage is retrieved,
* otherwise the target will be system usage.
* If options.connectionType is not provided, return statistics for all known
* network interfaces.
*
* If success, the request result will be an nsIDOMMozNetworkStats object.
* If options.manifestURL is not provided, return statistics regardless of the app.
*
* If successful, the request result will be an nsIDOMMozNetworkStats object.
*
* If network stats are not available for some dates, then rxBytes &
* txBytes are undefined for those dates.
*/
nsIDOMDOMRequest getSamples(in nsIDOMMozNetworkStatsInterface network,
in jsval start,
in jsval end,
[optional] in DOMString manifestURL);
nsIDOMDOMRequest getNetworkStats(in jsval options);
/**
* Remove all stats related with the provided network from DB.
* Return available connection types.
*/
nsIDOMDOMRequest clearStats(in nsIDOMMozNetworkStatsInterface network);
readonly attribute jsval connectionTypes; // array of DOMStrings.
/**
* Remove all stats in the database.
* Clear all stats from DB.
*/
nsIDOMDOMRequest clearAllStats();
nsIDOMDOMRequest clearAllData();
/**
* Return currently available networks.
* 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;
/**
* Time in milliseconds recorded by the API until present time. All samples
* older than maxStorageAge from now are deleted.
*/
readonly attribute long long maxStorageAge;
readonly attribute long maxStorageSamples;
};

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

@ -4,8 +4,6 @@
#include "nsISupports.idl"
interface nsINetworkInterface;
[scriptable, function, uuid(5f821529-1d80-4ab5-a933-4e1b3585b6bc)]
interface nsINetworkStatsServiceProxyCallback : nsISupports
{
@ -16,20 +14,20 @@ interface nsINetworkStatsServiceProxyCallback : nsISupports
void notify(in boolean aResult, in jsval aMessage);
};
[scriptable, uuid(facef032-3fd9-4509-a396-83d94c1a11ae)]
[scriptable, uuid(8fbd115d-f590-474c-96dc-e2b6803ca975)]
interface nsINetworkStatsServiceProxy : nsISupports
{
/*
* An interface used to record per-app traffic data.
* @param aAppId app id
* @param aNetworkInterface network
* @param aConnectionType network connection type (0 for wifi, 1 for mobile)
* @param aTimeStamp time stamp
* @param aRxBytes received data amount
* @param aTxBytes transmitted data amount
* @param aCallback an optional callback
*/
void saveAppStats(in unsigned long aAppId,
in nsINetworkInterface aNetwork,
in long aConnectionType,
in unsigned long long aTimeStamp,
in unsigned long long aRxBytes,
in unsigned long long aTxBytes,

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

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

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

@ -55,38 +55,9 @@ NetworkStatsData.prototype = {
QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsData])
};
// NetworkStatsInterface
const NETWORKSTATSINTERFACE_CONTRACTID = "@mozilla.org/networkstatsinterface;1";
const NETWORKSTATSINTERFACE_CID = Components.ID("{f540615b-d803-43ff-8200-2a9d145a5645}");
const nsIDOMMozNetworkStatsInterface = Components.interfaces.nsIDOMMozNetworkStatsInterface;
function NetworkStatsInterface(aNetwork) {
if (DEBUG) {
debug("NetworkStatsInterface Constructor");
}
this.type = aNetwork.type;
this.id = aNetwork.id;
}
NetworkStatsInterface.prototype = {
__exposedProps__: {
id: 'r',
type: 'r',
},
classID : NETWORKSTATSINTERFACE_CID,
classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSINTERFACE_CID,
contractID: NETWORKSTATSINTERFACE_CONTRACTID,
classDescription: "NetworkStatsInterface",
interfaces: [nsIDOMMozNetworkStatsInterface],
flags: nsIClassInfo.DOM_OBJECT}),
QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsInterface])
}
// NetworkStats
const NETWORKSTATS_CONTRACTID = "@mozilla.org/networkstats;1";
const NETWORKSTATS_CID = Components.ID("{b6fc4b14-628d-4c99-bf4e-e4ed56916cbe}");
const NETWORKSTATS_CID = Components.ID("{6613ea55-b99c-44f9-91bf-d07da10b9b74}");
const nsIDOMMozNetworkStats = Components.interfaces.nsIDOMMozNetworkStats;
function NetworkStats(aWindow, aStats) {
@ -94,7 +65,7 @@ function NetworkStats(aWindow, aStats) {
debug("NetworkStats Constructor");
}
this.manifestURL = aStats.manifestURL || null;
this.network = new NetworkStatsInterface(aStats.network);
this.connectionType = aStats.connectionType || null;
this.start = aStats.start || null;
this.end = aStats.end || null;
@ -107,7 +78,7 @@ function NetworkStats(aWindow, aStats) {
NetworkStats.prototype = {
__exposedProps__: {
manifestURL: 'r',
network: 'r',
connectionType: 'r',
start: 'r',
end: 'r',
data: 'r',
@ -121,14 +92,13 @@ NetworkStats.prototype = {
flags: nsIClassInfo.DOM_OBJECT}),
QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStats,
nsIDOMMozNetworkStatsData,
nsIDOMMozNetworkStatsInterface])
nsIDOMMozNetworkStatsData])
}
// NetworkStatsManager
const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1";
const NETWORKSTATSMANAGER_CID = Components.ID("{5fbdcae6-a2cd-47b3-929f-83ac75bd4881}");
const NETWORKSTATSMANAGER_CID = Components.ID("{87529a6c-aef6-11e1-a595-4f034275cfa6}");
const nsIDOMMozNetworkStatsManager = Components.interfaces.nsIDOMMozNetworkStatsManager;
function NetworkStatsManager() {
@ -146,64 +116,42 @@ NetworkStatsManager.prototype = {
}
},
getSamples: function getSamples(aNetwork, aStart, aEnd, aManifestURL) {
getNetworkStats: function getNetworkStats(aOptions) {
this.checkPrivileges();
if (aStart.constructor.name !== "Date" ||
aEnd.constructor.name !== "Date" ||
aStart > aEnd) {
if (!aOptions.start || !aOptions.end ||
aOptions.start > aOptions.end) {
throw Components.results.NS_ERROR_INVALID_ARG;
}
let request = this.createRequest();
cpmm.sendAsyncMessage("NetworkStats:Get",
{ network: aNetwork,
start: aStart,
end: aEnd,
manifestURL: aManifestURL,
id: this.getRequestId(request) });
{data: aOptions, id: this.getRequestId(request)});
return request;
},
clearStats: function clearStats(aNetwork) {
clearAllData: function clearAllData() {
this.checkPrivileges();
let request = this.createRequest();
cpmm.sendAsyncMessage("NetworkStats:Clear",
{ network: aNetwork,
id: this.getRequestId(request) });
return request;
},
clearAllStats: function clearAllStats() {
this.checkPrivileges();
let request = this.createRequest();
cpmm.sendAsyncMessage("NetworkStats:ClearAll",
{id: this.getRequestId(request)});
return request;
},
get availableNetworks() {
get connectionTypes() {
this.checkPrivileges();
let result = ObjectWrapper.wrap(cpmm.sendSyncMessage("NetworkStats:Networks")[0], this._window);
let networks = this.data = Cu.createArrayIn(this._window);
for (let i = 0; i < result.length; i++) {
networks.push(new NetworkStatsInterface(result[i]));
}
return networks;
return ObjectWrapper.wrap(cpmm.sendSyncMessage("NetworkStats:Types")[0], this._window);
},
get sampleRate() {
this.checkPrivileges();
return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0];
return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0] / 1000;
},
get maxStorageAge() {
get maxStorageSamples() {
this.checkPrivileges();
return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0];
return cpmm.sendSyncMessage("NetworkStats:MaxStorageSamples")[0];
},
receiveMessage: function(aMessage) {
@ -235,7 +183,6 @@ NetworkStatsManager.prototype = {
break;
case "NetworkStats:Clear:Return":
case "NetworkStats:ClearAll:Return":
if (msg.error) {
Services.DOMRequest.fireError(req, msg.error);
return;
@ -275,8 +222,7 @@ NetworkStatsManager.prototype = {
}
this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return",
"NetworkStats:Clear:Return",
"NetworkStats:ClearAll:Return"]);
"NetworkStats:Clear:Return"]);
},
// Called from DOMRequestIpcHelper
@ -299,6 +245,5 @@ NetworkStatsManager.prototype = {
}
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsData,
NetworkStatsInterface,
NetworkStats,
NetworkStatsManager]);

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -5,7 +5,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/NetworkStatsDB.jsm");
const netStatsDb = new NetworkStatsDB();
const netStatsDb = new NetworkStatsDB(this);
function filterTimestamp(date) {
var sampleRate = netStatsDb.sampleRate;
@ -13,15 +13,6 @@ function filterTimestamp(date) {
return Math.floor((date.getTime() - offset) / sampleRate) * sampleRate;
}
function getNetworks() {
return [{ id: '0', type: Ci.nsIDOMMozNetworkStatsManager.WIFI },
{ id: '1234', type: Ci.nsIDOMMozNetworkStatsManager.MOBILE }];
}
function compareNetworks(networkA, networkB) {
return (networkA[0] == networkB[0] && networkA[1] == networkB[1]);
}
add_test(function test_sampleRate() {
var sampleRate = netStatsDb.sampleRate;
do_check_true(sampleRate > 0);
@ -98,31 +89,20 @@ add_test(function test_fillResultSamples_noEmptyData() {
});
add_test(function test_clear() {
var networks = getNetworks();
netStatsDb.clearStats(networks, function (error, result) {
do_check_eq(error, null);
run_next_test();
});
});
add_test(function test_clear_interface() {
var networks = getNetworks();
netStatsDb.clearInterfaceStats(networks[0], function (error, result) {
netStatsDb.clear(function (error, result) {
do_check_eq(error, null);
run_next_test();
});
});
add_test(function test_internalSaveStats_singleSample() {
var networks = getNetworks();
var stats = { appId: 0,
network: [networks[0].id, networks[0].type],
timestamp: Date.now(),
rxBytes: 0,
txBytes: 0,
rxTotalBytes: 1234,
txTotalBytes: 1234 };
var stats = {appId: 0,
connectionType: "wifi",
timestamp: Date.now(),
rxBytes: 0,
txBytes: 0,
rxTotalBytes: 1234,
txTotalBytes: 1234};
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
netStatsDb._saveStats(txn, store, stats);
@ -133,7 +113,7 @@ add_test(function test_internalSaveStats_singleSample() {
do_check_eq(error, null);
do_check_eq(result.length, 1);
do_check_eq(result[0].appId, stats.appId);
do_check_true(compareNetworks(result[0].network, stats.network));
do_check_eq(result[0].connectionType, stats.connectionType);
do_check_eq(result[0].timestamp, stats.timestamp);
do_check_eq(result[0].rxBytes, stats.rxBytes);
do_check_eq(result[0].txBytes, stats.txBytes);
@ -145,23 +125,19 @@ add_test(function test_internalSaveStats_singleSample() {
});
add_test(function test_internalSaveStats_arraySamples() {
var networks = getNetworks();
netStatsDb.clearStats(networks, function (error, result) {
netStatsDb.clear(function (error, result) {
do_check_eq(error, null);
var network = [networks[0].id, networks[0].type];
var samples = 2;
var stats = [];
for (var i = 0; i < samples; i++) {
stats.push({ appId: 0,
network: network,
timestamp: Date.now() + (10 * i),
rxBytes: 0,
txBytes: 0,
rxTotalBytes: 1234,
txTotalBytes: 1234 });
stats.push({appId: 0,
connectionType: "wifi",
timestamp: Date.now() + (10 * i),
rxBytes: 0,
txBytes: 0,
rxTotalBytes: 1234,
txTotalBytes: 1234});
}
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
@ -171,16 +147,12 @@ add_test(function test_internalSaveStats_arraySamples() {
netStatsDb.logAllRecords(function(error, result) {
do_check_eq(error, null);
// Result has one sample more than samples because clear inserts
// an empty sample to keep totalBytes synchronized with netd counters
result.shift();
do_check_eq(result.length, samples);
var success = true;
for (var i = 1; i < samples; i++) {
for (var i = 0; i < samples; i++) {
if (result[i].appId != stats[i].appId ||
!compareNetworks(result[i].network, stats[i].network) ||
result[i].connectionType != stats[i].connectionType ||
result[i].timestamp != stats[i].timestamp ||
result[i].rxBytes != stats[i].rxBytes ||
result[i].txBytes != stats[i].txBytes ||
@ -198,31 +170,28 @@ add_test(function test_internalSaveStats_arraySamples() {
});
add_test(function test_internalRemoveOldStats() {
var networks = getNetworks();
netStatsDb.clearStats(networks, function (error, result) {
netStatsDb.clear(function (error, result) {
do_check_eq(error, null);
var network = [networks[0].id, networks[0].type];
var samples = 10;
var stats = [];
for (var i = 0; i < samples - 1; i++) {
stats.push({ appId: 0,
network: network, timestamp: Date.now() + (10 * i),
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234 });
stats.push({appId: 0,
connectionType: "wifi", timestamp: Date.now() + (10 * i),
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234});
}
stats.push({ appId: 0,
network: network, timestamp: Date.now() + (10 * samples),
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234 });
stats.push({appId: 0,
connectionType: "wifi", timestamp: Date.now() + (10 * samples),
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234});
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
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._removeOldStats(txn, store, 0, network, date);
netStatsDb._removeOldStats(txn, store, 0, "wifi", date);
}, function(error, result) {
do_check_eq(error, null);
@ -236,14 +205,14 @@ add_test(function test_internalRemoveOldStats() {
});
});
function processSamplesDiff(networks, lastStat, newStat, callback) {
netStatsDb.clearStats(networks, function (error, result){
function processSamplesDiff(lastStat, newStat, callback) {
netStatsDb.clear(function (error, result){
do_check_eq(error, null);
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
netStatsDb._saveStats(txn, store, lastStat);
}, function(error, result) {
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
let request = store.index("network").openCursor(newStat.network, "prev");
let request = store.index("connectionType").openCursor(newStat.connectionType, "prev");
request.onsuccess = function onsuccess(event) {
let cursor = event.target.result;
do_check_neq(cursor, null);
@ -261,26 +230,22 @@ function processSamplesDiff(networks, lastStat, newStat, callback) {
}
add_test(function test_processSamplesDiffSameSample() {
var networks = getNetworks();
var network = [networks[0].id, networks[0].type];
var sampleRate = netStatsDb.sampleRate;
var date = filterTimestamp(new Date());
var lastStat = {appId: 0,
connectionType: "wifi", timestamp: date,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234};
var lastStat = { appId: 0,
network: network, timestamp: date,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234 };
var newStat = {appId: 0,
connectionType: "wifi", timestamp: date,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 2234, txTotalBytes: 2234};
var newStat = { appId: 0,
network: network, timestamp: date,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 2234, txTotalBytes: 2234 };
processSamplesDiff(networks, lastStat, newStat, function(result) {
processSamplesDiff(lastStat, newStat, function(result) {
do_check_eq(result.length, 1);
do_check_eq(result[0].appId, newStat.appId);
do_check_true(compareNetworks(result[0].network, newStat.network));
do_check_eq(result[0].connectionType, newStat.connectionType);
do_check_eq(result[0].timestamp, newStat.timestamp);
do_check_eq(result[0].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
do_check_eq(result[0].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
@ -291,26 +256,22 @@ add_test(function test_processSamplesDiffSameSample() {
});
add_test(function test_processSamplesDiffNextSample() {
var networks = getNetworks();
var network = [networks[0].id, networks[0].type];
var sampleRate = netStatsDb.sampleRate;
var date = filterTimestamp(new Date());
var lastStat = {appId: 0,
connectionType: "wifi", timestamp: date,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234};
var lastStat = { appId: 0,
network: network, timestamp: date,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234 };
var newStat = {appId: 0,
connectionType: "wifi", timestamp: date + sampleRate,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 500, txTotalBytes: 500};
var newStat = { appId: 0,
network: network, timestamp: date + sampleRate,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 500, txTotalBytes: 500 };
processSamplesDiff(networks, lastStat, newStat, function(result) {
processSamplesDiff(lastStat, newStat, function(result) {
do_check_eq(result.length, 2);
do_check_eq(result[1].appId, newStat.appId);
do_check_true(compareNetworks(result[1].network, newStat.network));
do_check_eq(result[1].connectionType, newStat.connectionType);
do_check_eq(result[1].timestamp, newStat.timestamp);
do_check_eq(result[1].rxBytes, newStat.rxTotalBytes);
do_check_eq(result[1].txBytes, newStat.txTotalBytes);
@ -321,25 +282,23 @@ add_test(function test_processSamplesDiffNextSample() {
});
add_test(function test_processSamplesDiffSamplesLost() {
var networks = getNetworks();
var network = [networks[0].id, networks[0].type];
var samples = 5;
var sampleRate = netStatsDb.sampleRate;
var date = filterTimestamp(new Date());
var lastStat = { appId: 0,
network: network, timestamp: date,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234 };
var lastStat = {appId: 0,
connectionType: "wifi", timestamp: date,
rxBytes: 0, txBytes: 0,
rxTotalBytes: 1234, txTotalBytes: 1234};
var newStat = { appId: 0,
network: network, timestamp: date + (sampleRate * samples),
rxBytes: 0, txBytes: 0,
rxTotalBytes: 2234, txTotalBytes: 2234 };
var newStat = {appId: 0,
connectionType: "wifi", timestamp: date + (sampleRate * samples),
rxBytes: 0, txBytes: 0,
rxTotalBytes: 2234, txTotalBytes: 2234};
processSamplesDiff(networks, lastStat, newStat, function(result) {
processSamplesDiff(lastStat, newStat, function(result) {
do_check_eq(result.length, samples + 1);
do_check_eq(result[0].appId, newStat.appId);
do_check_true(compareNetworks(result[samples].network, newStat.network));
do_check_eq(result[samples].connectionType, newStat.connectionType);
do_check_eq(result[samples].timestamp, newStat.timestamp);
do_check_eq(result[samples].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
do_check_eq(result[samples].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
@ -350,17 +309,13 @@ add_test(function test_processSamplesDiffSamplesLost() {
});
add_test(function test_saveStats() {
var networks = getNetworks();
var network = [networks[0].id, networks[0].type];
var stats = {appId: 0,
connectionType: "wifi",
date: new Date(),
rxBytes: 2234,
txBytes: 2234};
var stats = { appId: 0,
networkId: networks[0].id,
networkType: networks[0].type,
date: new Date(),
rxBytes: 2234,
txBytes: 2234};
netStatsDb.clearStats(networks, function (error, result) {
netStatsDb.clear(function (error, result) {
do_check_eq(error, null);
netStatsDb.saveStats(stats, function(error, result) {
do_check_eq(error, null);
@ -368,7 +323,7 @@ add_test(function test_saveStats() {
do_check_eq(error, null);
do_check_eq(result.length, 1);
do_check_eq(result[0].appId, stats.appId);
do_check_true(compareNetworks(result[0].network, network));
do_check_eq(result[0].connectionType, stats.connectionType);
let timestamp = filterTimestamp(stats.date);
do_check_eq(result[0].timestamp, timestamp);
do_check_eq(result[0].rxBytes, 0);
@ -382,44 +337,35 @@ add_test(function test_saveStats() {
});
add_test(function test_saveAppStats() {
var networks = getNetworks();
var network = [networks[0].id, networks[0].type];
var stats = {appId: 1,
connectionType: "wifi",
date: new Date(),
rxBytes: 2234,
txBytes: 2234};
var stats = { appId: 1,
networkId: networks[0].id,
networkType: networks[0].type,
date: new Date(),
rxBytes: 2234,
txBytes: 2234};
netStatsDb.clearStats(networks, function (error, result) {
netStatsDb.clear(function (error, result) {
do_check_eq(error, null);
netStatsDb.saveStats(stats, function(error, result) {
do_check_eq(error, null);
netStatsDb.logAllRecords(function(error, result) {
do_check_eq(error, null);
// The clear function clears all records of the datbase but
// inserts a new element for each [appId, connectionId, connectionType]
// record to keep the track of rxTotalBytes / txTotalBytes.
// So at this point, we have two records, one for the appId 0 used in
// past tests and the new one for appId 1
do_check_eq(result.length, 2);
do_check_eq(result[1].appId, stats.appId);
do_check_true(compareNetworks(result[1].network, network));
do_check_eq(result.length, 1);
do_check_eq(result[0].appId, stats.appId);
do_check_eq(result[0].connectionType, stats.connectionType);
let timestamp = filterTimestamp(stats.date);
do_check_eq(result[1].timestamp, timestamp);
do_check_eq(result[1].rxBytes, stats.rxBytes);
do_check_eq(result[1].txBytes, stats.txBytes);
do_check_eq(result[1].rxTotalBytes, 0);
do_check_eq(result[1].txTotalBytes, 0);
do_check_eq(result[0].timestamp, timestamp);
do_check_eq(result[0].rxBytes, stats.rxBytes);
do_check_eq(result[0].txBytes, stats.txBytes);
do_check_eq(result[0].rxTotalBytes, 0);
do_check_eq(result[0].txTotalBytes, 0);
run_next_test();
});
});
});
});
function prepareFind(network, stats, callback) {
netStatsDb.clearStats(network, function (error, result) {
function prepareFind(stats, callback) {
netStatsDb.clear(function (error, result) {
do_check_eq(error, null);
netStatsDb.dbNewTxn("readwrite", function(txn, store) {
netStatsDb._saveStats(txn, store, stats);
@ -430,11 +376,6 @@ function prepareFind(network, stats, callback) {
}
add_test(function test_find () {
var networks = getNetworks();
var networkWifi = [networks[0].id, networks[0].type];
var networkMobile = [networks[1].id, networks[1].type]; // Fake mobile interface
var appId = 0;
var samples = 5;
var sampleRate = netStatsDb.sampleRate;
var start = Date.now();
@ -443,39 +384,46 @@ add_test(function test_find () {
start = new Date(start - sampleRate);
var stats = [];
for (var i = 0; i < samples; i++) {
stats.push({ appId: appId,
network: networkWifi, timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0 });
stats.push({appId: 0,
connectionType: "wifi", timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0});
stats.push({ appId: appId,
network: networkMobile, timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0 });
stats.push({appId: 0,
connectionType: "mobile", timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0});
}
prepareFind(networks[0], stats, function(error, result) {
prepareFind(stats, function(error, result) {
do_check_eq(error, null);
netStatsDb.find(function (error, result) {
do_check_eq(error, null);
do_check_eq(result.network.id, networks[0].id);
do_check_eq(result.network.type, networks[0].type);
do_check_eq(result.connectionType, "wifi");
do_check_eq(result.start.getTime(), start.getTime());
do_check_eq(result.end.getTime(), end.getTime());
do_check_eq(result.data.length, samples + 1);
do_check_eq(result.data[0].rxBytes, null);
do_check_eq(result.data[1].rxBytes, 0);
do_check_eq(result.data[samples].rxBytes, 0);
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 () {
var networks = getNetworks();
var networkWifi = [networks[0].id, networks[0].type];
var networkMobile = [networks[1].id, networks[1].type]; // Fake mobile interface
var samples = 5;
var sampleRate = netStatsDb.sampleRate;
var start = Date.now();
@ -484,63 +432,69 @@ add_test(function test_findAppStats () {
start = new Date(start - sampleRate);
var stats = [];
for (var i = 0; i < samples; i++) {
stats.push({ appId: 1,
network: networkWifi, timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0 });
stats.push({appId: 1,
connectionType: "wifi", timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0});
stats.push({ appId: 1,
network: networkMobile, timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0 });
stats.push({appId: 1,
connectionType: "mobile", timestamp: saveDate + (sampleRate * i),
rxBytes: 0, txBytes: 10,
rxTotalBytes: 0, txTotalBytes: 0});
}
prepareFind(networks[0], stats, function(error, result) {
prepareFind(stats, function(error, result) {
do_check_eq(error, null);
netStatsDb.find(function (error, result) {
do_check_eq(error, null);
do_check_eq(result.network.id, networks[0].id);
do_check_eq(result.network.type, networks[0].type);
do_check_eq(result.connectionType, "wifi");
do_check_eq(result.start.getTime(), start.getTime());
do_check_eq(result.end.getTime(), end.getTime());
do_check_eq(result.data.length, samples + 1);
do_check_eq(result.data[0].rxBytes, null);
do_check_eq(result.data[1].rxBytes, 0);
do_check_eq(result.data[samples].rxBytes, 0);
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 () {
var networks = getNetworks();
var networkWifi = networks[0];
var networkMobile = networks[1]; // Fake mobile interface
var saveDate = filterTimestamp(new Date());
var cached = Object.create(null);
cached['1wifi'] = { appId: 1, date: new Date(),
networkId: networkWifi.id, networkType: networkWifi.type,
rxBytes: 0, txBytes: 10 };
cached['1wifi'] = {appId: 1,
connectionType: "wifi", date: new Date(),
rxBytes: 0, txBytes: 10};
cached['1mobile'] = { appId: 1, date: new Date(),
networkId: networkMobile.id, networkType: networkMobile.type,
rxBytes: 0, txBytes: 10 };
cached['1mobile'] = {appId: 1,
connectionType: "mobile", date: new Date(),
rxBytes: 0, txBytes: 10};
cached['2wifi'] = { appId: 2, date: new Date(),
networkId: networkWifi.id, networkType: networkWifi.type,
rxBytes: 0, txBytes: 10 };
cached['2wifi'] = {appId: 2,
connectionType: "wifi", date: new Date(),
rxBytes: 0, txBytes: 10};
cached['2mobile'] = { appId: 2, date: new Date(),
networkId: networkMobile.id, networkType: networkMobile.type,
rxBytes: 0, txBytes: 10 };
cached['2mobile'] = {appId: 2,
connectionType: "mobile", date: new Date(),
rxBytes: 0, txBytes: 10};
let keys = Object.keys(cached);
let index = 0;
networks.push(networkMobile);
netStatsDb.clearStats(networks, function (error, result) {
netStatsDb.clear(function (error, result) {
do_check_eq(error, null);
netStatsDb.saveStats(cached[keys[index]],
function callback(error, result) {
@ -548,17 +502,10 @@ add_test(function test_saveMultipleAppStats () {
if (index == keys.length - 1) {
netStatsDb.logAllRecords(function(error, result) {
// Again, result has two samples more than expected samples because
// clear inserts one empty sample for each network to keep totalBytes
// synchronized with netd counters. so the first two samples have to
// be discarted.
result.shift();
result.shift();
do_check_eq(error, null);
do_check_eq(result.length, 4);
do_check_eq(result[0].appId, 1);
do_check_true(compareNetworks(result[0].network,[networkWifi.id, networkWifi.type]));
do_check_eq(result[0].connectionType, 'mobile');
do_check_eq(result[0].rxBytes, 0);
do_check_eq(result[0].txBytes, 10);
run_next_test();

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

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

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

@ -9,66 +9,33 @@ XPCOMUtils.defineLazyServiceGetter(this, "nssProxy",
"@mozilla.org/networkstatsServiceProxy;1",
"nsINetworkStatsServiceProxy");
function mokConvertNetworkInterface() {
NetworkStatsService.convertNetworkInterface = function(aNetwork) {
if (aNetwork.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE &&
aNetwork.type != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
return null;
}
let id = '0';
if (aNetwork.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
id = '1234'
}
let netId = this.getNetworkId(id, aNetwork.type);
if (!this._networks[netId]) {
this._networks[netId] = Object.create(null);
this._networks[netId].network = { id: id,
type: aNetwork.type };
}
return netId;
};
}
add_test(function test_saveAppStats() {
var cachedAppStats = NetworkStatsService.cachedAppStats;
var timestamp = NetworkStatsService.cachedAppStatsDate.getTime();
var samples = 5;
// Create to fake nsINetworkInterfaces. As nsINetworkInterface can not
// be instantiated, these two vars will emulate it by filling the properties
// that will be used.
var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
// Insert fake mobile network interface in NetworkStatsService
var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
do_check_eq(Object.keys(cachedAppStats).length, 0);
for (var i = 0; i < samples; i++) {
nssProxy.saveAppStats(1, wifi, timestamp, 10, 20);
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
timestamp, 10, 20);
nssProxy.saveAppStats(1, mobile, timestamp, 10, 20);
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
timestamp, 10, 20);
}
var key1 = 1 + NetworkStatsService.getNetworkId(wifi.id, wifi.type);
var key2 = 1 + mobileNetId;
var key1 = 1 + 'wifi';
var key2 = 1 + 'mobile';
do_check_eq(Object.keys(cachedAppStats).length, 2);
do_check_eq(cachedAppStats[key1].appId, 1);
do_check_eq(cachedAppStats[key1].networkId, wifi.id);
do_check_eq(cachedAppStats[key1].networkType, wifi.type);
do_check_eq(cachedAppStats[key1].connectionType, 'wifi');
do_check_eq(new Date(cachedAppStats[key1].date).getTime() / 1000,
Math.floor(timestamp / 1000));
do_check_eq(cachedAppStats[key1].rxBytes, 50);
do_check_eq(cachedAppStats[key1].txBytes, 100);
do_check_eq(cachedAppStats[key2].appId, 1);
do_check_eq(cachedAppStats[key2].networkId, mobile.id);
do_check_eq(cachedAppStats[key2].networkType, mobile.type);
do_check_eq(cachedAppStats[key2].connectionType, 'mobile');
do_check_eq(new Date(cachedAppStats[key2].date).getTime() / 1000,
Math.floor(timestamp / 1000));
do_check_eq(cachedAppStats[key2].rxBytes, 50);
@ -80,11 +47,7 @@ add_test(function test_saveAppStats() {
add_test(function test_saveAppStatsWithDifferentDates() {
var today = NetworkStatsService.cachedAppStatsDate;
var tomorrow = new Date(today.getTime() + (24 * 60 * 60 * 1000));
var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
var key = 1 + NetworkStatsService.getNetworkId(wifi.id, wifi.type);
var key = 1 + 'wifi';
NetworkStatsService.updateCachedAppStats(
function (success, msg) {
@ -92,20 +55,21 @@ add_test(function test_saveAppStatsWithDifferentDates() {
do_check_eq(Object.keys(NetworkStatsService.cachedAppStats).length, 0);
nssProxy.saveAppStats(1, wifi, today.getTime(), 10, 20);
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
today.getTime(), 10, 20);
nssProxy.saveAppStats(1, mobile, today.getTime(), 10, 20);
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
today.getTime(), 10, 20);
var saveAppStatsCb = {
notify: function notify(success, message) {
do_check_eq(success, true);
var cachedAppStats = NetworkStatsService.cachedAppStats;
var key = 2 + NetworkStatsService.getNetworkId(mobile.id, mobile.type);
var key = 2 + 'mobile';
do_check_eq(Object.keys(cachedAppStats).length, 1);
do_check_eq(cachedAppStats[key].appId, 2);
do_check_eq(cachedAppStats[key].networkId, mobile.id);
do_check_eq(cachedAppStats[key].networkType, mobile.type);
do_check_eq(cachedAppStats[key].connectionType, 'mobile');
do_check_eq(new Date(cachedAppStats[key].date).getTime() / 1000,
Math.floor(tomorrow.getTime() / 1000));
do_check_eq(cachedAppStats[key].rxBytes, 30);
@ -115,7 +79,8 @@ add_test(function test_saveAppStatsWithDifferentDates() {
}
};
nssProxy.saveAppStats(2, mobile, tomorrow.getTime(), 30, 40, saveAppStatsCb);
nssProxy.saveAppStats(2, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
tomorrow.getTime(), 30, 40, saveAppStatsCb);
}
);
});
@ -123,7 +88,6 @@ add_test(function test_saveAppStatsWithDifferentDates() {
add_test(function test_saveAppStatsWithMaxCachedTraffic() {
var timestamp = NetworkStatsService.cachedAppStatsDate.getTime();
var maxtraffic = NetworkStatsService.maxCachedTraffic;
var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
NetworkStatsService.updateCachedAppStats(
function (success, msg) {
@ -132,11 +96,13 @@ add_test(function test_saveAppStatsWithMaxCachedTraffic() {
var cachedAppStats = NetworkStatsService.cachedAppStats;
do_check_eq(Object.keys(cachedAppStats).length, 0);
nssProxy.saveAppStats(1, wifi, timestamp, 10, 20);
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
timestamp, 10, 20);
do_check_eq(Object.keys(cachedAppStats).length, 1);
nssProxy.saveAppStats(1, wifi, timestamp, maxtraffic, 20);
nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
timestamp, maxtraffic, 20);
do_check_eq(Object.keys(cachedAppStats).length, 0);
@ -149,9 +115,5 @@ function run_test() {
Cu.import("resource://gre/modules/NetworkStatsService.jsm");
// Function convertNetworkInterface of NetworkStatsService causes errors when dealing
// with RIL to get the iccid, so overwrite it.
mokConvertNetworkInterface();
run_next_test();
}

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

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

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

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

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

@ -73,9 +73,6 @@ function WifiGeoPositionProvider() {
this.timer = null;
this.hasSeenWiFi = false;
this.started = false;
// this is only used when logging is enabled, to debug interactions with the
// geolocation service
this.highAccuracy = false;
}
WifiGeoPositionProvider.prototype = {
@ -135,12 +132,10 @@ WifiGeoPositionProvider.prototype = {
},
setHighAccuracy: function(enable) {
this.highAccuracy = enable;
LOG("setting highAccuracy to " + (this.highAccuracy?"TRUE":"FALSE"));
},
onChange: function(accessPoints) {
LOG("onChange called, highAccuracy = " + (this.highAccuracy?"TRUE":"FALSE"));
LOG("onChange called");
this.hasSeenWiFi = true;
let url = Services.urlFormatter.formatURLPref("geo.wifi.uri");

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

@ -1,104 +0,0 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
const providerCID = Components.ID("{14aa4b81-e266-45cb-88f8-89595dece114}");
const providerContract = "@mozilla.org/geolocation/provider;1";
const categoryName = "geolocation-provider";
var provider = {
QueryInterface: function eventsink_qi(iid) {
if (iid.equals(Components.interfaces.nsISupports) ||
iid.equals(Components.interfaces.nsIFactory) ||
iid.equals(Components.interfaces.nsIGeolocationProvider))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
createInstance: function eventsink_ci(outer, iid) {
if (outer)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return this.QueryInterface(iid);
},
lockFactory: function eventsink_lockf(lock) {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
},
startup: function() {
},
watch: function() {
},
shutdown: function() {
},
setHighAccuracy: function(enable) {
this._isHigh = enable;
if (enable) {
this._seenHigh = true;
}
},
_isHigh: false,
_seenHigh: false
};
let runningInParent = true;
try {
runningInParent = Components.classes["@mozilla.org/xre/runtime;1"].
getService(Components.interfaces.nsIXULRuntime).processType
== Components.interfaces.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
}
catch (e) { }
function successCallback()
{
do_check_true(false);
do_test_finished();
}
function errorCallback()
{
do_check_true(false);
do_test_finished();
}
function run_test()
{
if (runningInParent) {
// XPCShell does not get a profile by default. The geolocation service
// depends on the settings service which uses IndexedDB and IndexedDB
// needs a place where it can store databases.
do_get_profile();
Components.manager.nsIComponentRegistrar.registerFactory(providerCID,
"Unit test geo provider", providerContract, provider);
var catMan = Components.classes["@mozilla.org/categorymanager;1"]
.getService(Components.interfaces.nsICategoryManager);
catMan.nsICategoryManager.addCategoryEntry(categoryName, "unit test",
providerContract, false, true);
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
prefs.setBoolPref("geo.testing.ignore_ipc_principal", true);
prefs.setBoolPref("geo.wifi.scan", false);
}
let geolocation = Cc["@mozilla.org/geolocation;1"].createInstance(Ci.nsISupports);
do_test_pending();
let watchID1 = geolocation.watchPosition(successCallback, errorCallback);
let watchID2 = geolocation.watchPosition(successCallback, errorCallback,
{enableHighAccuracy: true});
do_timeout(1000, function() {
geolocation.clearWatch(watchID2);
do_timeout(1000, check_results);
});
}
function check_results()
{
if (runningInParent) {
// check the provider was set to high accuracy during the test
do_check_true(provider._seenHigh);
// check the provider is not currently set to high accuracy
do_check_false(provider._isHigh);
}
do_test_finished();
}

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

@ -1,70 +0,0 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
const providerCID = Components.ID("{14aa4b81-e266-45cb-88f8-89595dece114}");
const providerContract = "@mozilla.org/geolocation/provider;1";
const categoryName = "geolocation-provider";
var provider = {
QueryInterface: function eventsink_qi(iid) {
if (iid.equals(Components.interfaces.nsISupports) ||
iid.equals(Components.interfaces.nsIFactory) ||
iid.equals(Components.interfaces.nsIGeolocationProvider))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
createInstance: function eventsink_ci(outer, iid) {
if (outer)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return this.QueryInterface(iid);
},
lockFactory: function eventsink_lockf(lock) {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
},
startup: function() {
},
watch: function() {
},
shutdown: function() {
},
setHighAccuracy: function(enable) {
this._isHigh = enable;
if (enable) {
this._seenHigh = true;
}
},
_isHigh: false,
_seenHigh: false
};
function run_test()
{
// XPCShell does not get a profile by default. The geolocation service
// depends on the settings service which uses IndexedDB and IndexedDB
// needs a place where it can store databases.
do_get_profile();
Components.manager.nsIComponentRegistrar.registerFactory(providerCID,
"Unit test geo provider", providerContract, provider);
var catMan = Components.classes["@mozilla.org/categorymanager;1"]
.getService(Components.interfaces.nsICategoryManager);
catMan.nsICategoryManager.addCategoryEntry(categoryName, "unit test",
providerContract, false, true);
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
prefs.setBoolPref("geo.testing.ignore_ipc_principal", true);
prefs.setBoolPref("geo.wifi.scan", false);
run_test_in_child("test_geolocation_reset_accuracy.js", check_results);
}
function check_results()
{
// check the provider was set to high accuracy during the test
do_check_true(provider._seenHigh);
// check the provider is not currently set to high accuracy
do_check_false(provider._isHigh);
do_test_finished();
}

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

@ -12,8 +12,3 @@ skip-if = os == "android"
skip-if = os == "android"
[test_geolocation_timeout_wrap.js]
skip-if = os == "mac" || os == "android"
[test_geolocation_reset_accuracy.js]
# Bug 919946: test hangs consistently on Android
skip-if = os == "android"
[test_geolocation_reset_accuracy_wrap.js]
skip-if = os == "mac" || os == "android"

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

@ -54,7 +54,7 @@ function takeReferenceSnapshot() {
"reference div should disappear when it becomes display:none");
}
function myOnStopFrame(aRequest) {
function myOnStopFrame() {
gOnStopFrameCounter++;
ok(true, "myOnStopFrame called");
let currentSnapshot = snapshotWindow(window, false);
@ -64,7 +64,8 @@ function myOnStopFrame(aRequest) {
"at call #" + gOnStopFrameCounter + " to onStopFrame");
cleanUpAndFinish();
}
setTimeout(function() { myOnStopFrame(0, 0); }, 1000);
else
setTimeout(myOnStopFrame, 1);
}
function failTest() {
@ -80,8 +81,6 @@ function cleanUpAndFinish() {
if (gIsTestFinished) {
return;
}
let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
imgLoadingContent.removeObserver(gMyDecoderObserver);
SimpleTest.finish();
gIsTestFinished = true;
}
@ -89,19 +88,12 @@ function cleanUpAndFinish() {
function main() {
takeReferenceSnapshot();
// Create, customize & attach decoder observer
observer = new ImageDecoderObserverStub();
observer.frameComplete = myOnStopFrame;
gMyDecoderObserver =
Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(observer);
let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
imgLoadingContent.addObserver(gMyDecoderObserver);
// We want to test the cold loading behavior, so clear cache in case an
// earlier test got our image in there already.
clearImageCache();
setTimeout(myOnStopFrame, 1);
// kick off image-loading! myOnStopFrame handles the rest.
gImg.setAttribute("src", "lime-anim-100x100.svg");

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

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

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

@ -4,6 +4,16 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
ifndef .PYMAKE
ifeq (,$(MAKE_VERSION))
$(error GNU Make is required)
endif
make_min_ver := 3.81
ifneq ($(make_min_ver),$(firstword $(sort $(make_min_ver) $(MAKE_VERSION))))
$(error GNU Make $(make_min_ver) or higher is required)
endif
endif
TOPLEVEL_BUILD := 1
run_for_side_effects := $(shell echo "MAKE: $(MAKE)")

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

@ -32,6 +32,7 @@
#include "assembler/wtf/Assertions.h"
#include "assembler/wtf/VMTags.h"
#include "js/Utility.h"
namespace JSC {
@ -42,7 +43,14 @@ size_t ExecutableAllocator::determinePageSize()
ExecutablePool::Allocation ExecutableAllocator::systemAlloc(size_t n)
{
void* allocation = mmap(NULL, n, INITIAL_PROTECTION_FLAGS, MAP_PRIVATE | MAP_ANON, VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY, 0);
void* allocation;
#ifdef JSGC_ROOT_ANALYSIS
do {
#endif
allocation = mmap(NULL, n, INITIAL_PROTECTION_FLAGS, MAP_PRIVATE | MAP_ANON, VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY, 0);
#ifdef JSGC_ROOT_ANALYSIS
} while (allocation && JS::IsPoisonedPtr(allocation));
#endif
if (allocation == MAP_FAILED)
allocation = NULL;
ExecutablePool::Allocation alloc = { reinterpret_cast<char*>(allocation), n };

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

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

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

@ -632,7 +632,7 @@ endif
PWD := $(CURDIR)
endif
NSINSTALL_PY := $(PYTHON) $(call core_abspath,$(topsrcdir)/config/nsinstall.py)
NSINSTALL_PY := $(PYTHON) $(abspath $(topsrcdir)/config/nsinstall.py)
# For Pymake, wherever we use nsinstall.py we're also going to try to make it
# a native command where possible. Since native commands can't be used outside
# of single-line commands, we continue to provide INSTALL for general use.
@ -691,7 +691,7 @@ else
IS_LANGUAGE_REPACK = 1
endif
EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/$(1)/en-US,$(call core_realpath,$(L10NBASEDIR))/$(AB_CD)/$(subst /locales,,$(1)))
EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/$(1)/en-US,$(or $(realpath $(L10NBASEDIR)),$(abspath $(L10NBASEDIR)))/$(AB_CD)/$(subst /locales,,$(1)))
ifdef relativesrcdir
LOCALE_SRCDIR ?= $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir))
@ -742,7 +742,7 @@ ifdef MOZ_DEBUG
JAVAC_FLAGS += -g
endif
CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py)
CREATE_PRECOMPLETE_CMD = $(PYTHON) $(abspath $(topsrcdir)/config/createprecomplete.py)
# MDDEPDIR is the subdirectory where dependency files are stored
MDDEPDIR := .deps

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

@ -16,7 +16,7 @@ $(error Do not include functions.mk twice!)
endif
INCLUDED_FUNCTIONS_MK = 1
core_abspath = $(if $(findstring :,$(1)),$(1),$(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1)))
core_realpath = $(if $(realpath $(1)),$(realpath $(1)),$(call core_abspath,$(1)))
core_abspath = $(error core_abspath is unsupported, use $$(abspath) instead)
core_realpath = $(error core_realpath is unsupported)
core_winabspath = $(firstword $(subst /, ,$(call core_abspath,$(1)))):$(subst $(space),,$(patsubst %,\\%,$(wordlist 2,$(words $(subst /, ,$(call core_abspath,$(1)))), $(strip $(subst /, ,$(call core_abspath,$(1)))))))
core_winabspath = $(error core_winabspath is unsupported)

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

@ -4076,11 +4076,13 @@ elif test "$OS_ARCH" != "WINNT" -a "$OS_ARCH" != "OS2"; then
AC_DEFINE(XP_UNIX)
fi
AC_ARG_ENABLE(threadsafe,
[ --enable-threadsafe Enable support for multiple threads.],
[if test "x$enableval" = "xyes"; then
AC_DEFINE(JS_THREADSAFE)
fi],)
JS_THREADSAFE=1
MOZ_ARG_DISABLE_BOOL(threadsafe,
[ --disable-threadsafe Disable support for multiple threads.],
JS_THREADSAFE= )
if test -n "$JS_THREADSAFE"; then
AC_DEFINE(JS_THREADSAFE)
fi
if test "$MOZ_DEBUG"; then
AC_DEFINE(MOZ_REFLOW_PERF)

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

@ -253,8 +253,8 @@ assertEq(f(-1,false), 0);
assertEq(f(-5,false), 1);
assertAsmTypeFail('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return (i32[0]+1)|0 } return f");
assertAsmTypeFail('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] + 1.0); } return f");
new Float64Array(BUF_64KB)[0] = 2.3;
assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] + 2.0) } return f"), this, null, BUF_64KB)(), 2.3+2);
assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] - 2.0) } return f"), this, null, BUF_64KB)(), 2.3-2);
assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] * 2.0) } return f"), this, null, BUF_64KB)(), 2.3*2);
assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] / 2.0) } return f"), this, null, BUF_64KB)(), 2.3/2);

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

@ -91,6 +91,12 @@ assertEq(f(0, 1.3), 1);
assertEq(f(4088, 2.5), 2);
assertEq(f(4096, 3.8), 0);
var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j) {i=i|0;j=+j; f64[i>>3] = j; return (~~f64[i>>3])|0}; return f');
var f = asmLink(code, this, null, new ArrayBuffer(4096));
assertEq(f(0, 1.3), 1);
assertEq(f(4088, 2.5), 2);
assertEq(f(4096, 3.8), 0);
var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j) {i=i|0;j=+j; f64[i>>3] = j; return +f64[i>>3]}; return f');
var f = asmLink(code, this, null, new ArrayBuffer(4096));
assertEq(f(0, 1.3), 1.3);

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

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

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

@ -5,6 +5,8 @@ evaluate("gcparam(\"maxBytes\", gcparam(\"gcBytes\") + 4*1024);");
evaluate("\
function testDontEnum(F) { \
function test() {\
var upvar = \"\";\
function f() { upvar += \"\"; }\
typeof (new test(\"1\")) != 'function'\
}\
test();\

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

@ -0,0 +1,14 @@
// |jit-test| error: TypeError
function $ERROR() {}
function testMultipleArgumentsObjects() {
var testargs = arguments;
var f = function (which) {
var args = [ testargs ];
return args[which][0];
};
var arr = [0, 0, 0, 0, 1];
for (var i = 0; i < arr.length; i++)
$ERROR[i] = f(arr[i]);
}
testMultipleArgumentsObjects()

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

@ -387,8 +387,7 @@ class Type
Signed,
Unsigned,
Intish,
Void,
Unknown
Void
};
private:
@ -414,7 +413,7 @@ class Type
}
bool isIntish() const {
return isInt() || which_ == Intish || which_ == Unknown;
return isInt() || which_ == Intish;
}
bool isDouble() const {
@ -422,7 +421,7 @@ class Type
}
bool isDoublish() const {
return isDouble() || which_ == Doublish || which_ == Unknown;
return isDouble() || which_ == Doublish;
}
bool isVoid() const {
@ -449,7 +448,6 @@ class Type
case Intish:
return MIRType_Int32;
case Void:
case Unknown:
return MIRType_None;
}
MOZ_ASSUME_UNREACHABLE("Invalid Type");
@ -465,7 +463,6 @@ class Type
case Unsigned: return "unsigned";
case Intish: return "intish";
case Void: return "void";
case Unknown: return "unknown";
}
MOZ_ASSUME_UNREACHABLE("Invalid Type");
}
@ -3947,14 +3944,14 @@ CheckCoerceToInt(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *
if (!CheckExpr(f, operand, &operandDef, &operandType))
return false;
if (operandType.isDouble()) {
if (operandType.isDoublish()) {
*def = f.unary<MTruncateToInt32>(operandDef);
*type = Type::Signed;
return true;
}
if (!operandType.isIntish())
return f.failf(operand, "%s is not a subtype of double or intish", operandType.toChars());
return f.failf(operand, "%s is not a subtype of doublish or intish", operandType.toChars());
*def = operandDef;
*type = Type::Signed;
@ -4160,28 +4157,19 @@ CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *typ
if (numAddOrSub > (1<<20))
return f.fail(expr, "too many + or - without intervening coercion");
if (expr->isKind(PNK_ADD)) {
if (lhsType.isInt() && rhsType.isInt()) {
*def = f.binary<MAdd>(lhsDef, rhsDef, MIRType_Int32);
*type = Type::Intish;
} else if (lhsType.isDouble() && rhsType.isDouble()) {
*def = f.binary<MAdd>(lhsDef, rhsDef, MIRType_Double);
*type = Type::Double;
} else {
return f.failf(expr, "operands to + must both be int or double, got %s and %s",
lhsType.toChars(), rhsType.toChars());
}
if (lhsType.isInt() && rhsType.isInt()) {
*def = expr->isKind(PNK_ADD)
? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Int32)
: f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32);
*type = Type::Intish;
} else if (lhsType.isDoublish() && rhsType.isDoublish()) {
*def = expr->isKind(PNK_ADD)
? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Double)
: f.binary<MSub>(lhsDef, rhsDef, MIRType_Double);
*type = Type::Double;
} else {
if (lhsType.isInt() && rhsType.isInt()) {
*def = f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32);
*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());
}
return f.failf(expr, "operands to +/- must both be int or doublish, got %s and %s",
lhsType.toChars(), rhsType.toChars());
}
if (numAddOrSubOut)

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

@ -8845,6 +8845,11 @@ DoInstanceOfFallback(JSContext *cx, ICInstanceOf_Fallback *stub,
RootedObject obj(cx, &rhs.toObject());
// For functions, keep track of the |prototype| property in type information,
// for use during Ion compilation.
if (obj->is<JSFunction>() && IsIonEnabled(cx))
types::EnsureTrackPropertyTypes(cx, obj, NameToId(cx->names().prototype));
bool cond = false;
if (!HasInstance(cx, obj, lhs, &cond))
return false;

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

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

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

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

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

@ -211,16 +211,25 @@ IsPhiObservable(MPhi *phi, Observability observe)
break;
}
// If the Phi is of the |this| value, it must always be observable.
uint32_t slot = phi->slot();
CompileInfo &info = phi->block()->info();
if (info.fun() && slot == info.thisSlot())
JSFunction *fun = info.fun();
// If the Phi is of the |this| value, it must always be observable.
if (fun && slot == info.thisSlot())
return true;
// If the function is heavyweight, and the Phi is of the |scopeChain|
// value, and the function may need an arguments object, then make sure
// to preserve the scope chain, because it may be needed to construct the
// arguments object during bailout.
if (fun && fun->isHeavyweight() && info.hasArguments() && slot == info.scopeChainSlot())
return true;
// If the Phi is one of the formal argument, and we are using an argument
// object in the function. The phi might be observable after a bailout.
// For inlined frames this is not needed, as they are captured in the inlineResumePoint.
if (info.fun() && info.hasArguments()) {
if (fun && info.hasArguments()) {
uint32_t first = info.firstArgSlot();
if (first <= slot && slot - first < info.nargs()) {
// If arguments obj aliases formals, then the arg slots will never be used.

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

@ -7232,8 +7232,14 @@ IonBuilder::setElemTryCache(bool *emitted, MDefinition *object,
return true;
}
// We can avoid worrying about holes in the IC if we know a priori we are safe
// from them. If TI can guard that there are no indexed properties on the prototype
// chain, we know that we anen't missing any setters by overwriting the hole with
// another value.
bool guardHoles = ElementAccessHasExtraIndexedProperty(constraints(), object);
// Emit SetElementCache.
MInstruction *ins = MSetElementCache::New(object, index, value, script()->strict);
MInstruction *ins = MSetElementCache::New(object, index, value, script()->strict, guardHoles);
current->add(ins);
current->push(value);

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

@ -1554,17 +1554,19 @@ GetPropertyIC::tryAttachProxy(JSContext *cx, IonScript *ion, HandleObject obj,
static void
GenerateProxyClassGuards(MacroAssembler &masm, Register object, Register scratchReg,
Label *failures, Label *success)
Label *failures)
{
Label success;
// Ensure that the incoming object has one of the magic class pointers, i.e,
// that it is one of an ObjectProxy, FunctionProxy, or OuterWindowProxy.
// This is equivalent to obj->is<ProxyObject>().
masm.branchTestObjClass(Assembler::Equal, object, scratchReg,
CallableProxyClassPtr, success);
CallableProxyClassPtr, &success);
masm.branchTestObjClass(Assembler::Equal, object, scratchReg,
UncallableProxyClassPtr, success);
UncallableProxyClassPtr, &success);
masm.branchTestObjClass(Assembler::NotEqual, object, scratchReg,
OuterWindowProxyClassPtr, failures);
masm.bind(&success);
}
bool
@ -1592,9 +1594,7 @@ GetPropertyIC::tryAttachGenericProxy(JSContext *cx, IonScript *ion, HandleObject
masm.setFramePushed(ion->frameSize());
Label proxySuccess;
GenerateProxyClassGuards(masm, object(), scratchReg, &failures, &proxySuccess);
masm.bind(&proxySuccess);
GenerateProxyClassGuards(masm, object(), scratchReg, &failures);
// Ensure that the incoming object is not a DOM proxy, so that we can get to
// the specialized stubs
@ -2179,12 +2179,12 @@ SetPropertyIC::attachGenericProxy(JSContext *cx, IonScript *ion, void *returnAdd
Register scratch = regSet.takeGeneral();
masm.push(scratch);
GenerateProxyClassGuards(masm, object(), scratch, &proxyFailures, &proxySuccess);
GenerateProxyClassGuards(masm, object(), scratch, &proxyFailures);
// Remove the DOM proxies. They'll take care of themselves so this stub doesn't
// catch too much.
masm.branchTestProxyHandlerFamily(Assembler::Equal, object(), scratch,
GetDOMProxyHandlerFamily(), &proxyFailures);
// catch too much. The failure case is actually Equal. Fall through to the failure code.
masm.branchTestProxyHandlerFamily(Assembler::NotEqual, object(), scratch,
GetDOMProxyHandlerFamily(), &proxySuccess);
masm.bind(&proxyFailures);
masm.pop(scratch);
@ -3466,14 +3466,17 @@ IsTypedArrayElementSetInlineable(JSObject *obj, const Value &idval, const Value
static bool
GenerateSetDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
JSObject *obj, const Value &idval, Register object, ValueOperand indexVal,
ConstantOrRegister value, Register tempToUnboxIndex, Register temp)
JSObject *obj, const Value &idval, bool guardHoles, Register object,
ValueOperand indexVal, ConstantOrRegister value, Register tempToUnboxIndex,
Register temp)
{
JS_ASSERT(obj->isNative());
JS_ASSERT(idval.isInt32());
Label failures;
Label outOfBounds; // index >= capacity || index > initialized length
Label outOfBounds; // index represents a known hole, or an illegal append
Label markElem, postBarrier; // used if TI protects us from worrying about holes.
// Guard object is a dense array.
Shape *shape = obj->lastProperty();
@ -3495,49 +3498,58 @@ GenerateSetDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttac
// Compute the location of the element.
BaseIndex target(elements, index, TimesEight);
// Guard that we can increase the initialized length.
Address capacity(elements, ObjectElements::offsetOfCapacity());
masm.branch32(Assembler::BelowOrEqual, capacity, index, &outOfBounds);
// If TI cannot help us deal with HOLES by preventing indexed properties
// on the prototype chain, we have to be very careful to check for ourselves
// to avoid stomping on what should be a setter call. Start by only allowing things
// within the initialized length.
if (guardHoles) {
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
masm.branch32(Assembler::BelowOrEqual, initLength, index, &outOfBounds);
} else {
// Guard that we can increase the initialized length.
Address capacity(elements, ObjectElements::offsetOfCapacity());
masm.branch32(Assembler::BelowOrEqual, capacity, index, &outOfBounds);
// Guard on the initialized length.
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
masm.branch32(Assembler::Below, initLength, index, &outOfBounds);
// Guard on the initialized length.
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
masm.branch32(Assembler::Below, initLength, index, &outOfBounds);
// if (initLength == index)
Label markElem, postBarrier;
masm.branch32(Assembler::NotEqual, initLength, index, &markElem);
{
// Increase initialize length.
Int32Key newLength(index);
masm.bumpKey(&newLength, 1);
masm.storeKey(newLength, initLength);
// if (initLength == index)
masm.branch32(Assembler::NotEqual, initLength, index, &markElem);
{
// Increase initialize length.
Int32Key newLength(index);
masm.bumpKey(&newLength, 1);
masm.storeKey(newLength, initLength);
// Increase length if needed.
Label bumpedLength;
Address length(elements, ObjectElements::offsetOfLength());
masm.branch32(Assembler::AboveOrEqual, length, index, &bumpedLength);
masm.storeKey(newLength, length);
masm.bind(&bumpedLength);
// Increase length if needed.
Label bumpedLength;
Address length(elements, ObjectElements::offsetOfLength());
masm.branch32(Assembler::AboveOrEqual, length, index, &bumpedLength);
masm.storeKey(newLength, length);
masm.bind(&bumpedLength);
// Restore the index.
masm.bumpKey(&newLength, -1);
masm.jump(&postBarrier);
}
// else
{
// Mark old element.
// Restore the index.
masm.bumpKey(&newLength, -1);
masm.jump(&postBarrier);
}
// else
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.
masm.bind(&postBarrier);
if (cx->zone()->needsBarrier())
masm.callPreBarrier(target, MIRType_Value);
// Call post barrier if necessary, and recalculate elements pointer if it got clobbered.
if (!guardHoles)
masm.bind(&postBarrier);
Register postBarrierScratch = elements;
if (masm.maybeCallPostBarrier(object, value, postBarrierScratch))
masm.loadPtr(Address(object, JSObject::offsetOfElements()), elements);
// Store the value.
if (guardHoles)
masm.branchTestMagic(Assembler::Equal, target, &failures);
masm.storeConstantOrRegister(value, target);
}
attacher.jumpRejoin(masm);
@ -3556,14 +3568,18 @@ SetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, c
MacroAssembler masm(cx);
RepatchStubAppender attacher(*this);
if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval,
object(), index(), value(),
tempToUnboxIndex(), temp()))
guardHoles(), object(), index(),
value(), tempToUnboxIndex(),
temp()))
{
return false;
}
setHasDenseStub();
return linkAndAttachStub(cx, masm, attacher, ion, "dense array");
const char *message = guardHoles() ?
"dense array (holes)" :
"dense array";
return linkAndAttachStub(cx, masm, attacher, ion, message);
}
static bool
@ -3704,13 +3720,18 @@ SetElementParIC::attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObjec
MacroAssembler masm(cx);
DispatchStubPrepender attacher(*this);
if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval,
object(), index(), value(),
tempToUnboxIndex(), temp()))
guardHoles(), object(), index(),
value(), tempToUnboxIndex(),
temp()))
{
return false;
}
return linkAndAttachStub(cx, masm, attacher, ion, "parallel dense array");
const char *message = guardHoles() ?
"parallel dense array (holes)" :
"parallel dense array";
return linkAndAttachStub(cx, masm, attacher, ion, message);
}
bool

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

@ -808,13 +808,14 @@ class SetElementIC : public RepatchIonCache
ValueOperand index_;
ConstantOrRegister value_;
bool strict_;
bool guardHoles_;
bool hasDenseStub_ : 1;
public:
SetElementIC(Register object, Register tempToUnboxIndex, Register temp,
FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
bool strict)
bool strict, bool guardHoles)
: object_(object),
tempToUnboxIndex_(tempToUnboxIndex),
temp_(temp),
@ -822,6 +823,7 @@ class SetElementIC : public RepatchIonCache
index_(index),
value_(value),
strict_(strict),
guardHoles_(guardHoles),
hasDenseStub_(false)
{
}
@ -851,6 +853,9 @@ class SetElementIC : public RepatchIonCache
bool strict() const {
return strict_;
}
bool guardHoles() const {
return guardHoles_;
}
bool hasDenseStub() const {
return hasDenseStub_;
@ -1182,18 +1187,20 @@ class SetElementParIC : public ParallelIonCache
ValueOperand index_;
ConstantOrRegister value_;
bool strict_;
bool guardHoles_;
public:
SetElementParIC(Register object, Register tempToUnboxIndex, Register temp,
FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
bool strict)
bool strict, bool guardHoles)
: object_(object),
tempToUnboxIndex_(tempToUnboxIndex),
temp_(temp),
tempFloat_(tempFloat),
index_(index),
value_(value),
strict_(strict)
strict_(strict),
guardHoles_(guardHoles)
{
}
@ -1226,6 +1233,9 @@ class SetElementParIC : public ParallelIonCache
bool strict() const {
return strict_;
}
bool guardHoles() const {
return guardHoles_;
}
bool attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj, const Value &idval);
bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, TypedArrayObject *tarr);

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

@ -2802,6 +2802,7 @@ PropertyReadNeedsTypeBarrier(JSContext *cx, types::CompilerConstraintList *const
if (name && object->singleton() && object->singleton()->isNative()) {
Shape *shape = object->singleton()->nativeLookup(cx, name);
if (shape &&
shape->hasSlot() &&
shape->hasDefaultGetter() &&
object->singleton()->nativeGetSlot(shape->slot()).isUndefined())
{

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

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

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

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

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

@ -50,8 +50,6 @@ BEGIN_TEST(testChromeBuffer)
if (!JS_AddNamedObjectRoot(cx, &trusted_glob, "trusted-global"))
return false;
if (!JS_AddNamedObjectRoot(cx, &trusted_fun, "trusted-function"))
return false;
JSFunction *fun;
@ -70,6 +68,8 @@ BEGIN_TEST(testChromeBuffer)
"trusted", 1, &paramName, bytes, strlen(bytes),
"", 0));
trusted_fun = JS_GetFunctionObject(fun);
if (!JS_AddNamedObjectRoot(cx, &trusted_fun, "trusted-function"))
return false;
}
JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun));

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

@ -1,3 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -148,15 +149,15 @@ nsresult MediaPipeline::TransportReady_s(TransportFlow *flow) {
// failure. bug 852665.
if (*state != MP_CONNECTING) {
MOZ_MTLOG(ML_ERROR, "Transport ready for flow in wrong state:" <<
description_ << ": " << (rtcp ? "rtcp" : "rtp"));
description_ << ": " << (rtcp ? "rtcp" : "rtp"));
return NS_ERROR_FAILURE;
}
nsresult res;
MOZ_MTLOG(ML_DEBUG, "Transport ready for pipeline " <<
static_cast<void *>(this) << " flow " << description_ << ": " <<
(rtcp ? "rtcp" : "rtp"));
static_cast<void *>(this) << " flow " << description_ << ": " <<
(rtcp ? "rtcp" : "rtp"));
// Now instantiate the SRTP objects
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
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -83,7 +84,6 @@ class MediaPipeline : public sigslot::has_slots<> {
rtcp_state_(MP_CONNECTING),
main_thread_(main_thread),
sts_thread_(sts_thread),
transport_(new PipelineTransport(this)),
rtp_send_srtp_(),
rtcp_send_srtp_(),
rtp_recv_srtp_(),
@ -102,6 +102,8 @@ class MediaPipeline : public sigslot::has_slots<> {
if (!rtcp_transport_) {
rtcp_transport_ = rtp_transport;
}
// PipelineTransport() will access this->sts_thread_; moved here for safety
transport_ = new PipelineTransport(this);
}
virtual ~MediaPipeline();
@ -145,7 +147,7 @@ class MediaPipeline : public sigslot::has_slots<> {
// Implement the TransportInterface functions
PipelineTransport(MediaPipeline *pipeline)
: pipeline_(pipeline),
sts_thread_(pipeline->sts_thread_) {}
sts_thread_(pipeline->sts_thread_) {}
void Detach() { pipeline_ = NULL; }
MediaPipeline *pipeline() const { return pipeline_; }
@ -183,12 +185,12 @@ class MediaPipeline : public sigslot::has_slots<> {
Direction direction_;
RefPtr<MediaStream> stream_; // A pointer to the stream we are servicing.
// Written on the main thread.
// Used on STS and MediaStreamGraph threads.
// Written on the main thread.
// Used on STS and MediaStreamGraph threads.
TrackID track_id_; // The track on the stream.
// Written and used as the stream_;
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.
RefPtr<TransportFlow> rtp_transport_;
@ -385,10 +387,10 @@ class MediaPipelineTransmit : public MediaPipeline {
const MediaSegment& media);
virtual void ProcessAudioChunk(AudioSessionConduit *conduit,
TrackRate rate, AudioChunk& chunk);
TrackRate rate, AudioChunk& chunk);
#ifdef MOZILLA_INTERNAL_API
virtual void ProcessVideoChunk(VideoSessionConduit *conduit,
TrackRate rate, VideoChunk& chunk);
TrackRate rate, VideoChunk& chunk);
#endif
RefPtr<MediaSessionConduit> conduit_;
volatile bool active_;
@ -517,7 +519,7 @@ class MediaPipelineReceiveVideo : public MediaPipelineReceive {
MediaPipelineReceive(pc, main_thread, sts_thread,
stream, track_id, conduit, rtp_transport,
rtcp_transport),
renderer_(new PipelineRenderer(this)),
renderer_(new PipelineRenderer(MOZ_THIS_IN_INITIALIZER_LIST())),
listener_(new PipelineListener(stream->AsSourceStream(), track_id)) {
}

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

@ -14,9 +14,6 @@
* character literals. C++11's char16_t is a distinct builtin type. C11's
* char16_t is a typedef for uint_least16_t. Technically, char16_t is a 16-bit
* code unit of a Unicode code point, not a "character".
*
* For now, Char16.h only supports C++ because we don't want mix different C
* and C++ definitions of char16_t in the same code base.
*/
#ifdef _MSC_VER
@ -39,10 +36,26 @@
* typedef from wchar_t.
*/
# define MOZ_CHAR16_IS_NOT_WCHAR
#elif !defined(__cplusplus)
# if defined(WIN32)
# include <yvals.h>
typedef wchar_t char16_t;
# else
/**
* We can't use the stdint.h uint16_t type here because including
* stdint.h will break building some of our C libraries, such as
* sqlite.
*/
typedef unsigned short char16_t;
# endif
#else
# error "Char16.h requires C++11 (or something like it) for UTF-16 support."
#endif
/* This is a temporary hack until bug 927728 is fixed. */
#define __PRUNICHAR__
typedef char16_t PRUnichar;
/*
* Macro arguments used in concatenation or stringification won't be expanded.
* Therefore, in order for |MOZ_UTF16(FOO)| to work as expected (which is to
@ -53,9 +66,12 @@
*/
#define MOZ_UTF16(s) MOZ_UTF16_HELPER(s)
#if defined(__cplusplus) && \
(__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__))
static_assert(sizeof(char16_t) == 2, "Is char16_t type 16 bits?");
static_assert(char16_t(-1) > char16_t(0), "Is char16_t type unsigned?");
static_assert(sizeof(MOZ_UTF16('A')) == 2, "Is char literal 16 bits?");
static_assert(sizeof(MOZ_UTF16("")[0]) == 2, "Is string char 16 bits?");
#endif
#endif /* mozilla_Char16_h */

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

@ -14,9 +14,6 @@
* the WeakPtrs to it and allows the WeakReference to live beyond the lifetime
* of 'Foo'.
*
* AtomicSupportsWeakPtr can be used for a variant with an atomically updated
* reference counter.
*
* The overhead of WeakPtr is that accesses to 'Foo' becomes an additional
* dereference, and an additional heap allocated pointer sized object shared
* between all of the WeakPtrs.
@ -63,7 +60,6 @@
#define mozilla_WeakPtr_h
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/NullPtr.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TypeTraits.h"
@ -76,8 +72,8 @@ template <typename T, class WeakReference> class SupportsWeakPtrBase;
namespace detail {
// This can live beyond the lifetime of the class derived from SupportsWeakPtrBase.
template<class T, RefCountAtomicity Atomicity>
class WeakReference : public RefCounted<WeakReference<T, Atomicity>, Atomicity>
template<class T>
class WeakReference : public ::mozilla::RefCounted<WeakReference<T> >
{
public:
explicit WeakReference(T* p) : ptr(p) {}
@ -86,8 +82,8 @@ class WeakReference : public RefCounted<WeakReference<T, Atomicity>, Atomicity>
}
private:
friend class WeakPtrBase<T, WeakReference>;
friend class SupportsWeakPtrBase<T, WeakReference>;
friend class WeakPtrBase<T, WeakReference<T> >;
friend class SupportsWeakPtrBase<T, WeakReference<T> >;
void detach() {
ptr = nullptr;
}
@ -121,30 +117,10 @@ class SupportsWeakPtrBase
};
template <typename T>
class SupportsWeakPtr
: public SupportsWeakPtrBase<T, detail::WeakReference<T, detail::NonAtomicRefCount> >
class SupportsWeakPtr : public SupportsWeakPtrBase<T, detail::WeakReference<T> >
{
};
template <typename T>
class AtomicSupportsWeakPtr
: public SupportsWeakPtrBase<T, detail::WeakReference<T, detail::AtomicRefCount> >
{
};
namespace detail {
template <typename T>
struct WeakReferenceCount
{
static const RefCountAtomicity atomicity =
IsBaseOf<AtomicSupportsWeakPtr<T>, T>::value
? AtomicRefCount
: NonAtomicRefCount;
};
}
template <typename T, class WeakReference>
class WeakPtrBase
{
@ -177,9 +153,9 @@ class WeakPtrBase
};
template <typename T>
class WeakPtr : public WeakPtrBase<T, detail::WeakReference<T, detail::WeakReferenceCount<T>::atomicity> >
class WeakPtr : public WeakPtrBase<T, detail::WeakReference<T> >
{
typedef WeakPtrBase<T, detail::WeakReference<T, detail::WeakReferenceCount<T>::atomicity> > Base;
typedef WeakPtrBase<T, detail::WeakReference<T> > Base;
public:
WeakPtr(const WeakPtr<T>& o) : Base(o) {}
WeakPtr(const Base& o) : Base(o) {}

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

@ -8,12 +8,12 @@ include $(topsrcdir)/config/config.mk
# http://code.google.com/p/android/issues/detail?id=3639
AB_rCD = $(if $(filter he, $(AB_CD)),iw,$(if $(filter id, $(AB_CD)),in,$(subst -,-r,$(AB_CD))))
SYNCSTRINGSPATH = $(call core_abspath,$(call MERGE_FILE,sync_strings.dtd))
STRINGSPATH = $(call core_abspath,$(call MERGE_FILE,android_strings.dtd))
SYNCSTRINGSPATH = $(abspath $(call MERGE_FILE,sync_strings.dtd))
STRINGSPATH = $(abspath $(call MERGE_FILE,android_strings.dtd))
ifeq (,$(XPI_NAME))
BRANDPATH = $(call core_abspath,$(DEPTH)/dist/bin/chrome/$(AB_CD)/locale/branding/brand.dtd)
BRANDPATH = $(abspath $(DEPTH)/dist/bin/chrome/$(AB_CD)/locale/branding/brand.dtd)
else
BRANDPATH = $(call core_abspath,$(DIST)/xpi-stage/$(XPI_NAME)/chrome/$(AB_CD)/locale/branding/brand.dtd)
BRANDPATH = $(abspath $(DIST)/xpi-stage/$(XPI_NAME)/chrome/$(AB_CD)/locale/branding/brand.dtd)
endif
$(warnIfEmpty,AB_CD) # todo: $(errorIfEmpty )
@ -32,7 +32,7 @@ chrome-%::
@$(MAKE) $(dir-res-values)-$(AB_rCD)/strings.xml AB_CD=$*
# setup the path to bookmarks.inc. copied and tweaked version of MERGE_FILE from config/config.mk
MOBILE_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/mobile/locales/en-US,$(call core_realpath,$(L10NBASEDIR))/$(AB_CD)/mobile)
MOBILE_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/mobile/locales/en-US,$(or $(realpath $(L10NBASEDIR)),$(abspath $(L10NBASEDIR)))/$(AB_CD)/mobile)
ifdef LOCALE_MERGEDIR
BOOKMARKSPATH = $(firstword \
@ -40,7 +40,7 @@ BOOKMARKSPATH = $(firstword \
$(wildcard $(MOBILE_LOCALE_SRCDIR)/profile/bookmarks.inc ) \
$(topsrcdir)/mobile/locales/en-US/profile/bookmarks.inc )
else
BOOKMARKSPATH = $(call core_abspath,$(MOBILE_LOCALE_SRCDIR)/profile/bookmarks.inc)
BOOKMARKSPATH = $(abspath $(MOBILE_LOCALE_SRCDIR)/profile/bookmarks.inc)
endif
# Determine the ../res/values[-*]/ path

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

@ -2,7 +2,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
TESTROOT = $(call core_abspath,$(DEPTH))/_tests/xpcshell/$(relativesrcdir)
TESTROOT = $(abspath $(DEPTH))/_tests/xpcshell/$(relativesrcdir)
DEFINES += -DBIN_SUFFIX=$(BIN_SUFFIX)

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

@ -25,6 +25,19 @@
#define __STDC_FORMAT_MACROS
#endif
/*
* Force-include Char16.h in order to define PRUnichar as char16_t everywhere.
* Note that this should be the first #include to make sure that prtypes.h does
* not attempt to define PRUnichar. This includes the following hunspell-specific
* includes.
*
* We don't use this to build elfhack and elf-dynstr-gc since those builds happen
* during the export tier. Also, disable this when building assembly files too.
*/
#if !defined(ELFHACK_BUILD) && !defined(ELFDYNSTRGC_BUILD) && !defined(__ASSEMBLER__)
#include "mozilla/Char16.h"
#endif
/*
* Force-include hunspell_alloc_hooks.h and hunspell_fopen_hooks.h for hunspell,
* so that we don't need to modify them directly.

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

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

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

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

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

@ -87,7 +87,7 @@ NSS_EXTRA_DLLS += freebl_64int_3
NSS_EXTRA_DLLS += freebl_64fpu_3
endif
ABS_DIST := $(call core_abspath,$(DIST))
ABS_DIST := $(abspath $(DIST))
ifeq ($(HOST_OS_ARCH),WINNT)
ifdef CYGDRIVE_MOUNT
ABS_DIST := $(shell cygpath -w $(ABS_DIST) | sed -e 's|\\|/|g')
@ -288,7 +288,7 @@ export::
cp -Rp $(topsrcdir)/security/coreconf $(NSS_SRCDIR)/security
cp -Rp $(topsrcdir)/security/dbm $(NSS_SRCDIR)/security
cp -Rp $(topsrcdir)/dbm $(NSS_SRCDIR)
(cd $(NSS_SRCDIR) && patch -p1 < $(call core_abspath,$(MOZ_NSS_PATCH)))
(cd $(NSS_SRCDIR) && patch -p1 < $(abspath $(MOZ_NSS_PATCH)))
else
NSS_SRCDIR = $(topsrcdir)
endif
@ -450,7 +450,7 @@ endif # MOZ_FOLD_LIBS
ifeq ($(NSINSTALL_PY),$(NSINSTALL))
DEFAULT_GMAKE_FLAGS += PYTHON='$(PYTHON)'
DEFAULT_GMAKE_FLAGS += NSINSTALL_PY='$(call core_abspath,$(topsrcdir)/config/nsinstall.py)'
DEFAULT_GMAKE_FLAGS += NSINSTALL_PY='$(abspath $(topsrcdir)/config/nsinstall.py)'
DEFAULT_GMAKE_FLAGS += NSINSTALL='$$(PYTHON) $$(NSINSTALL_PY)'
else
DEFAULT_GMAKE_FLAGS += NSINSTALL='$(NSINSTALL)'

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

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

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