зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound
--HG-- rename : gfx/skia/skia/src/animator/SkCondensedDebug.cpp => gfx/skia/skia/src/animator/SkCondensedDebug.inc rename : gfx/skia/skia/src/animator/SkCondensedRelease.cpp => gfx/skia/skia/src/animator/SkCondensedRelease.inc
This commit is contained in:
Коммит
31bdfd3ad3
|
@ -407,11 +407,6 @@ pref("dom.mozNetworkStats.enabled", true);
|
|||
pref("dom.webapps.firstRunWithSIM", true);
|
||||
#endif
|
||||
|
||||
// ResourceStats
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
pref("dom.resource_stats.enabled", true);
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
// SingleVariant
|
||||
pref("dom.mozApps.single_variant_sourcedir", "/persist/svoperapps");
|
||||
|
|
|
@ -19,7 +19,6 @@ MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official
|
|||
|
||||
MOZ_NO_SMART_CARDS=1
|
||||
MOZ_APP_STATIC_INI=1
|
||||
MOZ_NO_EV_CERTS=1
|
||||
|
||||
if test "$OS_TARGET" = "Android"; then
|
||||
MOZ_CAPTURE=1
|
||||
|
|
|
@ -442,14 +442,6 @@
|
|||
@RESPATH@/components/TetheringManager.manifest
|
||||
#endif
|
||||
|
||||
; ResourceStats
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
@RESPATH@/components/ResourceStats.js
|
||||
@RESPATH@/components/ResourceStats.manifest
|
||||
@RESPATH@/components/ResourceStatsManager.js
|
||||
@RESPATH@/components/ResourceStatsManager.manifest
|
||||
#endif // MOZ_WIDGET_GONK
|
||||
|
||||
; RIL
|
||||
#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
|
||||
@RESPATH@/components/CellBroadcastService.js
|
||||
|
|
|
@ -2350,15 +2350,33 @@ BrowserGlue.prototype = {
|
|||
const deviceName = Weave.Service.clientsEngine.getClientName(URIs[0].clientId);
|
||||
const bundle = Services.strings.createBundle("chrome://browser/locale/accounts.properties");
|
||||
if (URIs.length == 1) {
|
||||
title = bundle.GetStringFromName("tabArrivingNotification.title");
|
||||
const pageTitle = URIs[0].title || firstTab.linkedBrowser.contentTitle
|
||||
|| URIs[0].uri;
|
||||
body = bundle.formatStringFromName("tabArrivingNotification.body", [pageTitle, deviceName], 2);
|
||||
// Due to bug 1305895, tabs from iOS may not have device information, so
|
||||
// we have separate strings to handle those cases. (See Also
|
||||
// unnamedTabsArrivingNotificationNoDevice.body below)
|
||||
if (deviceName) {
|
||||
title = bundle.formatStringFromName("tabArrivingNotificationWithDevice.title", [deviceName], 1);
|
||||
} else {
|
||||
title = bundle.GetStringFromName("tabArrivingNotification.title");
|
||||
}
|
||||
// Use the page URL as the body. We strip the fragment and query to
|
||||
// reduce size, and also format it the same way that the url bar would.
|
||||
body = URIs[0].uri.replace(/[?#].*$/, "");
|
||||
if (win.gURLBar) {
|
||||
body = win.gURLBar.trimValue(body);
|
||||
}
|
||||
} else {
|
||||
title = bundle.GetStringFromName("tabsArrivingNotification.title");
|
||||
const tabArrivingBody = URIs.every(URI => URI.clientId == URIs[0].clientId) ?
|
||||
"unnamedTabsArrivingNotification.body" :
|
||||
"unnamedTabsArrivingNotificationMultiple.body";
|
||||
const allSameDevice = URIs.every(URI => URI.clientId == URIs[0].clientId);
|
||||
const unknownDevice = allSameDevice && !deviceName;
|
||||
let tabArrivingBody;
|
||||
if (unknownDevice) {
|
||||
tabArrivingBody = "unnamedTabsArrivingNotificationNoDevice.body";
|
||||
} else if (allSameDevice) {
|
||||
tabArrivingBody = "unnamedTabsArrivingNotification2.body";
|
||||
} else {
|
||||
tabArrivingBody = "unnamedTabsArrivingNotificationMultiple2.body"
|
||||
}
|
||||
|
||||
body = bundle.GetStringFromName(tabArrivingBody);
|
||||
body = PluralForm.get(URIs.length, body);
|
||||
body = body.replace("#1", URIs.length);
|
||||
|
|
|
@ -39,21 +39,32 @@ deviceDisconnectedNotification.body = This computer has been successfully discon
|
|||
# Displayed in the Send Tabs context menu when right clicking a tab, a page or a link.
|
||||
sendTabToAllDevices.menuitem = All Devices
|
||||
|
||||
# LOCALIZATION NOTE (tabArrivingNotification.title, tabArrivingNotification.body,
|
||||
# tabsArrivingNotification.title, tabsArrivingNotification.body)
|
||||
# LOCALIZATION NOTE (tabArrivingNotification.title, tabArrivingNotificationWithDevice.title,
|
||||
# tabsArrivingNotification.title, unnamedTabsArrivingNotification2.body,
|
||||
# unnamedTabsArrivingNotificationMultiple2.body, unnamedTabsArrivingNotificationNoDevice.body)
|
||||
# These strings are used in a notification shown when we're opening tab(s) another device sent us to display.
|
||||
|
||||
# LOCALIZATION NOTE (tabArrivingNotification.title, tabArrivingNotificationWithDevice.title)
|
||||
# The body for these is the URL of the tab recieved
|
||||
tabArrivingNotification.title = Tab received
|
||||
# LOCALIZATION NOTE (tabArrivingNotification.body) %1 is the title of the tab and %2 is the device name.
|
||||
tabArrivingNotification.body = “%1$S” has arrived from %2$S.
|
||||
# LOCALIZATION NOTE (tabArrivingNotificationWithDevice.title) %S is the device name
|
||||
tabArrivingNotificationWithDevice.title = Tab from %S
|
||||
|
||||
tabsArrivingNotification.title = Multiple tabs received
|
||||
# LOCALIZATION NOTE (unnamedTabsArrivingNotification.body):
|
||||
# LOCALIZATION NOTE (unnamedTabsArrivingNotification2.body):
|
||||
# Semi-colon list of plural forms.
|
||||
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
# #1 is the number of tabs received and #2 is the device name.
|
||||
unnamedTabsArrivingNotification.body = #1 tab has arrived from #2.;#1 tabs have arrived from #2.
|
||||
# LOCALIZATION NOTE (unnamedTabsArrivingNotificationMultiple.body):
|
||||
unnamedTabsArrivingNotification2.body = #1 tab has arrived from #2;#1 tabs have arrived from #2
|
||||
# LOCALIZATION NOTE (unnamedTabsArrivingNotificationMultiple2.body):
|
||||
# Semi-colon list of plural forms.
|
||||
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
# #1 is the number of tabs received.
|
||||
unnamedTabsArrivingNotificationMultiple.body = #1 tab has arrived from your connected devices.;#1 tabs have arrived from your connected devices.
|
||||
unnamedTabsArrivingNotificationMultiple2.body = #1 tab has arrived from your connected devices;#1 tabs have arrived from your connected devices
|
||||
|
||||
# LOCALIZATION NOTE (unnamedTabsArrivingNotificationNoDevice.body):
|
||||
# Semi-colon list of plural forms.
|
||||
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
# #1 is the number of tabs received
|
||||
# This version is used when we don't know any device names.
|
||||
unnamedTabsArrivingNotificationNoDevice.body = #1 tab has arrived;#1 tabs have arrived
|
||||
|
|
|
@ -705,6 +705,10 @@ a.learn-more-link.webconsole-learn-more-link {
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.devtools-toolbar.webconsole-filterbar-secondary {
|
||||
height: initial;
|
||||
}
|
||||
|
||||
.webconsole-filterbar-primary .devtools-plaininput {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { Cu } = require("chrome");
|
||||
const { extend } = require("sdk/core/heritage");
|
||||
const { AutoRefreshHighlighter } = require("./auto-refresh");
|
||||
const {
|
||||
|
@ -45,7 +46,11 @@ const GRID_GAP_PATTERN_STROKE_STYLE = "#9370DB";
|
|||
/**
|
||||
* Cached used by `CssGridHighlighter.getGridGapPattern`.
|
||||
*/
|
||||
const gCachedGridPattern = new Map();
|
||||
const gCachedGridPattern = new WeakMap();
|
||||
// WeakMap key for the Row grid pattern.
|
||||
const ROW_KEY = {};
|
||||
// WeakMap key for the Column grid pattern.
|
||||
const COLUMN_KEY = {};
|
||||
|
||||
/**
|
||||
* The CssGridHighlighter is the class that overlays a visual grid on top of
|
||||
|
@ -93,6 +98,9 @@ function CssGridHighlighter(highlighterEnv) {
|
|||
|
||||
this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
|
||||
this._buildMarkup.bind(this));
|
||||
|
||||
this.onNavigate = this.onNavigate.bind(this);
|
||||
this.highlighterEnv.on("navigate", this.onNavigate);
|
||||
}
|
||||
|
||||
CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
||||
|
@ -203,9 +211,9 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
|||
},
|
||||
|
||||
destroy() {
|
||||
AutoRefreshHighlighter.prototype.destroy.call(this);
|
||||
this.highlighterEnv.off("navigate", this.onNavigate);
|
||||
this.markup.destroy();
|
||||
gCachedGridPattern.clear();
|
||||
AutoRefreshHighlighter.prototype.destroy.call(this);
|
||||
},
|
||||
|
||||
getElement(id) {
|
||||
|
@ -223,13 +231,14 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
|||
/**
|
||||
* Gets the grid gap pattern used to render the gap regions.
|
||||
*
|
||||
* @param {String} dimensionType
|
||||
* The grid dimension type which is either the constant COLUMNS or ROWS.
|
||||
* @param {Object} dimension
|
||||
* Refers to the WeakMap key for the grid dimension type which is either the
|
||||
* constant COLUMN or ROW.
|
||||
* @return {CanvasPattern} grid gap pattern.
|
||||
*/
|
||||
getGridGapPattern(dimensionType) {
|
||||
if (gCachedGridPattern.has(dimensionType)) {
|
||||
return gCachedGridPattern.get(dimensionType);
|
||||
getGridGapPattern(dimension) {
|
||||
if (gCachedGridPattern.has(dimension)) {
|
||||
return gCachedGridPattern.get(dimension);
|
||||
}
|
||||
|
||||
// Create the diagonal lines pattern for the rendering the grid gaps.
|
||||
|
@ -242,7 +251,7 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
|||
ctx.beginPath();
|
||||
ctx.translate(.5, .5);
|
||||
|
||||
if (dimensionType === COLUMNS) {
|
||||
if (dimension === COLUMN_KEY) {
|
||||
ctx.moveTo(0, 0);
|
||||
ctx.lineTo(GRID_GAP_PATTERN_WIDTH, GRID_GAP_PATTERN_HEIGHT);
|
||||
} else {
|
||||
|
@ -254,10 +263,19 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
|||
ctx.stroke();
|
||||
|
||||
let pattern = ctx.createPattern(canvas, "repeat");
|
||||
gCachedGridPattern.set(dimensionType, pattern);
|
||||
gCachedGridPattern.set(dimension, pattern);
|
||||
return pattern;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the page navigates. Used to clear the cached gap patterns and avoid
|
||||
* using DeadWrapper objects as gap patterns the next time.
|
||||
*/
|
||||
onNavigate() {
|
||||
gCachedGridPattern.delete(ROW_KEY);
|
||||
gCachedGridPattern.delete(COLUMN_KEY);
|
||||
},
|
||||
|
||||
_show() {
|
||||
if (Services.prefs.getBoolPref(CSS_GRID_ENABLED_PREF) && !this.isGrid()) {
|
||||
this.hide();
|
||||
|
@ -602,11 +620,12 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
|||
*/
|
||||
renderGridGap(linePos, startPos, endPos, breadth, dimensionType) {
|
||||
this.ctx.save();
|
||||
this.ctx.fillStyle = this.getGridGapPattern(dimensionType);
|
||||
|
||||
if (dimensionType === COLUMNS) {
|
||||
this.ctx.fillStyle = this.getGridGapPattern(COLUMN_KEY);
|
||||
this.ctx.fillRect(linePos, startPos, breadth, endPos - startPos);
|
||||
} else {
|
||||
this.ctx.fillStyle = this.getGridGapPattern(ROW_KEY);
|
||||
this.ctx.fillRect(startPos, linePos, endPos - startPos, breadth);
|
||||
}
|
||||
|
||||
|
@ -710,6 +729,10 @@ exports.CssGridHighlighter = CssGridHighlighter;
|
|||
* @return {String} representation of the CSS grid fragment data.
|
||||
*/
|
||||
function stringifyGridFragments(fragments = []) {
|
||||
if (fragments[0] && Cu.isDeadWrapper(fragments[0])) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSON.stringify(fragments.map(getStringifiableFragment));
|
||||
}
|
||||
|
||||
|
|
|
@ -238,11 +238,6 @@ this.PermissionsTable = { geolocation: {
|
|||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
"resourcestats-manage": {
|
||||
app: DENY_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
"wifi-manage": {
|
||||
app: DENY_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 <algorithm>
|
||||
|
||||
#include "mozilla/dom/IdleDeadline.h"
|
||||
#include "mozilla/dom/IdleDeadlineBinding.h"
|
||||
#include "mozilla/dom/Performance.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDOMNavigationTiming.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(IdleDeadline, mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleDeadline)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleDeadline)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleDeadline)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
IdleDeadline::IdleDeadline(nsPIDOMWindowInner* aWindow, bool aDidTimeout,
|
||||
DOMHighResTimeStamp aDeadline)
|
||||
: mWindow(aWindow)
|
||||
, mDidTimeout(aDidTimeout)
|
||||
, mDeadline(aDeadline)
|
||||
{
|
||||
}
|
||||
|
||||
IdleDeadline::~IdleDeadline()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
IdleDeadline::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return IdleDeadlineBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
IdleDeadline::TimeRemaining()
|
||||
{
|
||||
if (mDidTimeout) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
RefPtr<Performance> performance = mWindow->GetPerformance();
|
||||
if (!performance) {
|
||||
// If there is no performance object the window is partially torn
|
||||
// down, so we can safely say that there is no time remaining.
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return std::max(mDeadline - performance->Now(), 0.0);
|
||||
}
|
||||
|
||||
bool
|
||||
IdleDeadline::DidTimeout() const
|
||||
{
|
||||
return mDidTimeout;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,55 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 mozilla_dom_IdleDeadline_h
|
||||
#define mozilla_dom_IdleDeadline_h
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDOMNavigationTiming.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class IdleDeadline final
|
||||
: public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
IdleDeadline(nsPIDOMWindowInner* aWindow, bool aDidTimeout,
|
||||
DOMHighResTimeStamp aDeadline);
|
||||
|
||||
nsPIDOMWindowInner* GetParentObject() const { return mWindow; }
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
DOMHighResTimeStamp TimeRemaining();
|
||||
bool DidTimeout() const;
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IdleDeadline)
|
||||
|
||||
private:
|
||||
~IdleDeadline();
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
const bool mDidTimeout;
|
||||
const DOMHighResTimeStamp mDeadline;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_IdleDeadline_h
|
|
@ -0,0 +1,157 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "IdleRequest.h"
|
||||
|
||||
#include "mozilla/Function.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/IdleDeadline.h"
|
||||
#include "mozilla/dom/Performance.h"
|
||||
#include "mozilla/dom/PerformanceTiming.h"
|
||||
#include "mozilla/dom/WindowBinding.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
IdleRequest::IdleRequest(JSContext* aCx, nsPIDOMWindowInner* aWindow,
|
||||
IdleRequestCallback& aCallback, uint32_t aHandle)
|
||||
: mWindow(aWindow)
|
||||
, mCallback(&aCallback)
|
||||
, mHandle(aHandle)
|
||||
, mTimeoutHandle(Nothing())
|
||||
{
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
// Get the calling location.
|
||||
nsJSUtils::GetCallingLocation(aCx, mFileName, &mLineNo, &mColumn);
|
||||
}
|
||||
|
||||
IdleRequest::~IdleRequest()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequest)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequest)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequest)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequest)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequest)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequest)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIIncrementalRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimeoutHandler)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
nsresult
|
||||
IdleRequest::SetTimeout(uint32_t aTimeout)
|
||||
{
|
||||
int32_t handle;
|
||||
nsresult rv = nsGlobalWindow::Cast(mWindow)->SetTimeoutOrInterval(
|
||||
this, aTimeout, false, Timeout::Reason::eIdleCallbackTimeout, &handle);
|
||||
mTimeoutHandle = Some(handle);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
IdleRequest::Run()
|
||||
{
|
||||
if (mCallback) {
|
||||
RunIdleRequestCallback(false);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
IdleRequest::Cancel()
|
||||
{
|
||||
mCallback = nullptr;
|
||||
CancelTimeout();
|
||||
if (isInList()) {
|
||||
remove();
|
||||
}
|
||||
Release();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
IdleRequest::SetDeadline(TimeStamp aDeadline)
|
||||
{
|
||||
mozilla::dom::Performance* perf = mWindow->GetPerformance();
|
||||
mDeadline =
|
||||
perf ? perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline) : 0.0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
IdleRequest::RunIdleRequestCallback(bool aDidTimeout)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!aDidTimeout) {
|
||||
CancelTimeout();
|
||||
}
|
||||
|
||||
remove();
|
||||
ErrorResult error;
|
||||
RefPtr<IdleDeadline> deadline =
|
||||
new IdleDeadline(mWindow, aDidTimeout, mDeadline);
|
||||
mCallback->Call(*deadline, error, "requestIdleCallback handler");
|
||||
mCallback = nullptr;
|
||||
Release();
|
||||
|
||||
return error.StealNSResult();
|
||||
}
|
||||
|
||||
void
|
||||
IdleRequest::CancelTimeout()
|
||||
{
|
||||
if (mTimeoutHandle.isSome()) {
|
||||
nsGlobalWindow::Cast(mWindow)->ClearTimeoutOrInterval(
|
||||
mTimeoutHandle.value(), Timeout::Reason::eIdleCallbackTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
IdleRequest::Call()
|
||||
{
|
||||
SetDeadline(TimeStamp::Now());
|
||||
return RunIdleRequestCallback(true);
|
||||
}
|
||||
|
||||
void
|
||||
IdleRequest::GetLocation(const char** aFileName, uint32_t* aLineNo,
|
||||
uint32_t* aColumn)
|
||||
{
|
||||
*aFileName = mFileName.get();
|
||||
*aLineNo = mLineNo;
|
||||
*aColumn = mColumn;
|
||||
}
|
||||
|
||||
void
|
||||
IdleRequest::MarkForCC()
|
||||
{
|
||||
mCallback->MarkForCC();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,74 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 mozilla_dom_idlerequest_h
|
||||
#define mozilla_dom_idlerequest_h
|
||||
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDOMNavigationTiming.h"
|
||||
#include "nsITimeoutHandler.h"
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class IdleRequestCallback;
|
||||
|
||||
class IdleRequest final : public nsITimeoutHandler
|
||||
, public nsIRunnable
|
||||
, public nsICancelableRunnable
|
||||
, public nsIIncrementalRunnable
|
||||
, public LinkedListElement<IdleRequest>
|
||||
{
|
||||
public:
|
||||
IdleRequest(JSContext* aCx, nsPIDOMWindowInner* aWindow,
|
||||
IdleRequestCallback& aCallback, uint32_t aHandle);
|
||||
|
||||
virtual nsresult Call() override;
|
||||
virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
|
||||
uint32_t* aColumn) override;
|
||||
virtual void MarkForCC() override;
|
||||
|
||||
nsresult SetTimeout(uint32_t aTimout);
|
||||
nsresult RunIdleRequestCallback(bool aDidTimeout);
|
||||
void CancelTimeout();
|
||||
|
||||
NS_DECL_NSIRUNNABLE;
|
||||
virtual nsresult Cancel() override;
|
||||
virtual void SetDeadline(mozilla::TimeStamp aDeadline) override;
|
||||
|
||||
uint32_t Handle() const
|
||||
{
|
||||
return mHandle;
|
||||
}
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequest, nsITimeoutHandler)
|
||||
|
||||
private:
|
||||
~IdleRequest();
|
||||
|
||||
// filename, line number and JS language version string of the
|
||||
// caller of setTimeout()
|
||||
nsCString mFileName;
|
||||
uint32_t mLineNo;
|
||||
uint32_t mColumn;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
RefPtr<IdleRequestCallback> mCallback;
|
||||
uint32_t mHandle;
|
||||
mozilla::Maybe<int32_t> mTimeoutHandle;
|
||||
DOMHighResTimeStamp mDeadline;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_idlerequest_h
|
|
@ -0,0 +1,91 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "Timeout.h"
|
||||
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsITimeoutHandler.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
Timeout::Timeout()
|
||||
: mCleared(false),
|
||||
mRunning(false),
|
||||
mIsInterval(false),
|
||||
mReason(Reason::eTimeoutOrInterval),
|
||||
mTimeoutId(0),
|
||||
mInterval(0),
|
||||
mFiringDepth(0),
|
||||
mNestingLevel(0),
|
||||
mPopupState(openAllowed)
|
||||
{
|
||||
MOZ_COUNT_CTOR(Timeout);
|
||||
}
|
||||
|
||||
Timeout::~Timeout()
|
||||
{
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
|
||||
MOZ_COUNT_DTOR(Timeout);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(Timeout)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Timeout)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptHandler)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Timeout)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptHandler)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Timeout, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Timeout, Release)
|
||||
|
||||
nsresult
|
||||
Timeout::InitTimer(uint32_t aDelay)
|
||||
{
|
||||
return mTimer->InitWithNameableFuncCallback(
|
||||
nsGlobalWindow::TimerCallback, this, aDelay,
|
||||
nsITimer::TYPE_ONE_SHOT, Timeout::TimerNameCallback);
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
Timeout::TimerNameCallback(nsITimer* aTimer, void* aClosure, char* aBuf,
|
||||
size_t aLen)
|
||||
{
|
||||
RefPtr<Timeout> timeout = (Timeout*)aClosure;
|
||||
|
||||
const char* filename;
|
||||
uint32_t lineNum, column;
|
||||
timeout->mScriptHandler->GetLocation(&filename, &lineNum, &column);
|
||||
snprintf(aBuf, aLen, "[content] %s:%u:%u", filename, lineNum, column);
|
||||
}
|
||||
|
||||
|
||||
// Return true if this timeout has a refcount of 1. This is used to check
|
||||
// that dummy_timeout doesn't leak from nsGlobalWindow::RunTimeout.
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
Timeout::HasRefCntOne() const
|
||||
{
|
||||
return mRefCnt.get() == 1;
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,101 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 mozilla_dom_timeout_h
|
||||
#define mozilla_dom_timeout_h
|
||||
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsGlobalWindow;
|
||||
class nsIPrincipal;
|
||||
class nsITimeoutHandler;
|
||||
class nsITimer;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/*
|
||||
* Timeout struct that holds information about each script
|
||||
* timeout. Holds a strong reference to an nsITimeoutHandler, which
|
||||
* abstracts the language specific cruft.
|
||||
*/
|
||||
class Timeout final
|
||||
: public LinkedListElement<Timeout>
|
||||
{
|
||||
public:
|
||||
Timeout();
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(Timeout)
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Timeout)
|
||||
|
||||
nsresult InitTimer(uint32_t aDelay);
|
||||
|
||||
enum class Reason { eTimeoutOrInterval, eIdleCallbackTimeout };
|
||||
|
||||
static void TimerNameCallback(nsITimer* aTimer, void* aClosure, char* aBuf,
|
||||
size_t aLen);
|
||||
|
||||
#ifdef DEBUG
|
||||
bool HasRefCntOne() const;
|
||||
#endif // DEBUG
|
||||
|
||||
// Window for which this timeout fires
|
||||
RefPtr<nsGlobalWindow> mWindow;
|
||||
|
||||
// The actual timer object
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
|
||||
// True if the timeout was cleared
|
||||
bool mCleared;
|
||||
|
||||
// True if this is one of the timeouts that are currently running
|
||||
bool mRunning;
|
||||
|
||||
// True if this is a repeating/interval timer
|
||||
bool mIsInterval;
|
||||
|
||||
Reason mReason;
|
||||
|
||||
// Returned as value of setTimeout()
|
||||
uint32_t mTimeoutId;
|
||||
|
||||
// Interval in milliseconds
|
||||
uint32_t mInterval;
|
||||
|
||||
// mWhen and mTimeRemaining can't be in a union, sadly, because they
|
||||
// have constructors.
|
||||
// Nominal time to run this timeout. Use only when timeouts are not
|
||||
// suspended.
|
||||
TimeStamp mWhen;
|
||||
// Remaining time to wait. Used only when timeouts are suspended.
|
||||
TimeDuration mTimeRemaining;
|
||||
|
||||
// Principal with which to execute
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
// stack depth at which timeout is firing
|
||||
uint32_t mFiringDepth;
|
||||
|
||||
uint32_t mNestingLevel;
|
||||
|
||||
// The popup state at timeout creation time if not created from
|
||||
// another timeout
|
||||
PopupControlState mPopupState;
|
||||
|
||||
// The language-specific information about the callback.
|
||||
nsCOMPtr<nsITimeoutHandler> mScriptHandler;
|
||||
|
||||
private:
|
||||
~Timeout();
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_timeout_h
|
|
@ -102,6 +102,7 @@ EXPORTS += [
|
|||
'nsIScriptObjectPrincipal.h',
|
||||
'nsIScriptTimeoutHandler.h',
|
||||
'nsIStyleSheetLinkingElement.h',
|
||||
'nsITimeoutHandler.h',
|
||||
'nsJSEnvironment.h',
|
||||
'nsJSUtils.h',
|
||||
'nsLineBreaker.h',
|
||||
|
@ -182,6 +183,8 @@ EXPORTS.mozilla.dom += [
|
|||
'FormData.h',
|
||||
'FragmentOrElement.h',
|
||||
'FromParser.h',
|
||||
'IdleDeadline.h',
|
||||
'IdleRequest.h',
|
||||
'ImageEncoder.h',
|
||||
'ImageTracker.h',
|
||||
'ImportManager.h',
|
||||
|
@ -204,6 +207,7 @@ EXPORTS.mozilla.dom += [
|
|||
'StyleSheetList.h',
|
||||
'SubtleCrypto.h',
|
||||
'Text.h',
|
||||
'Timeout.h',
|
||||
'TreeWalker.h',
|
||||
'WebKitCSSMatrix.h',
|
||||
'WebSocket.h',
|
||||
|
@ -244,6 +248,8 @@ UNIFIED_SOURCES += [
|
|||
'FileReader.cpp',
|
||||
'FormData.cpp',
|
||||
'FragmentOrElement.cpp',
|
||||
'IdleDeadline.cpp',
|
||||
'IdleRequest.cpp',
|
||||
'ImageEncoder.cpp',
|
||||
'ImageTracker.cpp',
|
||||
'ImportManager.cpp',
|
||||
|
@ -342,6 +348,7 @@ UNIFIED_SOURCES += [
|
|||
'Text.cpp',
|
||||
'TextInputProcessor.cpp',
|
||||
'ThirdPartyUtil.cpp',
|
||||
'Timeout.cpp',
|
||||
'TreeWalker.cpp',
|
||||
'WebKitCSSMatrix.cpp',
|
||||
'WebSocket.cpp',
|
||||
|
|
|
@ -1796,43 +1796,6 @@ nsFrameLoader::OwnerIsIsolatedMozBrowserFrame()
|
|||
return true;
|
||||
}
|
||||
|
||||
// After bug 1238160, which allows isolation to be disabled on mozbrowser
|
||||
// frames, we no longer have a way to tell from the principal alone if
|
||||
// something "is a mozbrowser". Instead, we now only know "is an isolated
|
||||
// mozbrowser". The following code paths would return invalid results if it
|
||||
// were possible to have apps *and* isolation could be disabled:
|
||||
// * CheckPermission in AppProcessChecker.cpp
|
||||
// * nsScriptSecurityManager::AppStatusForPrincipal
|
||||
// * init() in SystemMessageManager.js
|
||||
// Currently, desktop is the only platform where we intend to disable
|
||||
// isolation on a browser frame, so non-desktop should be able to assume that
|
||||
// inIsolatedMozBrowser is true for all mozbrowser frames. To enforce these
|
||||
// assumptions, we assert that there are no apps installed if we have tried
|
||||
// to disable isolation.
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
|
||||
if (!appsService) {
|
||||
// If the apps service is not present, we assume this means there can't be
|
||||
// any apps at all, so there is no problem.
|
||||
return false;
|
||||
}
|
||||
bool appsInstalled;
|
||||
nsresult rv = appsService->AreAnyAppsInstalled(&appsInstalled);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
// The apps service exists, but it threw an error when checking if there are
|
||||
// any apps, so we don't know if we have them or not.
|
||||
return false;
|
||||
}
|
||||
#ifdef MOZ_B2G
|
||||
MOZ_RELEASE_ASSERT(!appsInstalled,
|
||||
"Disabling mozbrowser isolation is not currently "
|
||||
"allowed when apps are installed.");
|
||||
#else
|
||||
if (appsInstalled) {
|
||||
NS_WARNING("Disabling mozbrowser isolation is not currently allowed when "
|
||||
"apps are installed.");
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
#include "nsDOMNavigationTiming.h"
|
||||
#include "nsIDOMStorageManager.h"
|
||||
#include "mozilla/dom/DOMStorage.h"
|
||||
#include "mozilla/dom/IdleRequest.h"
|
||||
#include "mozilla/dom/Performance.h"
|
||||
#include "mozilla/dom/StorageEvent.h"
|
||||
#include "mozilla/dom/StorageEventBinding.h"
|
||||
#include "mozilla/dom/Timeout.h"
|
||||
#include "mozilla/IntegerPrintfMacros.h"
|
||||
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
|
||||
#include "mozilla/dom/WindowOrientationObserver.h"
|
||||
|
@ -40,6 +42,7 @@
|
|||
#include "nsIPermissionManager.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsIScriptTimeoutHandler.h"
|
||||
#include "nsITimeoutHandler.h"
|
||||
#include "nsIController.h"
|
||||
#include "nsScriptNameSpaceManager.h"
|
||||
#include "nsISlowScriptDebug.h"
|
||||
|
@ -324,6 +327,11 @@ nsGlobalWindow::DOMMinTimeoutValue() const {
|
|||
// nsTimerImpl.h for details.
|
||||
#define DOM_MAX_TIMEOUT_VALUE DELAY_INTERVAL_LIMIT
|
||||
|
||||
// The interval at which we execute idle callbacks
|
||||
static uint32_t gThrottledIdlePeriodLength;
|
||||
|
||||
#define DEFAULT_THROTTLED_IDLE_PERIOD_LENGTH 10000
|
||||
|
||||
#define FORWARD_TO_OUTER(method, args, err_rval) \
|
||||
PR_BEGIN_MACRO \
|
||||
if (IsInnerWindow()) { \
|
||||
|
@ -502,71 +510,6 @@ private:
|
|||
|
||||
NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor)
|
||||
|
||||
nsTimeout::nsTimeout()
|
||||
: mCleared(false),
|
||||
mRunning(false),
|
||||
mIsInterval(false),
|
||||
mPublicId(0),
|
||||
mInterval(0),
|
||||
mFiringDepth(0),
|
||||
mNestingLevel(0),
|
||||
mPopupState(openAllowed)
|
||||
{
|
||||
#ifdef DEBUG_jst
|
||||
{
|
||||
extern int gTimeoutCnt;
|
||||
|
||||
++gTimeoutCnt;
|
||||
}
|
||||
#endif
|
||||
|
||||
MOZ_COUNT_CTOR(nsTimeout);
|
||||
}
|
||||
|
||||
nsTimeout::~nsTimeout()
|
||||
{
|
||||
#ifdef DEBUG_jst
|
||||
{
|
||||
extern int gTimeoutCnt;
|
||||
|
||||
--gTimeoutCnt;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
|
||||
MOZ_COUNT_DTOR(nsTimeout);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsTimeout)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsTimeout)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTimeout)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptHandler)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTimeout, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTimeout, Release)
|
||||
|
||||
nsresult
|
||||
nsTimeout::InitTimer(uint32_t aDelay)
|
||||
{
|
||||
return mTimer->InitWithNameableFuncCallback(
|
||||
nsGlobalWindow::TimerCallback, this, aDelay,
|
||||
nsITimer::TYPE_ONE_SHOT, nsGlobalWindow::TimerNameCallback);
|
||||
}
|
||||
|
||||
// Return true if this timeout has a refcount of 1. This is used to check
|
||||
// that dummy_timeout doesn't leak from nsGlobalWindow::RunTimeout.
|
||||
bool
|
||||
nsTimeout::HasRefCntOne()
|
||||
{
|
||||
return mRefCnt.get() == 1;
|
||||
}
|
||||
|
||||
static already_AddRefed<nsIVariant>
|
||||
CreateVoidVariant()
|
||||
{
|
||||
|
@ -602,6 +545,109 @@ DialogValueHolder::Get(JSContext* aCx, JS::Handle<JSObject*> aScope,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::PostThrottledIdleCallback()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (mThrottledIdleRequestCallbacks.isEmpty())
|
||||
return;
|
||||
|
||||
RefPtr<IdleRequest> request(mThrottledIdleRequestCallbacks.popFirst());
|
||||
// ownership transferred from mThrottledIdleRequestCallbacks to
|
||||
// mIdleRequestCallbacks
|
||||
mIdleRequestCallbacks.insertBack(request);
|
||||
NS_IdleDispatchToCurrentThread(request.forget());
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsGlobalWindow::InsertIdleCallbackIntoList(IdleRequest* aRequest,
|
||||
IdleRequests& aList)
|
||||
{
|
||||
aList.insertBack(aRequest);
|
||||
aRequest->AddRef();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsGlobalWindow::RequestIdleCallback(JSContext* aCx,
|
||||
IdleRequestCallback& aCallback,
|
||||
const IdleRequestOptions& aOptions,
|
||||
ErrorResult& aError)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(IsInnerWindow());
|
||||
AssertIsOnMainThread();
|
||||
|
||||
uint32_t handle = ++mIdleRequestCallbackCounter;
|
||||
|
||||
RefPtr<IdleRequest> request =
|
||||
new IdleRequest(aCx, AsInner(), aCallback, handle);
|
||||
|
||||
if (aOptions.mTimeout.WasPassed()) {
|
||||
aError = request->SetTimeout(aOptions.mTimeout.Value());
|
||||
if (NS_WARN_IF(aError.Failed())) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
nsGlobalWindow* outer = GetOuterWindowInternal();
|
||||
if (outer && outer->AsOuter()->IsBackground()) {
|
||||
// mThrottledIdleRequestCallbacks now owns request
|
||||
InsertIdleCallbackIntoList(request, mThrottledIdleRequestCallbacks);
|
||||
|
||||
NS_DelayedDispatchToCurrentThread(
|
||||
NewRunnableMethod(this, &nsGlobalWindow::PostThrottledIdleCallback),
|
||||
10000);
|
||||
} else {
|
||||
MOZ_ASSERT(mThrottledIdleRequestCallbacks.isEmpty());
|
||||
|
||||
// mIdleRequestCallbacks now owns request
|
||||
InsertIdleCallbackIntoList(request, mIdleRequestCallbacks);
|
||||
|
||||
NS_IdleDispatchToCurrentThread(request.forget());
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::CancelIdleCallback(uint32_t aHandle)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(IsInnerWindow());
|
||||
|
||||
for (IdleRequest* r : mIdleRequestCallbacks) {
|
||||
if (r->Handle() == aHandle) {
|
||||
r->Cancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::DisableIdleCallbackRequests()
|
||||
{
|
||||
while (!mIdleRequestCallbacks.isEmpty()) {
|
||||
RefPtr<IdleRequest> request = mIdleRequestCallbacks.popFirst();
|
||||
request->Cancel();
|
||||
}
|
||||
|
||||
while (!mThrottledIdleRequestCallbacks.isEmpty()) {
|
||||
RefPtr<IdleRequest> request = mThrottledIdleRequestCallbacks.popFirst();
|
||||
request->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void nsGlobalWindow::UnthrottleIdleCallbackRequests()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
while (!mThrottledIdleRequestCallbacks.isEmpty()) {
|
||||
RefPtr<IdleRequest> request(mThrottledIdleRequestCallbacks.popFirst());
|
||||
mIdleRequestCallbacks.insertBack(request);
|
||||
NS_IdleDispatchToCurrentThread(request.forget());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
extern uint64_t
|
||||
|
@ -1211,11 +1257,13 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
|
|||
mNotifiedIDDestroyed(false),
|
||||
mAllowScriptsToClose(false),
|
||||
mTimeoutInsertionPoint(nullptr),
|
||||
mTimeoutPublicIdCounter(1),
|
||||
mTimeoutIdCounter(1),
|
||||
mTimeoutFiringDepth(0),
|
||||
mTimeoutsSuspendDepth(0),
|
||||
mFocusMethod(0),
|
||||
mSerial(0),
|
||||
mIdleCallbackTimeoutCounter(1),
|
||||
mIdleRequestCallbackCounter(1),
|
||||
#ifdef DEBUG
|
||||
mSetOpenerWindowCalled(false),
|
||||
#endif
|
||||
|
@ -1286,6 +1334,10 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
|
|||
Preferences::AddBoolVarCache(&sIdleObserversAPIFuzzTimeDisabled,
|
||||
"dom.idle-observers-api.fuzz_time.disabled",
|
||||
false);
|
||||
|
||||
Preferences::AddUintVarCache(&gThrottledIdlePeriodLength,
|
||||
"dom.idle_period.throttled_length",
|
||||
DEFAULT_THROTTLED_IDLE_PERIOD_LENGTH);
|
||||
}
|
||||
|
||||
if (gDumpFile == nullptr) {
|
||||
|
@ -1627,6 +1679,7 @@ nsGlobalWindow::CleanUp()
|
|||
#ifdef MOZ_B2G
|
||||
DisableTimeChangeNotifications();
|
||||
#endif
|
||||
DisableIdleCallbackRequests();
|
||||
} else {
|
||||
MOZ_ASSERT(!mHasGamepad);
|
||||
MOZ_ASSERT(!mHasVREvents);
|
||||
|
@ -1706,6 +1759,8 @@ nsGlobalWindow::FreeInnerObjects()
|
|||
|
||||
mIdleObservers.Clear();
|
||||
|
||||
DisableIdleCallbackRequests();
|
||||
|
||||
mChromeEventHandler = nullptr;
|
||||
|
||||
if (mListenerManager) {
|
||||
|
@ -1895,10 +1950,10 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
|
|||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
|
||||
|
||||
for (nsTimeout* timeout = tmp->mTimeouts.getFirst();
|
||||
for (Timeout* timeout = tmp->mTimeouts.getFirst();
|
||||
timeout;
|
||||
timeout = timeout->getNext()) {
|
||||
cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(nsTimeout));
|
||||
cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout));
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
|
||||
|
@ -1915,6 +1970,15 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStorageEvents)
|
||||
|
||||
for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
|
||||
cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
|
||||
}
|
||||
|
||||
for (IdleRequest* request : tmp->mThrottledIdleRequestCallbacks) {
|
||||
cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)
|
||||
|
||||
#ifdef MOZ_GAMEPAD
|
||||
|
@ -1975,6 +2039,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
|
|||
tmp->mListenerManager->Disconnect();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomElements)
|
||||
|
@ -2020,6 +2085,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
|
|||
|
||||
tmp->UnlinkHostObjectURIs();
|
||||
|
||||
tmp->DisableIdleCallbackRequests();
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
|
@ -2058,14 +2125,11 @@ nsGlobalWindow::IsBlackForCC(bool aTracingNeeded)
|
|||
void
|
||||
nsGlobalWindow::UnmarkGrayTimers()
|
||||
{
|
||||
for (nsTimeout* timeout = mTimeouts.getFirst();
|
||||
for (Timeout* timeout = mTimeouts.getFirst();
|
||||
timeout;
|
||||
timeout = timeout->getNext()) {
|
||||
if (timeout->mScriptHandler) {
|
||||
Function* f = timeout->mScriptHandler->GetCallback();
|
||||
if (f) {
|
||||
f->MarkForCC();
|
||||
}
|
||||
timeout->mScriptHandler->MarkForCC();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7929,7 +7993,7 @@ nsGlobalWindow::ClearTimeout(int32_t aHandle)
|
|||
MOZ_RELEASE_ASSERT(IsInnerWindow());
|
||||
|
||||
if (aHandle > 0) {
|
||||
ClearTimeoutOrInterval(aHandle);
|
||||
ClearTimeoutOrInterval(aHandle, Timeout::Reason::eTimeoutOrInterval);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7937,7 +8001,7 @@ void
|
|||
nsGlobalWindow::ClearInterval(int32_t aHandle)
|
||||
{
|
||||
if (aHandle > 0) {
|
||||
ClearTimeoutOrInterval(aHandle);
|
||||
ClearTimeoutOrInterval(aHandle, Timeout::Reason::eTimeoutOrInterval);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9944,6 +10008,13 @@ void nsGlobalWindow::SetIsBackground(bool aIsBackground)
|
|||
if (resetTimers) {
|
||||
ResetTimersForNonBackgroundWindow();
|
||||
}
|
||||
|
||||
if (!aIsBackground) {
|
||||
nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
|
||||
if (inner) {
|
||||
inner->UnthrottleIdleCallbackRequests();
|
||||
}
|
||||
}
|
||||
#ifdef MOZ_GAMEPAD
|
||||
if (!aIsBackground) {
|
||||
nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
|
||||
|
@ -12016,6 +12087,18 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
|
|||
|
||||
uint32_t sNestingLevel;
|
||||
|
||||
uint32_t
|
||||
nsGlobalWindow::GetTimeoutId(Timeout::Reason aReason)
|
||||
{
|
||||
switch (aReason) {
|
||||
case Timeout::Reason::eIdleCallbackTimeout:
|
||||
return ++mIdleCallbackTimeoutCounter;
|
||||
case Timeout::Reason::eTimeoutOrInterval:
|
||||
default:
|
||||
return ++mTimeoutIdCounter;
|
||||
}
|
||||
}
|
||||
|
||||
nsGlobalWindow*
|
||||
nsGlobalWindow::InnerForSetTimeoutOrInterval(ErrorResult& aError)
|
||||
{
|
||||
|
@ -12123,9 +12206,9 @@ nsGlobalWindow::SetInterval(JSContext* aCx, const nsAString& aHandler,
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsGlobalWindow::SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
|
||||
int32_t interval,
|
||||
bool aIsInterval, int32_t *aReturn)
|
||||
nsGlobalWindow::SetTimeoutOrInterval(nsITimeoutHandler* aHandler,
|
||||
int32_t interval, bool aIsInterval,
|
||||
Timeout::Reason aReason, int32_t* aReturn)
|
||||
{
|
||||
MOZ_ASSERT(IsInnerWindow());
|
||||
|
||||
|
@ -12147,10 +12230,11 @@ nsGlobalWindow::SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
|
|||
interval = maxTimeoutMs;
|
||||
}
|
||||
|
||||
RefPtr<nsTimeout> timeout = new nsTimeout();
|
||||
RefPtr<Timeout> timeout = new Timeout();
|
||||
timeout->mIsInterval = aIsInterval;
|
||||
timeout->mInterval = interval;
|
||||
timeout->mScriptHandler = aHandler;
|
||||
timeout->mReason = aReason;
|
||||
|
||||
// Now clamp the actual interval we will use for the timer based on
|
||||
uint32_t nestingLevel = sNestingLevel + 1;
|
||||
|
@ -12176,7 +12260,7 @@ nsGlobalWindow::SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
|
|||
return rv;
|
||||
}
|
||||
|
||||
RefPtr<nsTimeout> copy = timeout;
|
||||
RefPtr<Timeout> copy = timeout;
|
||||
|
||||
rv = timeout->InitTimer(realInterval);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -12223,11 +12307,10 @@ nsGlobalWindow::SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
|
|||
|
||||
InsertTimeoutIntoList(timeout);
|
||||
|
||||
timeout->mPublicId = ++mTimeoutPublicIdCounter;
|
||||
*aReturn = timeout->mPublicId;
|
||||
timeout->mTimeoutId = GetTimeoutId(aReason);
|
||||
*aReturn = timeout->mTimeoutId;
|
||||
|
||||
return NS_OK;
|
||||
|
||||
}
|
||||
|
||||
int32_t
|
||||
|
@ -12253,7 +12336,8 @@ nsGlobalWindow::SetTimeoutOrInterval(JSContext *aCx, Function& aFunction,
|
|||
}
|
||||
|
||||
int32_t result;
|
||||
aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval, &result);
|
||||
aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval,
|
||||
Timeout::Reason::eTimeoutOrInterval, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -12279,18 +12363,19 @@ nsGlobalWindow::SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler,
|
|||
}
|
||||
|
||||
int32_t result;
|
||||
aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval, &result);
|
||||
aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval,
|
||||
Timeout::Reason::eTimeoutOrInterval, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
nsGlobalWindow::RunTimeoutHandler(nsTimeout* aTimeout,
|
||||
nsGlobalWindow::RunTimeoutHandler(Timeout* aTimeout,
|
||||
nsIScriptContext* aScx)
|
||||
{
|
||||
// Hold on to the timeout in case mExpr or mFunObj releases its
|
||||
// doc.
|
||||
RefPtr<nsTimeout> timeout = aTimeout;
|
||||
nsTimeout* last_running_timeout = mRunningTimeout;
|
||||
RefPtr<Timeout> timeout = aTimeout;
|
||||
Timeout* last_running_timeout = mRunningTimeout;
|
||||
mRunningTimeout = timeout;
|
||||
timeout->mRunning = true;
|
||||
|
||||
|
@ -12322,39 +12407,47 @@ nsGlobalWindow::RunTimeoutHandler(nsTimeout* aTimeout,
|
|||
}
|
||||
|
||||
bool abortIntervalHandler = false;
|
||||
nsCOMPtr<nsIScriptTimeoutHandler> handler(timeout->mScriptHandler);
|
||||
RefPtr<Function> callback = handler->GetCallback();
|
||||
if (!callback) {
|
||||
// Evaluate the timeout expression.
|
||||
const nsAString& script = handler->GetHandlerText();
|
||||
nsCOMPtr<nsIScriptTimeoutHandler> handler(do_QueryInterface(timeout->mScriptHandler));
|
||||
if (handler) {
|
||||
RefPtr<Function> callback = handler->GetCallback();
|
||||
|
||||
const char* filename = nullptr;
|
||||
uint32_t lineNo = 0, dummyColumn = 0;
|
||||
handler->GetLocation(&filename, &lineNo, &dummyColumn);
|
||||
if (!callback) {
|
||||
// Evaluate the timeout expression.
|
||||
const nsAString& script = handler->GetHandlerText();
|
||||
|
||||
// New script entry point required, due to the "Create a script" sub-step of
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
|
||||
nsAutoMicroTask mt;
|
||||
AutoEntryScript aes(this, reason, true);
|
||||
JS::CompileOptions options(aes.cx());
|
||||
options.setFileAndLine(filename, lineNo)
|
||||
.setVersion(JSVERSION_DEFAULT);
|
||||
JS::Rooted<JSObject*> global(aes.cx(), FastGetGlobalJSObject());
|
||||
nsresult rv = nsJSUtils::EvaluateString(aes.cx(), script, global, options);
|
||||
if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
|
||||
abortIntervalHandler = true;
|
||||
const char* filename = nullptr;
|
||||
uint32_t lineNo = 0, dummyColumn = 0;
|
||||
handler->GetLocation(&filename, &lineNo, &dummyColumn);
|
||||
|
||||
// New script entry point required, due to the "Create a script" sub-step of
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
|
||||
nsAutoMicroTask mt;
|
||||
AutoEntryScript aes(this, reason, true);
|
||||
JS::CompileOptions options(aes.cx());
|
||||
options.setFileAndLine(filename, lineNo).setVersion(JSVERSION_DEFAULT);
|
||||
JS::Rooted<JSObject*> global(aes.cx(), FastGetGlobalJSObject());
|
||||
nsresult rv =
|
||||
nsJSUtils::EvaluateString(aes.cx(), script, global, options);
|
||||
if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
|
||||
abortIntervalHandler = true;
|
||||
}
|
||||
} else {
|
||||
// Hold strong ref to ourselves while we call the callback.
|
||||
nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow*>(this));
|
||||
ErrorResult rv;
|
||||
JS::Rooted<JS::Value> ignoredVal(RootingCx());
|
||||
callback->Call(me, handler->GetArgs(), &ignoredVal, rv, reason);
|
||||
if (rv.IsUncatchableException()) {
|
||||
abortIntervalHandler = true;
|
||||
}
|
||||
|
||||
rv.SuppressException();
|
||||
}
|
||||
} else {
|
||||
// Hold strong ref to ourselves while we call the callback.
|
||||
nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow *>(this));
|
||||
ErrorResult rv;
|
||||
JS::Rooted<JS::Value> ignoredVal(RootingCx());
|
||||
callback->Call(me, handler->GetArgs(), &ignoredVal, rv, reason);
|
||||
if (rv.IsUncatchableException()) {
|
||||
abortIntervalHandler = true;
|
||||
}
|
||||
|
||||
rv.SuppressException();
|
||||
nsCOMPtr<nsITimeoutHandler> basicHandler(timeout->mScriptHandler);
|
||||
nsCOMPtr<nsISupports> kungFuDeathGrip(static_cast<nsIDOMWindow*>(this));
|
||||
mozilla::Unused << kungFuDeathGrip;
|
||||
basicHandler->Call();
|
||||
}
|
||||
|
||||
// If we received an uncatchable exception, do not schedule the timeout again.
|
||||
|
@ -12396,7 +12489,7 @@ nsGlobalWindow::RunTimeoutHandler(nsTimeout* aTimeout,
|
|||
}
|
||||
|
||||
bool
|
||||
nsGlobalWindow::RescheduleTimeout(nsTimeout* aTimeout, const TimeStamp& now,
|
||||
nsGlobalWindow::RescheduleTimeout(Timeout* aTimeout, const TimeStamp& now,
|
||||
bool aRunningPendingTimeouts)
|
||||
{
|
||||
if (!aTimeout->mIsInterval) {
|
||||
|
@ -12475,7 +12568,7 @@ nsGlobalWindow::RescheduleTimeout(nsTimeout* aTimeout, const TimeStamp& now,
|
|||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
|
||||
nsGlobalWindow::RunTimeout(Timeout* aTimeout)
|
||||
{
|
||||
// If a modal dialog is open for this window, return early. Pending
|
||||
// timeouts will run when the modal dialog is dismissed.
|
||||
|
@ -12486,8 +12579,9 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
|
|||
NS_ASSERTION(IsInnerWindow(), "Timeout running on outer window!");
|
||||
NS_ASSERTION(!IsFrozen(), "Timeout running on a window in the bfcache!");
|
||||
|
||||
nsTimeout *nextTimeout;
|
||||
nsTimeout *last_expired_timeout, *last_insertion_point;
|
||||
Timeout* nextTimeout;
|
||||
Timeout* last_expired_timeout;
|
||||
Timeout* last_insertion_point;
|
||||
uint32_t firingDepth = mTimeoutFiringDepth + 1;
|
||||
|
||||
// Make sure that the window and the script context don't go away as
|
||||
|
@ -12518,7 +12612,7 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
|
|||
// whose mWhen is greater than deadline, since once that happens we know
|
||||
// nothing past that point is expired.
|
||||
last_expired_timeout = nullptr;
|
||||
for (nsTimeout *timeout = mTimeouts.getFirst();
|
||||
for (Timeout* timeout = mTimeouts.getFirst();
|
||||
timeout && timeout->mWhen <= deadline;
|
||||
timeout = timeout->getNext()) {
|
||||
if (timeout->mFiringDepth == 0) {
|
||||
|
@ -12548,18 +12642,18 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
|
|||
// timeouts that will be processed in a future call to
|
||||
// win_run_timeout(). This dummy timeout serves as the head of the
|
||||
// list for any timeouts inserted as a result of running a timeout.
|
||||
RefPtr<nsTimeout> dummy_timeout = new nsTimeout();
|
||||
RefPtr<Timeout> dummy_timeout = new Timeout();
|
||||
dummy_timeout->mFiringDepth = firingDepth;
|
||||
dummy_timeout->mWhen = now;
|
||||
last_expired_timeout->setNext(dummy_timeout);
|
||||
RefPtr<nsTimeout> timeoutExtraRef(dummy_timeout);
|
||||
RefPtr<Timeout> timeoutExtraRef(dummy_timeout);
|
||||
|
||||
last_insertion_point = mTimeoutInsertionPoint;
|
||||
// If we ever start setting mTimeoutInsertionPoint to a non-dummy timeout,
|
||||
// the logic in ResetTimersForNonBackgroundWindow will need to change.
|
||||
mTimeoutInsertionPoint = dummy_timeout;
|
||||
|
||||
for (nsTimeout *timeout = mTimeouts.getFirst();
|
||||
for (Timeout* timeout = mTimeouts.getFirst();
|
||||
timeout != dummy_timeout && !IsFrozen();
|
||||
timeout = nextTimeout) {
|
||||
nextTimeout = timeout->getNext();
|
||||
|
@ -12636,15 +12730,15 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
|
|||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::ClearTimeoutOrInterval(int32_t aTimerID)
|
||||
nsGlobalWindow::ClearTimeoutOrInterval(int32_t aTimerId, Timeout::Reason aReason)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(IsInnerWindow());
|
||||
|
||||
uint32_t public_id = (uint32_t)aTimerID;
|
||||
nsTimeout *timeout;
|
||||
uint32_t timerId = (uint32_t)aTimerId;
|
||||
Timeout* timeout;
|
||||
|
||||
for (timeout = mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) {
|
||||
if (timeout->mPublicId == public_id) {
|
||||
if (timeout->mTimeoutId == timerId && timeout->mReason == aReason) {
|
||||
if (timeout->mRunning) {
|
||||
/* We're running from inside the timeout. Mark this
|
||||
timeout for deferred deletion by the code in
|
||||
|
@ -12685,7 +12779,7 @@ nsresult nsGlobalWindow::ResetTimersForNonBackgroundWindow()
|
|||
// anything with mTimeoutInsertionPoint or anything before it, so should
|
||||
// start at the timer after mTimeoutInsertionPoint, if there is one.
|
||||
// Otherwise, start at the beginning of the list.
|
||||
for (nsTimeout *timeout = mTimeoutInsertionPoint ?
|
||||
for (Timeout* timeout = mTimeoutInsertionPoint ?
|
||||
mTimeoutInsertionPoint->getNext() : mTimeouts.getFirst();
|
||||
timeout; ) {
|
||||
// It's important that this check be <= so that we guarantee that
|
||||
|
@ -12729,7 +12823,7 @@ nsresult nsGlobalWindow::ResetTimersForNonBackgroundWindow()
|
|||
|
||||
// Get the pointer to the next timeout now, before we move the
|
||||
// current timeout in the list.
|
||||
nsTimeout* nextTimeout = timeout->getNext();
|
||||
Timeout* nextTimeout = timeout->getNext();
|
||||
|
||||
// It is safe to remove and re-insert because mWhen is now
|
||||
// strictly smaller than it used to be, so we know we'll insert
|
||||
|
@ -12763,7 +12857,8 @@ nsresult nsGlobalWindow::ResetTimersForNonBackgroundWindow()
|
|||
void
|
||||
nsGlobalWindow::ClearAllTimeouts()
|
||||
{
|
||||
nsTimeout *timeout, *nextTimeout;
|
||||
Timeout* timeout;
|
||||
Timeout* nextTimeout;
|
||||
|
||||
for (timeout = mTimeouts.getFirst(); timeout; timeout = nextTimeout) {
|
||||
/* If RunTimeout() is higher up on the stack for this
|
||||
|
@ -12798,7 +12893,7 @@ nsGlobalWindow::ClearAllTimeouts()
|
|||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::InsertTimeoutIntoList(nsTimeout *aTimeout)
|
||||
nsGlobalWindow::InsertTimeoutIntoList(Timeout* aTimeout)
|
||||
{
|
||||
NS_ASSERTION(IsInnerWindow(),
|
||||
"InsertTimeoutIntoList() called on outer window!");
|
||||
|
@ -12806,7 +12901,7 @@ nsGlobalWindow::InsertTimeoutIntoList(nsTimeout *aTimeout)
|
|||
// Start at mLastTimeout and go backwards. Don't go further than
|
||||
// mTimeoutInsertionPoint, though. This optimizes for the common case of
|
||||
// insertion at the end.
|
||||
nsTimeout* prevSibling;
|
||||
Timeout* prevSibling;
|
||||
for (prevSibling = mTimeouts.getLast();
|
||||
prevSibling && prevSibling != mTimeoutInsertionPoint &&
|
||||
// This condition needs to match the one in SetTimeoutOrInterval that
|
||||
|
@ -12836,24 +12931,11 @@ nsGlobalWindow::InsertTimeoutIntoList(nsTimeout *aTimeout)
|
|||
void
|
||||
nsGlobalWindow::TimerCallback(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
RefPtr<nsTimeout> timeout = (nsTimeout *)aClosure;
|
||||
RefPtr<Timeout> timeout = (Timeout*)aClosure;
|
||||
|
||||
timeout->mWindow->RunTimeout(timeout);
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsGlobalWindow::TimerNameCallback(nsITimer* aTimer, void* aClosure, char* aBuf,
|
||||
size_t aLen)
|
||||
{
|
||||
RefPtr<nsTimeout> timeout = (nsTimeout*)aClosure;
|
||||
|
||||
const char* filename;
|
||||
uint32_t lineNum, column;
|
||||
timeout->mScriptHandler->GetLocation(&filename, &lineNum, &column);
|
||||
snprintf(aBuf, aLen, "[content] %s:%u:%u", filename, lineNum, column);
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// nsGlobalWindow: Helper Functions
|
||||
//*****************************************************************************
|
||||
|
@ -13080,7 +13162,7 @@ nsGlobalWindow::SuspendTimeouts(uint32_t aIncrease,
|
|||
}
|
||||
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
for (nsTimeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
|
||||
for (Timeout* t = mTimeouts.getFirst(); t; t = t->getNext()) {
|
||||
// Set mTimeRemaining to be the time remaining for this timer.
|
||||
if (t->mWhen > now)
|
||||
t->mTimeRemaining = t->mWhen - now;
|
||||
|
@ -13174,7 +13256,7 @@ nsGlobalWindow::ResumeTimeouts(bool aThawChildren, bool aThawWorkers)
|
|||
bool _seenDummyTimeout = false;
|
||||
#endif
|
||||
|
||||
for (nsTimeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
|
||||
for (Timeout* t = mTimeouts.getFirst(); t; t = t->getNext()) {
|
||||
// There's a chance we're being called with RunTimeout on the stack in which
|
||||
// case we have a dummy timeout in the list that *must not* be resumed. It
|
||||
// can be identified by a null mWindow.
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
#include "nsSize.h"
|
||||
#include "nsCheapSets.h"
|
||||
#include "mozilla/dom/ImageBitmapSource.h"
|
||||
#include "mozilla/dom/Timeout.h"
|
||||
|
||||
#define DEFAULT_HOME_PAGE "www.mozilla.org"
|
||||
#define PREF_BROWSER_STARTUP_HOMEPAGE "browser.startup.homepage"
|
||||
|
@ -85,6 +86,7 @@ class nsIControllers;
|
|||
class nsIJSID;
|
||||
class nsIScriptContext;
|
||||
class nsIScriptTimeoutHandler;
|
||||
class nsITimeoutHandler;
|
||||
class nsIWebBrowserChrome;
|
||||
|
||||
class nsDOMWindowList;
|
||||
|
@ -110,6 +112,8 @@ class External;
|
|||
class Function;
|
||||
class Gamepad;
|
||||
enum class ImageBitmapFormat : uint32_t;
|
||||
class IdleRequest;
|
||||
class IdleRequestCallback;
|
||||
class Location;
|
||||
class MediaQueryList;
|
||||
class MozSelfSupport;
|
||||
|
@ -121,6 +125,7 @@ struct RequestInit;
|
|||
class RequestOrUSVString;
|
||||
class Selection;
|
||||
class SpeechSynthesis;
|
||||
class Timeout;
|
||||
class U2F;
|
||||
class VRDisplay;
|
||||
class VREventObserver;
|
||||
|
@ -148,69 +153,6 @@ NS_CreateJSTimeoutHandler(JSContext* aCx, nsGlobalWindow *aWindow,
|
|||
|
||||
extern const js::Class OuterWindowProxyClass;
|
||||
|
||||
/*
|
||||
* Timeout struct that holds information about each script
|
||||
* timeout. Holds a strong reference to an nsIScriptTimeoutHandler, which
|
||||
* abstracts the language specific cruft.
|
||||
*/
|
||||
struct nsTimeout final
|
||||
: mozilla::LinkedListElement<nsTimeout>
|
||||
{
|
||||
private:
|
||||
~nsTimeout();
|
||||
|
||||
public:
|
||||
nsTimeout();
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsTimeout)
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsTimeout)
|
||||
|
||||
nsresult InitTimer(uint32_t aDelay);
|
||||
|
||||
bool HasRefCntOne();
|
||||
|
||||
// Window for which this timeout fires
|
||||
RefPtr<nsGlobalWindow> mWindow;
|
||||
|
||||
// The actual timer object
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
|
||||
// True if the timeout was cleared
|
||||
bool mCleared;
|
||||
|
||||
// True if this is one of the timeouts that are currently running
|
||||
bool mRunning;
|
||||
|
||||
// True if this is a repeating/interval timer
|
||||
bool mIsInterval;
|
||||
|
||||
// Returned as value of setTimeout()
|
||||
uint32_t mPublicId;
|
||||
|
||||
// Interval in milliseconds
|
||||
uint32_t mInterval;
|
||||
|
||||
// mWhen and mTimeRemaining can't be in a union, sadly, because they
|
||||
// have constructors.
|
||||
// Nominal time to run this timeout. Use only when timeouts are not
|
||||
// suspended.
|
||||
mozilla::TimeStamp mWhen;
|
||||
// Remaining time to wait. Used only when timeouts are suspended.
|
||||
mozilla::TimeDuration mTimeRemaining;
|
||||
|
||||
// stack depth at which timeout is firing
|
||||
uint32_t mFiringDepth;
|
||||
|
||||
uint32_t mNestingLevel;
|
||||
|
||||
// The popup state at timeout creation time if not created from
|
||||
// another timeout
|
||||
PopupControlState mPopupState;
|
||||
|
||||
// The language-specific information about the callback.
|
||||
nsCOMPtr<nsIScriptTimeoutHandler> mScriptHandler;
|
||||
};
|
||||
|
||||
struct IdleObserverHolder
|
||||
{
|
||||
nsCOMPtr<nsIIdleObserver> mIdleObserver;
|
||||
|
@ -812,6 +754,7 @@ public:
|
|||
|
||||
// Update the VR displays for this window
|
||||
bool UpdateVRDisplays(nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDisplays);
|
||||
|
||||
// Inner windows only.
|
||||
// Called to inform that the set of active VR displays has changed.
|
||||
void NotifyActiveVRDisplaysChanged();
|
||||
|
@ -1125,6 +1068,14 @@ public:
|
|||
int32_t RequestAnimationFrame(mozilla::dom::FrameRequestCallback& aCallback,
|
||||
mozilla::ErrorResult& aError);
|
||||
void CancelAnimationFrame(int32_t aHandle, mozilla::ErrorResult& aError);
|
||||
|
||||
uint32_t RequestIdleCallback(JSContext* aCx,
|
||||
mozilla::dom::IdleRequestCallback& aCallback,
|
||||
const mozilla::dom::IdleRequestOptions& aOptions,
|
||||
mozilla::ErrorResult& aError);
|
||||
void CancelIdleCallback(uint32_t aHandle);
|
||||
|
||||
|
||||
#ifdef MOZ_WEBSPEECH
|
||||
mozilla::dom::SpeechSynthesis*
|
||||
GetSpeechSynthesis(mozilla::ErrorResult& aError);
|
||||
|
@ -1296,7 +1247,6 @@ public:
|
|||
already_AddRefed<nsWindowRoot> GetWindowRoot(mozilla::ErrorResult& aError);
|
||||
|
||||
mozilla::dom::Performance* GetPerformance();
|
||||
|
||||
protected:
|
||||
// Web IDL helpers
|
||||
|
||||
|
@ -1501,9 +1451,10 @@ public:
|
|||
// Timeout Functions
|
||||
// Language agnostic timeout function (all args passed).
|
||||
// |interval| is in milliseconds.
|
||||
nsresult SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
|
||||
int32_t interval,
|
||||
bool aIsInterval, int32_t* aReturn);
|
||||
nsresult SetTimeoutOrInterval(nsITimeoutHandler* aHandler,
|
||||
int32_t interval, bool aIsInterval,
|
||||
mozilla::dom::Timeout::Reason aReason,
|
||||
int32_t* aReturn);
|
||||
int32_t SetTimeoutOrInterval(JSContext* aCx,
|
||||
mozilla::dom::Function& aFunction,
|
||||
int32_t aTimeout,
|
||||
|
@ -1512,27 +1463,27 @@ public:
|
|||
int32_t SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler,
|
||||
int32_t aTimeout, bool aIsInterval,
|
||||
mozilla::ErrorResult& aError);
|
||||
void ClearTimeoutOrInterval(int32_t aTimerID);
|
||||
void ClearTimeoutOrInterval(int32_t aTimerId,
|
||||
mozilla::dom::Timeout::Reason aReason);
|
||||
|
||||
// JS specific timeout functions (JS args grabbed from context).
|
||||
nsresult ResetTimersForNonBackgroundWindow();
|
||||
|
||||
// The timeout implementation functions.
|
||||
void RunTimeout(nsTimeout *aTimeout);
|
||||
void RunTimeout(mozilla::dom::Timeout* aTimeout);
|
||||
void RunTimeout() { RunTimeout(nullptr); }
|
||||
// Return true if |aTimeout| was cleared while its handler ran.
|
||||
bool RunTimeoutHandler(nsTimeout* aTimeout, nsIScriptContext* aScx);
|
||||
bool RunTimeoutHandler(mozilla::dom::Timeout* aTimeout, nsIScriptContext* aScx);
|
||||
// Return true if |aTimeout| needs to be reinserted into the timeout list.
|
||||
bool RescheduleTimeout(nsTimeout* aTimeout, const TimeStamp& now,
|
||||
bool RescheduleTimeout(mozilla::dom::Timeout* aTimeout, const TimeStamp& now,
|
||||
bool aRunningPendingTimeouts);
|
||||
|
||||
void ClearAllTimeouts();
|
||||
// Insert aTimeout into the list, before all timeouts that would
|
||||
// fire after it, but no earlier than mTimeoutInsertionPoint, if any.
|
||||
void InsertTimeoutIntoList(nsTimeout *aTimeout);
|
||||
void InsertTimeoutIntoList(mozilla::dom::Timeout* aTimeout);
|
||||
static void TimerCallback(nsITimer *aTimer, void *aClosure);
|
||||
static void TimerNameCallback(nsITimer* aTimer, void* aClosure, char* aBuf,
|
||||
size_t aLen);
|
||||
uint32_t GetTimeoutId(mozilla::dom::Timeout::Reason aReason);
|
||||
|
||||
// Helper Functions
|
||||
already_AddRefed<nsIDocShellTreeOwner> GetTreeOwner();
|
||||
|
@ -1859,7 +1810,7 @@ protected:
|
|||
RefPtr<mozilla::dom::BarProp> mPersonalbar;
|
||||
RefPtr<mozilla::dom::BarProp> mStatusbar;
|
||||
RefPtr<mozilla::dom::BarProp> mScrollbars;
|
||||
RefPtr<nsDOMWindowUtils> mWindowUtils;
|
||||
RefPtr<nsDOMWindowUtils> mWindowUtils;
|
||||
nsString mStatus;
|
||||
nsString mDefaultStatus;
|
||||
RefPtr<nsGlobalWindowObserver> mObserver; // Inner windows only.
|
||||
|
@ -1884,13 +1835,13 @@ protected:
|
|||
// non-null. In that case, the dummy timeout pointed to by
|
||||
// mTimeoutInsertionPoint may have a later mWhen than some of the timeouts
|
||||
// that come after it.
|
||||
mozilla::LinkedList<nsTimeout> mTimeouts;
|
||||
mozilla::LinkedList<mozilla::dom::Timeout> mTimeouts;
|
||||
// If mTimeoutInsertionPoint is non-null, insertions should happen after it.
|
||||
// This is a dummy timeout at the moment; if that ever changes, the logic in
|
||||
// ResetTimersForNonBackgroundWindow needs to change.
|
||||
nsTimeout* mTimeoutInsertionPoint;
|
||||
uint32_t mTimeoutPublicIdCounter;
|
||||
uint32_t mTimeoutFiringDepth;
|
||||
mozilla::dom::Timeout* mTimeoutInsertionPoint;
|
||||
uint32_t mTimeoutIdCounter;
|
||||
uint32_t mTimeoutFiringDepth;
|
||||
RefPtr<mozilla::dom::Location> mLocation;
|
||||
RefPtr<nsHistory> mHistory;
|
||||
RefPtr<mozilla::dom::CustomElementRegistry> mCustomElements;
|
||||
|
@ -1908,6 +1859,22 @@ protected:
|
|||
|
||||
uint32_t mSerial;
|
||||
|
||||
void DisableIdleCallbackRequests();
|
||||
void UnthrottleIdleCallbackRequests();
|
||||
|
||||
void PostThrottledIdleCallback();
|
||||
|
||||
typedef mozilla::LinkedList<mozilla::dom::IdleRequest> IdleRequests;
|
||||
static void InsertIdleCallbackIntoList(mozilla::dom::IdleRequest* aRequest,
|
||||
IdleRequests& aList);
|
||||
|
||||
// The current idle request callback timeout handle
|
||||
uint32_t mIdleCallbackTimeoutCounter;
|
||||
// The current idle request callback handle
|
||||
uint32_t mIdleRequestCallbackCounter;
|
||||
IdleRequests mIdleRequestCallbacks;
|
||||
IdleRequests mThrottledIdleRequestCallbacks;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mSetOpenerWindowCalled;
|
||||
nsCOMPtr<nsIURI> mLastOpenedURI;
|
||||
|
|
|
@ -6,8 +6,11 @@
|
|||
#ifndef nsIScriptTimeoutHandler_h___
|
||||
#define nsIScriptTimeoutHandler_h___
|
||||
|
||||
#include "nsITimeoutHandler.h"
|
||||
#include "nsTArray.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/Function.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -24,14 +27,14 @@ class Function;
|
|||
* language agnostic way.
|
||||
*/
|
||||
|
||||
class nsIScriptTimeoutHandler : public nsISupports
|
||||
class nsIScriptTimeoutHandler : public nsITimeoutHandler
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCRIPTTIMEOUTHANDLER_IID)
|
||||
|
||||
// Get the Function to call. If this returns nullptr, GetHandlerText() will
|
||||
// be called to get the string.
|
||||
virtual mozilla::dom::Function *GetCallback() = 0;
|
||||
virtual mozilla::dom::Function* GetCallback() = 0;
|
||||
|
||||
// Get the handler text of not a compiled object.
|
||||
virtual const nsAString& GetHandlerText() = 0;
|
||||
|
@ -39,8 +42,6 @@ public:
|
|||
// Get the location of the script.
|
||||
// Note: The memory pointed to by aFileName is owned by the
|
||||
// nsIScriptTimeoutHandler and should not be freed by the caller.
|
||||
virtual void GetLocation(const char **aFileName, uint32_t *aLineNo,
|
||||
uint32_t *aColumn) = 0;
|
||||
|
||||
// If we have a Function, get the arguments for passing to it.
|
||||
virtual const nsTArray<JS::Value>& GetArgs() = 0;
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 nsITimeoutHandler_h___
|
||||
#define nsITimeoutHandler_h___
|
||||
|
||||
#include "nsISupports.h"
|
||||
|
||||
#define NS_ITIMEOUTHANDLER_IID \
|
||||
{ 0xb071a1d3, 0xfd54, 0x40a8, \
|
||||
{ 0x91, 0x9f, 0xc8, 0xf3, 0x3e, 0xb8, 0x3c, 0xfe } }
|
||||
|
||||
class nsITimeoutHandler : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITIMEOUTHANDLER_IID)
|
||||
|
||||
virtual nsresult Call() = 0;
|
||||
|
||||
virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
|
||||
uint32_t* aColumn) = 0;
|
||||
|
||||
virtual void MarkForCC() = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsITimeoutHandler,
|
||||
NS_ITIMEOUTHANDLER_IID)
|
||||
|
||||
#endif // nsITimeoutHandler_h___
|
|
@ -4,21 +4,24 @@
|
|||
* 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 <algorithm>
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Function.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/dom/FunctionBinding.h"
|
||||
#include "nsAXPCNativeCallContext.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIScriptTimeoutHandler.h"
|
||||
#include "nsIXPConnect.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsError.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include <algorithm>
|
||||
#include "mozilla/dom/FunctionBinding.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIScriptTimeoutHandler.h"
|
||||
#include "nsIXPConnect.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "nsAXPCNativeCallContext.h"
|
||||
|
||||
static const char kSetIntervalStr[] = "setInterval";
|
||||
static const char kSetTimeoutStr[] = "setTimeout";
|
||||
|
@ -37,11 +40,11 @@ public:
|
|||
|
||||
nsJSScriptTimeoutHandler();
|
||||
// This will call SwapElements on aArguments with an empty array.
|
||||
nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindow *aWindow,
|
||||
nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindow* aWindow,
|
||||
Function& aFunction,
|
||||
nsTArray<JS::Heap<JS::Value>>&& aArguments,
|
||||
ErrorResult& aError);
|
||||
nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindow *aWindow,
|
||||
nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindow* aWindow,
|
||||
const nsAString& aExpression, bool* aAllowEval,
|
||||
ErrorResult& aError);
|
||||
nsJSScriptTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
|
@ -51,10 +54,22 @@ public:
|
|||
const nsAString& aExpression);
|
||||
|
||||
virtual const nsAString& GetHandlerText() override;
|
||||
|
||||
virtual Function* GetCallback() override
|
||||
{
|
||||
return mFunction;
|
||||
}
|
||||
|
||||
virtual const nsTArray<JS::Value>& GetArgs() override
|
||||
{
|
||||
return mArgs;
|
||||
}
|
||||
|
||||
virtual nsresult Call() override
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
|
||||
uint32_t* aColumn) override
|
||||
{
|
||||
|
@ -63,9 +78,11 @@ public:
|
|||
*aColumn = mColumn;
|
||||
}
|
||||
|
||||
virtual const nsTArray<JS::Value>& GetArgs() override
|
||||
virtual void MarkForCC() override
|
||||
{
|
||||
return mArgs;
|
||||
if (mFunction) {
|
||||
mFunction->MarkForCC();
|
||||
}
|
||||
}
|
||||
|
||||
void ReleaseJSObjects();
|
||||
|
@ -146,6 +163,7 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSScriptTimeoutHandler)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIScriptTimeoutHandler)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ class nsPIDOMWindowInner;
|
|||
class nsPIDOMWindowOuter;
|
||||
class nsPIWindowRoot;
|
||||
class nsXBLPrototypeHandler;
|
||||
struct nsTimeout;
|
||||
|
||||
typedef uint32_t SuspendTypes;
|
||||
|
||||
|
@ -46,6 +45,7 @@ class AudioContext;
|
|||
class Element;
|
||||
class Performance;
|
||||
class ServiceWorkerRegistration;
|
||||
class Timeout;
|
||||
class CustomElementRegistry;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -627,7 +627,7 @@ protected:
|
|||
uint32_t mModalStateDepth;
|
||||
|
||||
// These variables are only used on inner windows.
|
||||
nsTimeout *mRunningTimeout;
|
||||
mozilla::dom::Timeout *mRunningTimeout;
|
||||
|
||||
uint32_t mMutationBits;
|
||||
|
||||
|
|
|
@ -1414,6 +1414,9 @@ DOMInterfaces = {
|
|||
'binaryNames': {
|
||||
'postMessage': 'postMessageMoz',
|
||||
},
|
||||
'implicitJSContext': [
|
||||
'requestIdleCallback'
|
||||
],
|
||||
},
|
||||
|
||||
'WindowProxy': {
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "nsGeolocation.h"
|
||||
#include "nsDOMClassInfoID.h"
|
||||
|
@ -74,7 +73,7 @@ class nsGeolocationRequest final
|
|||
nsGeolocationRequest(Geolocation* aLocator,
|
||||
GeoPositionCallback aCallback,
|
||||
GeoPositionErrorCallback aErrorCallback,
|
||||
nsAutoPtr<PositionOptions>&& aOptions,
|
||||
UniquePtr<PositionOptions>&& aOptions,
|
||||
uint8_t aProtocolType,
|
||||
bool aWatchPositionRequest = false,
|
||||
int32_t aWatchId = 0);
|
||||
|
@ -117,7 +116,7 @@ class nsGeolocationRequest final
|
|||
nsCOMPtr<nsITimer> mTimeoutTimer;
|
||||
GeoPositionCallback mCallback;
|
||||
GeoPositionErrorCallback mErrorCallback;
|
||||
nsAutoPtr<PositionOptions> mOptions;
|
||||
UniquePtr<PositionOptions> mOptions;
|
||||
|
||||
RefPtr<Geolocation> mLocator;
|
||||
|
||||
|
@ -127,10 +126,10 @@ class nsGeolocationRequest final
|
|||
uint8_t mProtocolType;
|
||||
};
|
||||
|
||||
static nsAutoPtr<PositionOptions>
|
||||
static UniquePtr<PositionOptions>
|
||||
CreatePositionOptionsCopy(const PositionOptions& aOptions)
|
||||
{
|
||||
nsAutoPtr<PositionOptions> geoOptions(new PositionOptions());
|
||||
UniquePtr<PositionOptions> geoOptions = MakeUnique<PositionOptions>();
|
||||
|
||||
geoOptions->mEnableHighAccuracy = aOptions.mEnableHighAccuracy;
|
||||
geoOptions->mMaximumAge = aOptions.mMaximumAge;
|
||||
|
@ -291,7 +290,7 @@ PositionError::NotifyCallback(const GeoPositionErrorCallback& aCallback)
|
|||
nsGeolocationRequest::nsGeolocationRequest(Geolocation* aLocator,
|
||||
GeoPositionCallback aCallback,
|
||||
GeoPositionErrorCallback aErrorCallback,
|
||||
nsAutoPtr<PositionOptions>&& aOptions,
|
||||
UniquePtr<PositionOptions>&& aOptions,
|
||||
uint8_t aProtocolType,
|
||||
bool aWatchPositionRequest,
|
||||
int32_t aWatchId)
|
||||
|
@ -1208,7 +1207,7 @@ Geolocation::GetCurrentPosition(PositionCallback& aCallback,
|
|||
NS_IMETHODIMP
|
||||
Geolocation::GetCurrentPosition(nsIDOMGeoPositionCallback* aCallback,
|
||||
nsIDOMGeoPositionErrorCallback* aErrorCallback,
|
||||
nsAutoPtr<PositionOptions>&& aOptions)
|
||||
UniquePtr<PositionOptions>&& aOptions)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aCallback);
|
||||
|
||||
|
@ -1220,7 +1219,7 @@ Geolocation::GetCurrentPosition(nsIDOMGeoPositionCallback* aCallback,
|
|||
nsresult
|
||||
Geolocation::GetCurrentPosition(GeoPositionCallback callback,
|
||||
GeoPositionErrorCallback errorCallback,
|
||||
nsAutoPtr<PositionOptions>&& options)
|
||||
UniquePtr<PositionOptions>&& options)
|
||||
{
|
||||
if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
@ -1286,7 +1285,7 @@ Geolocation::WatchPosition(PositionCallback& aCallback,
|
|||
NS_IMETHODIMP
|
||||
Geolocation::WatchPosition(nsIDOMGeoPositionCallback *aCallback,
|
||||
nsIDOMGeoPositionErrorCallback *aErrorCallback,
|
||||
nsAutoPtr<PositionOptions>&& aOptions,
|
||||
UniquePtr<PositionOptions>&& aOptions,
|
||||
int32_t* aRv)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aCallback);
|
||||
|
@ -1299,7 +1298,7 @@ Geolocation::WatchPosition(nsIDOMGeoPositionCallback *aCallback,
|
|||
nsresult
|
||||
Geolocation::WatchPosition(GeoPositionCallback aCallback,
|
||||
GeoPositionErrorCallback aErrorCallback,
|
||||
nsAutoPtr<PositionOptions>&& aOptions,
|
||||
UniquePtr<PositionOptions>&& aOptions,
|
||||
int32_t* aRv)
|
||||
{
|
||||
if (mWatchingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
|
||||
|
|
|
@ -177,10 +177,10 @@ private:
|
|||
|
||||
nsresult GetCurrentPosition(GeoPositionCallback aCallback,
|
||||
GeoPositionErrorCallback aErrorCallback,
|
||||
nsAutoPtr<PositionOptions>&& aOptions);
|
||||
UniquePtr<PositionOptions>&& aOptions);
|
||||
nsresult WatchPosition(GeoPositionCallback aCallback,
|
||||
GeoPositionErrorCallback aErrorCallback,
|
||||
nsAutoPtr<PositionOptions>&& aOptions, int32_t* aRv);
|
||||
UniquePtr<PositionOptions>&& aOptions, int32_t* aRv);
|
||||
|
||||
bool RegisterRequestWithPrompt(nsGeolocationRequest* request);
|
||||
|
||||
|
|
|
@ -11,13 +11,13 @@ interface nsIDOMGeoPositionErrorCallback;
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
struct PositionOptions;
|
||||
}
|
||||
}
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
template<class T> class nsAutoPtr;
|
||||
#include "mozilla/UniquePtr.h"
|
||||
%}
|
||||
|
||||
native PositionOptionsRef(nsAutoPtr<mozilla::dom::PositionOptions>&&);
|
||||
native PositionOptionsRef(mozilla::UniquePtr<mozilla::dom::PositionOptions>&&);
|
||||
|
||||
[builtinclass, uuid(9142ab45-0ab5-418c-9bab-338a6d271d4f)]
|
||||
interface nsIDOMGeoGeolocation : nsISupports
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
#include "nsDocShell.h"
|
||||
#include "nsIConsoleListener.h"
|
||||
#include "nsICycleCollectorListener.h"
|
||||
#include "nsIIdlePeriod.h"
|
||||
#include "nsIDragService.h"
|
||||
#include "nsIIPCBackgroundChildCreateCallback.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
|
|
|
@ -3948,7 +3948,7 @@ AddGeolocationListener(nsIDOMGeoPositionCallback* watcher,
|
|||
return -1;
|
||||
}
|
||||
|
||||
nsAutoPtr<PositionOptions> options(new PositionOptions());
|
||||
UniquePtr<PositionOptions> options = MakeUnique<PositionOptions>();
|
||||
options->mTimeout = 0;
|
||||
options->mMaximumAge = 0;
|
||||
options->mEnableHighAccuracy = highAccuracy;
|
||||
|
|
|
@ -2651,6 +2651,13 @@ SourceMediaStream::DestroyImpl()
|
|||
{
|
||||
CloseAudioInput();
|
||||
|
||||
GraphImpl()->AssertOnGraphThreadOrNotRunning();
|
||||
for (int32_t i = mConsumers.Length() - 1; i >= 0; --i) {
|
||||
// Disconnect before we come under mMutex's lock since it can call back
|
||||
// through RemoveDirectTrackListenerImpl() and deadlock.
|
||||
mConsumers[i]->Disconnect();
|
||||
}
|
||||
|
||||
// Hold mMutex while mGraph is reset so that other threads holding mMutex
|
||||
// can null-check know that the graph will not destroyed.
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
|
|
@ -95,7 +95,7 @@ void AudioInputCubeb::UpdateDeviceList()
|
|||
if (devices->device[i]->preferred & CUBEB_DEVICE_PREF_VOICE) {
|
||||
// There can be only one... we hope
|
||||
NS_ASSERTION(mDefaultDevice == -1, "multiple default cubeb input devices!");
|
||||
mDefaultDevice = i;
|
||||
mDefaultDevice = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,7 +101,6 @@ DIRS += [
|
|||
'xml',
|
||||
'xslt',
|
||||
'xul',
|
||||
'resourcestats',
|
||||
'manifest',
|
||||
'vr',
|
||||
'newapps',
|
||||
|
|
|
@ -376,11 +376,10 @@ this.PushDB.prototype = {
|
|||
*
|
||||
* @param {String} aKeyID The registration ID.
|
||||
* @param {Function} aUpdateFunc A function that receives the existing
|
||||
* registration record as its argument, and returns a new record. If the
|
||||
* function returns `null` or `undefined`, the record will not be updated.
|
||||
* If the record does not exist, the function will not be called.
|
||||
* @returns {Promise} A promise resolved with either the updated record, or
|
||||
* `undefined` if the record was not updated.
|
||||
* registration record as its argument, and returns a new record.
|
||||
* @returns {Promise} A promise resolved with either the updated record.
|
||||
* Rejects if the record does not exist, or the function returns an invalid
|
||||
* record.
|
||||
*/
|
||||
update: function(aKeyID, aUpdateFunc) {
|
||||
return new Promise((resolve, reject) =>
|
||||
|
@ -393,14 +392,13 @@ this.PushDB.prototype = {
|
|||
|
||||
let record = aEvent.target.result;
|
||||
if (!record) {
|
||||
console.error("update: Record does not exist", aKeyID);
|
||||
return;
|
||||
throw new Error("Record " + aKeyID + " does not exist");
|
||||
}
|
||||
let newRecord = aUpdateFunc(this.toPushRecord(record));
|
||||
if (!this.isValidRecord(newRecord)) {
|
||||
console.error("update: Ignoring invalid update",
|
||||
aKeyID, newRecord);
|
||||
return;
|
||||
throw new Error("Invalid update for record " + aKeyID);
|
||||
}
|
||||
function putRecord() {
|
||||
let req = aStore.put(newRecord);
|
||||
|
|
|
@ -492,6 +492,7 @@ this.PushService = {
|
|||
|
||||
this._setState(PUSH_SERVICE_ACTIVATING);
|
||||
|
||||
prefs.observe("serverURL", this);
|
||||
Services.obs.addObserver(this, "quit-application", false);
|
||||
|
||||
if (options.serverURI) {
|
||||
|
@ -503,8 +504,6 @@ this.PushService = {
|
|||
} else {
|
||||
// This is only used for testing. Different tests require connecting to
|
||||
// slightly different URLs.
|
||||
prefs.observe("serverURL", this);
|
||||
|
||||
this._stateChangeProcessEnqueue(_ =>
|
||||
this._changeServerURL(prefs.get("serverURL"), STARTING_SERVICE_EVENT));
|
||||
}
|
||||
|
@ -829,9 +828,6 @@ this.PushService = {
|
|||
});
|
||||
});
|
||||
}).then(record => {
|
||||
if (!record) {
|
||||
throw new Error("Ignoring update for key ID " + keyID);
|
||||
}
|
||||
gPushNotifier.notifySubscriptionModified(record.scope,
|
||||
record.principal);
|
||||
return record;
|
||||
|
@ -880,18 +876,16 @@ this.PushService = {
|
|||
}
|
||||
return record;
|
||||
}).then(record => {
|
||||
if (record) {
|
||||
if (record.isExpired()) {
|
||||
this._recordDidNotNotify(kDROP_NOTIFICATION_REASON_EXPIRED);
|
||||
// Drop the registration in the background. If the user returns to the
|
||||
// site, the service worker will be notified on the next `idle-daily`
|
||||
// event.
|
||||
this._backgroundUnregister(record,
|
||||
Ci.nsIPushErrorReporter.UNSUBSCRIBE_QUOTA_EXCEEDED);
|
||||
} else {
|
||||
gPushNotifier.notifySubscriptionModified(record.scope,
|
||||
record.principal);
|
||||
}
|
||||
if (record.isExpired()) {
|
||||
this._recordDidNotNotify(kDROP_NOTIFICATION_REASON_EXPIRED);
|
||||
// Drop the registration in the background. If the user returns to the
|
||||
// site, the service worker will be notified on the next `idle-daily`
|
||||
// event.
|
||||
this._backgroundUnregister(record,
|
||||
Ci.nsIPushErrorReporter.UNSUBSCRIBE_QUOTA_EXCEEDED);
|
||||
} else {
|
||||
gPushNotifier.notifySubscriptionModified(record.scope,
|
||||
record.principal);
|
||||
}
|
||||
if (this._updateQuotaTestCallback) {
|
||||
// Callback so that test may be notified when the quota update is complete.
|
||||
|
|
|
@ -1,3 +1,14 @@
|
|||
// Returns the test H/2 server port, throwing if it's missing or invalid.
|
||||
function getTestServerPort() {
|
||||
let portEnv = Cc["@mozilla.org/process/environment;1"]
|
||||
.getService(Ci.nsIEnvironment).get("MOZHTTP2_PORT");
|
||||
let port = parseInt(portEnv, 10);
|
||||
if (!Number.isFinite(port) || port < 1 || port > 65535) {
|
||||
throw new Error(`Invalid port in MOZHTTP2_PORT env var: ${portEnv}`);
|
||||
}
|
||||
do_print(`Using HTTP/2 server on port ${port}`);
|
||||
return port;
|
||||
}
|
||||
|
||||
// Support for making sure we can talk to the invalid cert the server presents
|
||||
var CertOverrideListener = function(host, port, bits) {
|
||||
|
|
|
@ -13,10 +13,7 @@ var tlsProfile;
|
|||
var serverPort = -1;
|
||||
|
||||
function run_test() {
|
||||
var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
serverPort = env.get("MOZHTTP2_PORT");
|
||||
do_check_neq(serverPort, null);
|
||||
dump("using port " + serverPort + "\n");
|
||||
serverPort = getTestServerPort();
|
||||
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
|
|
|
@ -14,9 +14,7 @@ var serverURL;
|
|||
var serverPort = -1;
|
||||
|
||||
function run_test() {
|
||||
var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
serverPort = env.get("MOZHTTP2_PORT");
|
||||
do_check_neq(serverPort, null);
|
||||
serverPort = getTestServerPort();
|
||||
|
||||
do_get_profile();
|
||||
prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
|
|
|
@ -16,9 +16,7 @@ var pushConnectionEnabled;
|
|||
var db;
|
||||
|
||||
function run_test() {
|
||||
var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
serverPort = env.get("MOZHTTP2_PORT");
|
||||
do_check_neq(serverPort, null);
|
||||
serverPort = getTestServerPort();
|
||||
|
||||
do_get_profile();
|
||||
prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
|
|
|
@ -4,15 +4,6 @@
|
|||
'use strict';
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://testing-common/PromiseTestUtils.jsm");
|
||||
|
||||
///////////////////
|
||||
//
|
||||
// Whitelisting this test.
|
||||
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
|
||||
//
|
||||
// Instances of the rejection "record is undefined" may or may not appear.
|
||||
PromiseTestUtils.thisTestLeaksUncaughtRejectionsAndShouldBeFixed();
|
||||
|
||||
const {PushDB, PushService, PushServiceHttp2} = serviceExports;
|
||||
|
||||
|
@ -21,9 +12,7 @@ var prefs;
|
|||
var serverPort = -1;
|
||||
|
||||
function run_test() {
|
||||
var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
serverPort = env.get("MOZHTTP2_PORT");
|
||||
do_check_neq(serverPort, null);
|
||||
serverPort = getTestServerPort();
|
||||
|
||||
do_get_profile();
|
||||
prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
|
|
|
@ -4,15 +4,6 @@
|
|||
'use strict';
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://testing-common/PromiseTestUtils.jsm");
|
||||
|
||||
///////////////////
|
||||
//
|
||||
// Whitelisting this test.
|
||||
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
|
||||
//
|
||||
// Instances of the rejection "record is undefined" may or may not appear.
|
||||
PromiseTestUtils.thisTestLeaksUncaughtRejectionsAndShouldBeFixed();
|
||||
|
||||
const {PushDB, PushService, PushServiceHttp2} = serviceExports;
|
||||
|
||||
|
@ -24,9 +15,7 @@ var pushConnectionEnabled;
|
|||
var serverPort = -1;
|
||||
|
||||
function run_test() {
|
||||
var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
serverPort = env.get("MOZHTTP2_PORT");
|
||||
do_check_neq(serverPort, null);
|
||||
serverPort = getTestServerPort();
|
||||
|
||||
do_get_profile();
|
||||
prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
|
|
|
@ -1,538 +0,0 @@
|
|||
/* 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/.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['ResourceStatsDB'];
|
||||
|
||||
const DEBUG = false;
|
||||
function debug(s) { dump("-*- ResourceStatsDB: " + s + "\n"); }
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.importGlobalProperties(["indexedDB"]);
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "appsService",
|
||||
"@mozilla.org/AppsService;1",
|
||||
"nsIAppsService");
|
||||
|
||||
const DB_NAME = "resource_stats";
|
||||
const DB_VERSION = 1;
|
||||
const POWER_STATS_STORE = "power_stats_store";
|
||||
const NETWORK_STATS_STORE = "network_stats_store";
|
||||
const ALARM_STORE = "alarm_store";
|
||||
|
||||
const statsStoreNames = {
|
||||
power: POWER_STATS_STORE,
|
||||
network: NETWORK_STATS_STORE
|
||||
};
|
||||
|
||||
// Constant defining the sampling rate.
|
||||
const SAMPLE_RATE = 24 * 60 * 60 * 1000; // 1 day.
|
||||
|
||||
// Constant defining the MAX age of stored stats.
|
||||
const MAX_STORAGE_AGE = 180 * SAMPLE_RATE; // 180 days.
|
||||
|
||||
this.ResourceStatsDB = function ResourceStatsDB() {
|
||||
if (DEBUG) {
|
||||
debug("Constructor()");
|
||||
}
|
||||
|
||||
this.initDBHelper(DB_NAME, DB_VERSION,
|
||||
[POWER_STATS_STORE, NETWORK_STATS_STORE, ALARM_STORE]);
|
||||
};
|
||||
|
||||
ResourceStatsDB.prototype = {
|
||||
__proto__: IndexedDBHelper.prototype,
|
||||
|
||||
_dbNewTxn: function(aStoreName, aTxnType, aCallback, aTxnCb) {
|
||||
function successCb(aResult) {
|
||||
aTxnCb(null, aResult);
|
||||
}
|
||||
function errorCb(aError) {
|
||||
aTxnCb(aError, null);
|
||||
}
|
||||
return this.newTxn(aTxnType, aStoreName, aCallback, successCb, errorCb);
|
||||
},
|
||||
|
||||
upgradeSchema: function(aTransaction, aDb, aOldVersion, aNewVersion) {
|
||||
if (DEBUG) {
|
||||
debug("Upgrade DB from ver." + aOldVersion + " to ver." + aNewVersion);
|
||||
}
|
||||
|
||||
let objectStore;
|
||||
|
||||
// Create PowerStatsStore.
|
||||
objectStore = aDb.createObjectStore(POWER_STATS_STORE, {
|
||||
keyPath: ["appId", "serviceType", "component", "timestamp"]
|
||||
});
|
||||
objectStore.createIndex("component", "component", { unique: false });
|
||||
|
||||
// Create NetworkStatsStore.
|
||||
objectStore = aDb.createObjectStore(NETWORK_STATS_STORE, {
|
||||
keyPath: ["appId", "serviceType", "component", "timestamp"]
|
||||
});
|
||||
objectStore.createIndex("component", "component", { unique: false });
|
||||
|
||||
// Create AlarmStore.
|
||||
objectStore = aDb.createObjectStore(ALARM_STORE, {
|
||||
keyPath: "alarmId",
|
||||
autoIncrement: true
|
||||
});
|
||||
objectStore.createIndex("type", "type", { unique: false });
|
||||
// Index for resource control target.
|
||||
objectStore.createIndex("controlTarget",
|
||||
["type", "manifestURL", "serviceType", "component"],
|
||||
{ unique: false });
|
||||
},
|
||||
|
||||
// Convert to UTC according to the current timezone and the filter timestamp
|
||||
// to get SAMPLE_RATE precission.
|
||||
_normalizeTime: function(aTime, aOffset) {
|
||||
let time = Math.floor((aTime - aOffset) / SAMPLE_RATE) * SAMPLE_RATE;
|
||||
|
||||
return time;
|
||||
},
|
||||
|
||||
/**
|
||||
* aRecordArray contains an array of json objects storing network stats.
|
||||
* The structure of the json object =
|
||||
* {
|
||||
* appId: XX,
|
||||
* serviceType: "XX",
|
||||
* componentStats: {
|
||||
* "component_1": { receivedBytes: XX, sentBytes: XX },
|
||||
* "component_2": { receivedBytes: XX, sentBytes: XX },
|
||||
* ...
|
||||
* },
|
||||
* }
|
||||
*/
|
||||
saveNetworkStats: function(aRecordArray, aTimestamp, aResultCb) {
|
||||
if (DEBUG) {
|
||||
debug("saveNetworkStats()");
|
||||
}
|
||||
|
||||
let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||
let timestamp = this._normalizeTime(aTimestamp, offset);
|
||||
|
||||
this._dbNewTxn(NETWORK_STATS_STORE, "readwrite", function(aTxn, aStore) {
|
||||
aRecordArray.forEach(function(aRecord) {
|
||||
let stats = {
|
||||
appId: aRecord.appId,
|
||||
serviceType: aRecord.serviceType,
|
||||
component: "",
|
||||
timestamp: timestamp,
|
||||
receivedBytes: 0,
|
||||
sentBytes: 0
|
||||
};
|
||||
|
||||
let totalReceivedBytes = 0;
|
||||
let totalSentBytes = 0;
|
||||
|
||||
// Save stats of each component.
|
||||
let data = aRecord.componentStats;
|
||||
for (let component in data) {
|
||||
// Save stats to database.
|
||||
stats.component = component;
|
||||
stats.receivedBytes = data[component].receivedBytes;
|
||||
stats.sentBytes = data[component].sentBytes;
|
||||
aStore.put(stats);
|
||||
if (DEBUG) {
|
||||
debug("Save network stats: " + JSON.stringify(stats));
|
||||
}
|
||||
|
||||
// Accumulated to tatal stats.
|
||||
totalReceivedBytes += stats.receivedBytes;
|
||||
totalSentBytes += stats.sentBytes;
|
||||
}
|
||||
|
||||
// Save total stats.
|
||||
stats.component = "";
|
||||
stats.receivedBytes = totalReceivedBytes;
|
||||
stats.sentBytes = totalSentBytes;
|
||||
aStore.put(stats);
|
||||
if (DEBUG) {
|
||||
debug("Save network stats: " + JSON.stringify(stats));
|
||||
}
|
||||
});
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
/**
|
||||
* aRecordArray contains an array of json objects storing power stats.
|
||||
* The structure of the json object =
|
||||
* {
|
||||
* appId: XX,
|
||||
* serviceType: "XX",
|
||||
* componentStats: {
|
||||
* "component_1": XX, // consumedPower
|
||||
* "component_2": XX,
|
||||
* ...
|
||||
* },
|
||||
* }
|
||||
*/
|
||||
savePowerStats: function(aRecordArray, aTimestamp, aResultCb) {
|
||||
if (DEBUG) {
|
||||
debug("savePowerStats()");
|
||||
}
|
||||
let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||
let timestamp = this._normalizeTime(aTimestamp, offset);
|
||||
|
||||
this._dbNewTxn(POWER_STATS_STORE, "readwrite", function(aTxn, aStore) {
|
||||
aRecordArray.forEach(function(aRecord) {
|
||||
let stats = {
|
||||
appId: aRecord.appId,
|
||||
serviceType: aRecord.serviceType,
|
||||
component: "",
|
||||
timestamp: timestamp,
|
||||
consumedPower: aRecord.totalStats
|
||||
};
|
||||
|
||||
let totalConsumedPower = 0;
|
||||
|
||||
// Save stats of each component to database.
|
||||
let data = aRecord.componentStats;
|
||||
for (let component in data) {
|
||||
// Save stats to database.
|
||||
stats.component = component;
|
||||
stats.consumedPower = data[component];
|
||||
aStore.put(stats);
|
||||
if (DEBUG) {
|
||||
debug("Save power stats: " + JSON.stringify(stats));
|
||||
}
|
||||
// Accumulated to total stats.
|
||||
totalConsumedPower += stats.consumedPower;
|
||||
}
|
||||
|
||||
// Save total stats.
|
||||
stats.component = "";
|
||||
stats.consumedPower = totalConsumedPower;
|
||||
aStore.put(stats);
|
||||
if (DEBUG) {
|
||||
debug("Save power stats: " + JSON.stringify(stats));
|
||||
}
|
||||
});
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
// Get stats from a store.
|
||||
getStats: function(aType, aManifestURL, aServiceType, aComponent,
|
||||
aStart, aEnd, aResultCb) {
|
||||
if (DEBUG) {
|
||||
debug(aType + "Mgr.getStats()");
|
||||
}
|
||||
|
||||
let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||
|
||||
// Get appId and check whether manifestURL is a valid app.
|
||||
let appId = 0;
|
||||
if (aManifestURL) {
|
||||
appId = appsService.getAppLocalIdByManifestURL(aManifestURL);
|
||||
|
||||
if (!appId) {
|
||||
aResultCb("Invalid manifestURL", null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get store name.
|
||||
let storeName = statsStoreNames[aType];
|
||||
|
||||
// Normalize start time and end time to SAMPLE_RATE precission.
|
||||
let start = this._normalizeTime(aStart, offset);
|
||||
let end = this._normalizeTime(aEnd, offset);
|
||||
if (DEBUG) {
|
||||
debug("Query time range: " + start + " to " + end);
|
||||
debug("[appId, serviceType, component] = [" + appId + ", " + aServiceType
|
||||
+ ", " + aComponent + "]");
|
||||
}
|
||||
|
||||
// Create filters.
|
||||
let lowerFilter = [appId, aServiceType, aComponent, start];
|
||||
let upperFilter = [appId, aServiceType, aComponent, end];
|
||||
|
||||
// Execute DB query.
|
||||
this._dbNewTxn(storeName, "readonly", function(aTxn, aStore) {
|
||||
let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
|
||||
|
||||
let statsData = [];
|
||||
|
||||
if (!aTxn.result) {
|
||||
aTxn.result = Object.create(null);
|
||||
}
|
||||
aTxn.result.type = aType;
|
||||
aTxn.result.component = aComponent;
|
||||
aTxn.result.serviceType = aServiceType;
|
||||
aTxn.result.manifestURL = aManifestURL;
|
||||
aTxn.result.start = start + offset;
|
||||
aTxn.result.end = end + offset;
|
||||
// Since ResourceStats() would require SAMPLE_RATE when filling the empty
|
||||
// entries of statsData array, we append SAMPLE_RATE to the result field
|
||||
// to save an IPC call.
|
||||
aTxn.result.sampleRate = SAMPLE_RATE;
|
||||
|
||||
let request = aStore.openCursor(range, "prev");
|
||||
if (aType == "power") {
|
||||
request.onsuccess = function(aEvent) {
|
||||
var cursor = aEvent.target.result;
|
||||
if (cursor) {
|
||||
if (DEBUG) {
|
||||
debug("Get " + JSON.stringify(cursor.value));
|
||||
}
|
||||
|
||||
// Covert timestamp to current timezone.
|
||||
statsData.push({
|
||||
timestamp: cursor.value.timestamp + offset,
|
||||
consumedPower: cursor.value.consumedPower
|
||||
});
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
aTxn.result.statsData = statsData;
|
||||
};
|
||||
} else if (aType == "network") {
|
||||
request.onsuccess = function(aEvent) {
|
||||
var cursor = aEvent.target.result;
|
||||
if (cursor) {
|
||||
if (DEBUG) {
|
||||
debug("Get " + JSON.stringify(cursor.value));
|
||||
}
|
||||
|
||||
// Covert timestamp to current timezone.
|
||||
statsData.push({
|
||||
timestamp: cursor.value.timestamp + offset,
|
||||
receivedBytes: cursor.value.receivedBytes,
|
||||
sentBytes: cursor.value.sentBytes
|
||||
});
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
aTxn.result.statsData = statsData;
|
||||
};
|
||||
}
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
// Delete the stats of a specific app/service (within a specified time range).
|
||||
clearStats: function(aType, aAppId, aServiceType, aComponent,
|
||||
aStart, aEnd, aResultCb) {
|
||||
if (DEBUG) {
|
||||
debug(aType + "Mgr.clearStats()");
|
||||
}
|
||||
|
||||
let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||
|
||||
// Get store name.
|
||||
let storeName = statsStoreNames[aType];
|
||||
|
||||
// Normalize start and end time to SAMPLE_RATE precission.
|
||||
let start = this._normalizeTime(aStart, offset);
|
||||
let end = this._normalizeTime(aEnd, offset);
|
||||
if (DEBUG) {
|
||||
debug("Query time range: " + start + " to " + end);
|
||||
debug("[appId, serviceType, component] = [" + aAppId + ", " + aServiceType
|
||||
+ ", " + aComponent + "]");
|
||||
}
|
||||
|
||||
// Create filters.
|
||||
let lowerFilter = [aAppId, aServiceType, aComponent, start];
|
||||
let upperFilter = [aAppId, aServiceType, aComponent, end];
|
||||
|
||||
// Execute clear operation.
|
||||
this._dbNewTxn(storeName, "readwrite", function(aTxn, aStore) {
|
||||
let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
|
||||
let request = aStore.openCursor(range).onsuccess = function(aEvent) {
|
||||
let cursor = aEvent.target.result;
|
||||
if (cursor) {
|
||||
if (DEBUG) {
|
||||
debug("Delete " + JSON.stringify(cursor.value) + " from database");
|
||||
}
|
||||
cursor.delete();
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
};
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
// Delete all stats saved in a store.
|
||||
clearAllStats: function(aType, aResultCb) {
|
||||
if (DEBUG) {
|
||||
debug(aType + "Mgr.clearAllStats()");
|
||||
}
|
||||
|
||||
let storeName = statsStoreNames[aType];
|
||||
|
||||
// Execute clear operation.
|
||||
this._dbNewTxn(storeName, "readwrite", function(aTxn, aStore) {
|
||||
if (DEBUG) {
|
||||
debug("Clear " + aType + " stats from datastore");
|
||||
}
|
||||
aStore.clear();
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
addAlarm: function(aAlarm, aResultCb) {
|
||||
if (DEBUG) {
|
||||
debug(aAlarm.type + "Mgr.addAlarm()");
|
||||
debug("alarm = " + JSON.stringify(aAlarm));
|
||||
}
|
||||
|
||||
this._dbNewTxn(ALARM_STORE, "readwrite", function(aTxn, aStore) {
|
||||
aStore.put(aAlarm).onsuccess = function setResult(aEvent) {
|
||||
// Get alarmId.
|
||||
aTxn.result = aEvent.target.result;
|
||||
if (DEBUG) {
|
||||
debug("New alarm ID: " + aTxn.result);
|
||||
}
|
||||
};
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
// Convert DB record to alarm object.
|
||||
_recordToAlarm: function(aRecord) {
|
||||
let alarm = {
|
||||
alarmId: aRecord.alarmId,
|
||||
type: aRecord.type,
|
||||
component: aRecord.component,
|
||||
serviceType: aRecord.serviceType,
|
||||
manifestURL: aRecord.manifestURL,
|
||||
threshold: aRecord.threshold,
|
||||
data: aRecord.data
|
||||
};
|
||||
|
||||
return alarm;
|
||||
},
|
||||
|
||||
getAlarms: function(aType, aOptions, aResultCb) {
|
||||
if (DEBUG) {
|
||||
debug(aType + "Mgr.getAlarms()");
|
||||
debug("[appId, serviceType, component] = [" + aOptions.appId + ", "
|
||||
+ aOptions.serviceType + ", " + aOptions.component + "]");
|
||||
}
|
||||
|
||||
// Execute clear operation.
|
||||
this._dbNewTxn(ALARM_STORE, "readwrite", function(aTxn, aStore) {
|
||||
if (!aTxn.result) {
|
||||
aTxn.result = [];
|
||||
}
|
||||
|
||||
let indexName = null;
|
||||
let range = null;
|
||||
|
||||
if (aOptions) { // Get alarms associated to specified statsOptions.
|
||||
indexName = "controlTarget";
|
||||
range = IDBKeyRange.only([aType, aOptions.manifestURL,
|
||||
aOptions.serviceType, aOptions.component]);
|
||||
} else { // Get all alarms of the specified type.
|
||||
indexName = "type";
|
||||
range = IDBKeyRange.only(aType);
|
||||
}
|
||||
|
||||
let request = aStore.index(indexName).openCursor(range);
|
||||
request.onsuccess = function onsuccess(aEvent) {
|
||||
let cursor = aEvent.target.result;
|
||||
if (cursor) {
|
||||
aTxn.result.push(this._recordToAlarm(cursor.value));
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
}.bind(this);
|
||||
}.bind(this), aResultCb);
|
||||
},
|
||||
|
||||
removeAlarm: function(aType, aAlarmId, aResultCb) {
|
||||
if (DEBUG) {
|
||||
debug("removeAlarms(" + aAlarmId + ")");
|
||||
}
|
||||
|
||||
// Execute clear operation.
|
||||
this._dbNewTxn(ALARM_STORE, "readwrite", function(aTxn, aStore) {
|
||||
aStore.get(aAlarmId).onsuccess = function onsuccess(aEvent) {
|
||||
let alarm = aEvent.target.result;
|
||||
aTxn.result = false;
|
||||
if (!alarm || aType !== alarm.type) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("Remove alarm " + JSON.stringify(alarm) + " from datastore");
|
||||
}
|
||||
aStore.delete(aAlarmId);
|
||||
aTxn.result = true;
|
||||
};
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
removeAllAlarms: function(aType, aResultCb) {
|
||||
if (DEBUG) {
|
||||
debug(aType + "Mgr.removeAllAlarms()");
|
||||
}
|
||||
|
||||
// Execute clear operation.
|
||||
this._dbNewTxn(ALARM_STORE, "readwrite", function(aTxn, aStore) {
|
||||
if (DEBUG) {
|
||||
debug("Remove all " + aType + " alarms from datastore.");
|
||||
}
|
||||
|
||||
let range = IDBKeyRange.only(aType);
|
||||
let request = aStore.index("type").openCursor(range);
|
||||
request.onsuccess = function onsuccess(aEvent) {
|
||||
let cursor = aEvent.target.result;
|
||||
if (cursor) {
|
||||
if (DEBUG) {
|
||||
debug("Remove " + JSON.stringify(cursor.value) + " from database.");
|
||||
}
|
||||
cursor.delete();
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
};
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
// Get all index keys of the component.
|
||||
getComponents: function(aType, aResultCb) {
|
||||
if (DEBUG) {
|
||||
debug(aType + "Mgr.getComponents()");
|
||||
}
|
||||
|
||||
let storeName = statsStoreNames[aType];
|
||||
|
||||
this._dbNewTxn(storeName, "readonly", function(aTxn, aStore) {
|
||||
if (!aTxn.result) {
|
||||
aTxn.result = [];
|
||||
}
|
||||
|
||||
let request = aStore.index("component").openKeyCursor(null, "nextunique");
|
||||
request.onsuccess = function onsuccess(aEvent) {
|
||||
let cursor = aEvent.target.result;
|
||||
if (cursor) {
|
||||
aTxn.result.push(cursor.key);
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove "" from the result, which indicates sum of all
|
||||
// components' stats.
|
||||
let index = aTxn.result.indexOf("");
|
||||
if (index > -1) {
|
||||
aTxn.result.splice(index, 1);
|
||||
}
|
||||
};
|
||||
}, aResultCb);
|
||||
},
|
||||
|
||||
get sampleRate () {
|
||||
return SAMPLE_RATE;
|
||||
},
|
||||
|
||||
get maxStorageAge() {
|
||||
return MAX_STORAGE_AGE;
|
||||
},
|
||||
};
|
||||
|
|
@ -1,468 +0,0 @@
|
|||
/* 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/.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const DEBUG = false;
|
||||
function debug(s) { dump("-*- ResourceStatsManager: " + s + "\n"); }
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||
|
||||
// Constant defines supported statistics.
|
||||
const resourceTypeList = ["network", "power"];
|
||||
|
||||
function NetworkStatsData(aStatsData) {
|
||||
if (DEBUG) {
|
||||
debug("NetworkStatsData(): " + JSON.stringify(aStatsData));
|
||||
}
|
||||
|
||||
this.receivedBytes = aStatsData.receivedBytes || 0;
|
||||
this.sentBytes = aStatsData.sentBytes || 0;
|
||||
this.timestamp = aStatsData.timestamp;
|
||||
}
|
||||
|
||||
NetworkStatsData.prototype = {
|
||||
classID: Components.ID("{dce5729a-ba92-4185-8854-e29e71b9e8a2}"),
|
||||
contractID: "@mozilla.org/networkStatsData;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([])
|
||||
};
|
||||
|
||||
function PowerStatsData(aStatsData) {
|
||||
if (DEBUG) {
|
||||
debug("PowerStatsData(): " + JSON.stringify(aStatsData));
|
||||
}
|
||||
|
||||
this.consumedPower = aStatsData.consumedPower || 0;
|
||||
this.timestamp = aStatsData.timestamp;
|
||||
}
|
||||
|
||||
PowerStatsData.prototype = {
|
||||
classID: Components.ID("{acb9af6c-8143-4e59-bc18-4bb1736a4004}"),
|
||||
contractID: "@mozilla.org/powerStatsData;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([])
|
||||
};
|
||||
|
||||
function ResourceStats(aWindow, aStats) {
|
||||
if (DEBUG) {
|
||||
debug("ResourceStats(): " + JSON.stringify(aStats));
|
||||
}
|
||||
|
||||
this._window = aWindow;
|
||||
this.type = aStats.type;
|
||||
this.component = aStats.component || null;
|
||||
this.serviceType = aStats.serviceType || null;
|
||||
this.manifestURL = aStats.manifestURL || null;
|
||||
this.start = aStats.start;
|
||||
this.end = aStats.end;
|
||||
this.statsData = new aWindow.Array();
|
||||
|
||||
// A function creates a StatsData object according to type.
|
||||
let createStatsDataObject = null;
|
||||
let self = this;
|
||||
switch (this.type) {
|
||||
case "power":
|
||||
createStatsDataObject = function(aStats) {
|
||||
let chromeObj = new PowerStatsData(aStats);
|
||||
return self._window.PowerStatsData._create(self._window, chromeObj);
|
||||
};
|
||||
break;
|
||||
case "network":
|
||||
createStatsDataObject = function(aStats) {
|
||||
let chromeObj = new NetworkStatsData(aStats);
|
||||
return self._window.NetworkStatsData._create(self._window, chromeObj);
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
let sampleRate = aStats.sampleRate;
|
||||
let queryResult = aStats.statsData;
|
||||
let stats = queryResult.pop(); // Pop out the last element.
|
||||
let timestamp = this.start;
|
||||
|
||||
// Push query result to statsData, and fill empty elements so that:
|
||||
// 1. the timestamp of the first element of statsData is equal to start;
|
||||
// 2. the timestamp of the last element of statsData is equal to end;
|
||||
// 3. the timestamp difference of every neighboring elements is SAMPLE_RATE.
|
||||
for (; timestamp <= this.end; timestamp += sampleRate) {
|
||||
if (!stats || stats.timestamp != timestamp) {
|
||||
// If dataArray is empty or timestamp are not equal, push a dummy object
|
||||
// (which stats are set to 0) to statsData.
|
||||
this.statsData.push(createStatsDataObject({ timestamp: timestamp }));
|
||||
} else {
|
||||
// Push stats to statsData and pop a new element form queryResult.
|
||||
this.statsData.push(createStatsDataObject(stats));
|
||||
stats = queryResult.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResourceStats.prototype = {
|
||||
getData: function() {
|
||||
return this.statsData;
|
||||
},
|
||||
|
||||
classID: Components.ID("{b7c970f2-3d58-4966-9633-2024feb5132b}"),
|
||||
contractID: "@mozilla.org/resourceStats;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([])
|
||||
};
|
||||
|
||||
function ResourceStatsAlarm(aWindow, aAlarm) {
|
||||
if (DEBUG) {
|
||||
debug("ResourceStatsAlarm(): " + JSON.stringify(aAlarm));
|
||||
}
|
||||
|
||||
this.alarmId = aAlarm.alarmId;
|
||||
this.type = aAlarm.type;
|
||||
this.component = aAlarm.component || null;
|
||||
this.serviceType = aAlarm.serviceType || null;
|
||||
this.manifestURL = aAlarm.manifestURL || null;
|
||||
this.threshold = aAlarm.threshold;
|
||||
|
||||
// Clone data object using structured clone algorithm.
|
||||
this.data = null;
|
||||
if (aAlarm.data) {
|
||||
this.data = Cu.cloneInto(aAlarm.data, aWindow);
|
||||
}
|
||||
}
|
||||
|
||||
ResourceStatsAlarm.prototype = {
|
||||
classID: Components.ID("{e2b66e7a-0ff1-4015-8690-a2a3f6a5b63a}"),
|
||||
contractID: "@mozilla.org/resourceStatsAlarm;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([]),
|
||||
};
|
||||
|
||||
function ResourceStatsManager() {
|
||||
if (DEBUG) {
|
||||
debug("constructor()");
|
||||
}
|
||||
}
|
||||
|
||||
ResourceStatsManager.prototype = {
|
||||
__proto__: DOMRequestIpcHelper.prototype,
|
||||
|
||||
// Check time range.
|
||||
_checkTimeRange: function(aStart, aEnd) {
|
||||
if (DEBUG) {
|
||||
debug("_checkTimeRange(): " + aStart + " to " + aEnd);
|
||||
}
|
||||
|
||||
let start = aStart ? aStart : 0;
|
||||
let end = aEnd ? aEnd : Date.now();
|
||||
|
||||
if (start > end) {
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return { start: start, end: end };
|
||||
},
|
||||
|
||||
getStats: function(aStatsOptions, aStart, aEnd) {
|
||||
// Check time range.
|
||||
let { start: start, end: end } = this._checkTimeRange(aStart, aEnd);
|
||||
|
||||
// Create Promise.
|
||||
let self = this;
|
||||
return this.createPromiseWithId(function(aResolverId) {
|
||||
self.cpmm.sendAsyncMessage("ResourceStats:GetStats", {
|
||||
resolverId: aResolverId,
|
||||
type: self.type,
|
||||
statsOptions: aStatsOptions,
|
||||
manifestURL: self._manifestURL,
|
||||
start: start,
|
||||
end: end
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
clearStats: function(aStatsOptions, aStart, aEnd) {
|
||||
// Check time range.
|
||||
let {start: start, end: end} = this._checkTimeRange(aStart, aEnd);
|
||||
|
||||
// Create Promise.
|
||||
let self = this;
|
||||
return this.createPromiseWithId(function(aResolverId) {
|
||||
self.cpmm.sendAsyncMessage("ResourceStats:ClearStats", {
|
||||
resolverId: aResolverId,
|
||||
type: self.type,
|
||||
statsOptions: aStatsOptions,
|
||||
manifestURL: self._manifestURL,
|
||||
start: start,
|
||||
end: end
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
clearAllStats: function() {
|
||||
// Create Promise.
|
||||
let self = this;
|
||||
return this.createPromiseWithId(function(aResolverId) {
|
||||
self.cpmm.sendAsyncMessage("ResourceStats:ClearAllStats", {
|
||||
resolverId: aResolverId,
|
||||
type: self.type,
|
||||
manifestURL: self._manifestURL
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
addAlarm: function(aThreshold, aStatsOptions, aAlarmOptions) {
|
||||
if (DEBUG) {
|
||||
debug("aStatsOptions: " + JSON.stringify(aAlarmOptions));
|
||||
debug("aAlarmOptions: " + JSON.stringify(aAlarmOptions));
|
||||
}
|
||||
|
||||
// Parse alarm options.
|
||||
let startTime = aAlarmOptions.startTime || 0;
|
||||
|
||||
// Clone data object using structured clone algorithm.
|
||||
let data = null;
|
||||
if (aAlarmOptions.data) {
|
||||
data = Cu.cloneInto(aAlarmOptions.data, this._window);
|
||||
}
|
||||
|
||||
// Create Promise.
|
||||
let self = this;
|
||||
return this.createPromiseWithId(function(aResolverId) {
|
||||
self.cpmm.sendAsyncMessage("ResourceStats:AddAlarm", {
|
||||
resolverId: aResolverId,
|
||||
type: self.type,
|
||||
threshold: aThreshold,
|
||||
statsOptions: aStatsOptions,
|
||||
manifestURL: self._manifestURL,
|
||||
startTime: startTime,
|
||||
data: data
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
getAlarms: function(aStatsOptions) {
|
||||
// Create Promise.
|
||||
let self = this;
|
||||
return this.createPromiseWithId(function(aResolverId) {
|
||||
self.cpmm.sendAsyncMessage("ResourceStats:GetAlarms", {
|
||||
resolverId: aResolverId,
|
||||
type: self.type,
|
||||
statsOptions: aStatsOptions,
|
||||
manifestURL: self._manifestURL
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
removeAlarm: function(aAlarmId) {
|
||||
// Create Promise.
|
||||
let self = this;
|
||||
return this.createPromiseWithId(function(aResolverId) {
|
||||
self.cpmm.sendAsyncMessage("ResourceStats:RemoveAlarm", {
|
||||
resolverId: aResolverId,
|
||||
type: self.type,
|
||||
manifestURL: self._manifestURL,
|
||||
alarmId: aAlarmId
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
removeAllAlarms: function() {
|
||||
// Create Promise.
|
||||
let self = this;
|
||||
return this.createPromiseWithId(function(aResolverId) {
|
||||
self.cpmm.sendAsyncMessage("ResourceStats:RemoveAllAlarms", {
|
||||
resolverId: aResolverId,
|
||||
type: self.type,
|
||||
manifestURL: self._manifestURL
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
getAvailableComponents: function() {
|
||||
// Create Promise.
|
||||
let self = this;
|
||||
return this.createPromiseWithId(function(aResolverId) {
|
||||
self.cpmm.sendAsyncMessage("ResourceStats:GetComponents", {
|
||||
resolverId: aResolverId,
|
||||
type: self.type,
|
||||
manifestURL: self._manifestURL
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
get resourceTypes() {
|
||||
let types = new this._window.Array();
|
||||
resourceTypeList.forEach(function(aType) {
|
||||
types.push(aType);
|
||||
});
|
||||
|
||||
return types;
|
||||
},
|
||||
|
||||
get sampleRate() {
|
||||
let msg = { manifestURL: this._manifestURL };
|
||||
|
||||
return this.cpmm.sendSyncMessage("ResourceStats:SampleRate", msg)[0];
|
||||
},
|
||||
|
||||
get maxStorageAge() {
|
||||
let msg = { manifestURL: this._manifestURL };
|
||||
|
||||
return this.cpmm.sendSyncMessage("ResourceStats:MaxStorageAge", msg)[0];
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
let data = aMessage.data;
|
||||
let chromeObj = null;
|
||||
let webidlObj = null;
|
||||
let self = this;
|
||||
|
||||
if (DEBUG) {
|
||||
debug("receiveMessage(): " + aMessage.name + " " + data.resolverId);
|
||||
}
|
||||
|
||||
let resolver = this.takePromiseResolver(data.resolverId);
|
||||
if (!resolver) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "ResourceStats:GetStats:Resolved":
|
||||
if (DEBUG) {
|
||||
debug("data.value = " + JSON.stringify(data.value));
|
||||
}
|
||||
|
||||
chromeObj = new ResourceStats(this._window, data.value);
|
||||
webidlObj = this._window.ResourceStats._create(this._window, chromeObj);
|
||||
resolver.resolve(webidlObj);
|
||||
break;
|
||||
|
||||
case "ResourceStats:AddAlarm:Resolved":
|
||||
if (DEBUG) {
|
||||
debug("data.value = " + JSON.stringify(data.value));
|
||||
}
|
||||
|
||||
resolver.resolve(data.value); // data.value is alarmId.
|
||||
break;
|
||||
|
||||
case "ResourceStats:GetAlarms:Resolved":
|
||||
if (DEBUG) {
|
||||
debug("data.value = " + JSON.stringify(data.value));
|
||||
}
|
||||
|
||||
let alarmArray = this._window.Array();
|
||||
data.value.forEach(function(aAlarm) {
|
||||
chromeObj = new ResourceStatsAlarm(self._window, aAlarm);
|
||||
webidlObj = self._window.ResourceStatsAlarm._create(self._window,
|
||||
chromeObj);
|
||||
alarmArray.push(webidlObj);
|
||||
});
|
||||
resolver.resolve(alarmArray);
|
||||
break;
|
||||
|
||||
case "ResourceStats:GetComponents:Resolved":
|
||||
if (DEBUG) {
|
||||
debug("data.value = " + JSON.stringify(data.value));
|
||||
}
|
||||
|
||||
let components = this._window.Array();
|
||||
data.value.forEach(function(aComponent) {
|
||||
components.push(aComponent);
|
||||
});
|
||||
resolver.resolve(components);
|
||||
break;
|
||||
|
||||
case "ResourceStats:ClearStats:Resolved":
|
||||
case "ResourceStats:ClearAllStats:Resolved":
|
||||
case "ResourceStats:RemoveAlarm:Resolved":
|
||||
case "ResourceStats:RemoveAllAlarms:Resolved":
|
||||
if (DEBUG) {
|
||||
debug("data.value = " + JSON.stringify(data.value));
|
||||
}
|
||||
|
||||
resolver.resolve(data.value);
|
||||
break;
|
||||
|
||||
case "ResourceStats:GetStats:Rejected":
|
||||
case "ResourceStats:ClearStats:Rejected":
|
||||
case "ResourceStats:ClearAllStats:Rejected":
|
||||
case "ResourceStats:AddAlarm:Rejected":
|
||||
case "ResourceStats:GetAlarms:Rejected":
|
||||
case "ResourceStats:RemoveAlarm:Rejected":
|
||||
case "ResourceStats:RemoveAllAlarms:Rejected":
|
||||
case "ResourceStats:GetComponents:Rejected":
|
||||
if (DEBUG) {
|
||||
debug("data.reason = " + JSON.stringify(data.reason));
|
||||
}
|
||||
|
||||
resolver.reject(data.reason);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (DEBUG) {
|
||||
debug("Could not find a handler for " + aMessage.name);
|
||||
}
|
||||
resolver.reject();
|
||||
}
|
||||
},
|
||||
|
||||
__init: function(aType) {
|
||||
if (resourceTypeList.indexOf(aType) < 0) {
|
||||
if (DEBUG) {
|
||||
debug("Do not support resource statistics for " + aType);
|
||||
}
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
debug("Create " + aType + "Mgr");
|
||||
}
|
||||
this.type = aType;
|
||||
}
|
||||
},
|
||||
|
||||
init: function(aWindow) {
|
||||
if (DEBUG) {
|
||||
debug("init()");
|
||||
}
|
||||
|
||||
// Get the manifestURL if this is an installed app
|
||||
let principal = aWindow.document.nodePrincipal;
|
||||
let appsService = Cc["@mozilla.org/AppsService;1"]
|
||||
.getService(Ci.nsIAppsService);
|
||||
this._manifestURL = appsService.getManifestURLByLocalId(principal.appId);
|
||||
|
||||
const messages = ["ResourceStats:GetStats:Resolved",
|
||||
"ResourceStats:ClearStats:Resolved",
|
||||
"ResourceStats:ClearAllStats:Resolved",
|
||||
"ResourceStats:AddAlarm:Resolved",
|
||||
"ResourceStats:GetAlarms:Resolved",
|
||||
"ResourceStats:RemoveAlarm:Resolved",
|
||||
"ResourceStats:RemoveAllAlarms:Resolved",
|
||||
"ResourceStats:GetComponents:Resolved",
|
||||
"ResourceStats:GetStats:Rejected",
|
||||
"ResourceStats:ClearStats:Rejected",
|
||||
"ResourceStats:ClearAllStats:Rejected",
|
||||
"ResourceStats:AddAlarm:Rejected",
|
||||
"ResourceStats:GetAlarms:Rejected",
|
||||
"ResourceStats:RemoveAlarm:Rejected",
|
||||
"ResourceStats:RemoveAllAlarms:Rejected",
|
||||
"ResourceStats:GetComponents:Rejected"];
|
||||
this.initDOMRequestHelper(aWindow, messages);
|
||||
|
||||
this.cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
|
||||
.getService(Ci.nsISyncMessageSender);
|
||||
},
|
||||
|
||||
classID: Components.ID("{101ed1f8-31b3-491c-95ea-04091e6e8027}"),
|
||||
contractID: "@mozilla.org/resourceStatsManager;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsIObserver])
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsData,
|
||||
PowerStatsData,
|
||||
ResourceStats,
|
||||
ResourceStatsAlarm,
|
||||
ResourceStatsManager]);
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
component {dce5729a-ba92-4185-8854-e29e71b9e8a2} ResourceStatsManager.js
|
||||
contract @mozilla.org/networkStatsData;1 {dce5729a-ba92-4185-8854-e29e71b9e8a2}
|
||||
|
||||
component {acb9af6c-8143-4e59-bc18-4bb1736a4004} ResourceStatsManager.js
|
||||
contract @mozilla.org/powerStatsData;1 {acb9af6c-8143-4e59-bc18-4bb1736a4004}
|
||||
|
||||
component {b7c970f2-3d58-4966-9633-2024feb5132b} ResourceStatsManager.js
|
||||
contract @mozilla.org/resourceStats;1 {b7c970f2-3d58-4966-9633-2024feb5132b}
|
||||
|
||||
component {e2b66e7a-0ff1-4015-8690-a2a3f6a5b63a} ResourceStatsManager.js
|
||||
contract @mozilla.org/resourceStatsAlarm;1 {e2b66e7a-0ff1-4015-8690-a2a3f6a5b63a}
|
||||
|
||||
component {101ed1f8-31b3-491c-95ea-04091e6e8027} ResourceStatsManager.js
|
||||
contract @mozilla.org/resourceStatsManager;1 {101ed1f8-31b3-491c-95ea-04091e6e8027}
|
|
@ -1,334 +0,0 @@
|
|||
/* 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/.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["ResourceStatsService"];
|
||||
|
||||
const DEBUG = false;
|
||||
function debug(s) { dump("-*- ResourceStatsService: " + s + "\n"); }
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
// Load ResourceStatsDB.
|
||||
Cu.import("resource://gre/modules/ResourceStatsDB.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gIDBManager",
|
||||
"@mozilla.org/dom/indexeddb/manager;1",
|
||||
"nsIIndexedDatabaseManager");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||
"@mozilla.org/parentprocessmessagemanager;1",
|
||||
"nsIMessageListenerManager");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "appsService",
|
||||
"@mozilla.org/AppsService;1",
|
||||
"nsIAppsService");
|
||||
|
||||
this.ResourceStatsService = {
|
||||
|
||||
init: function() {
|
||||
if (DEBUG) {
|
||||
debug("Service started");
|
||||
}
|
||||
|
||||
// Set notification to observe.
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
|
||||
// Add message listener.
|
||||
this.messages = ["ResourceStats:GetStats",
|
||||
"ResourceStats:ClearStats",
|
||||
"ResourceStats:ClearAllStats",
|
||||
"ResourceStats:AddAlarm",
|
||||
"ResourceStats:GetAlarms",
|
||||
"ResourceStats:RemoveAlarm",
|
||||
"ResourceStats:RemoveAllAlarms",
|
||||
"ResourceStats:GetComponents",
|
||||
"ResourceStats:SampleRate",
|
||||
"ResourceStats:MaxStorageAge"];
|
||||
|
||||
this.messages.forEach(function(aMessageName){
|
||||
ppmm.addMessageListener(aMessageName, this);
|
||||
}, this);
|
||||
|
||||
// Create indexedDB.
|
||||
this._db = new ResourceStatsDB();
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (DEBUG) {
|
||||
debug("receiveMessage(): " + aMessage.name);
|
||||
}
|
||||
|
||||
let mm = aMessage.target;
|
||||
let data = aMessage.data;
|
||||
|
||||
if (DEBUG) {
|
||||
debug("received aMessage.data = " + JSON.stringify(data));
|
||||
}
|
||||
|
||||
// To prevent the hacked child process from sending commands to parent,
|
||||
// we need to check its permission and manifest URL.
|
||||
if (!aMessage.target.assertPermission("resourcestats-manage")) {
|
||||
return;
|
||||
}
|
||||
if (!aMessage.target.assertContainApp(data.manifestURL)) {
|
||||
if (DEBUG) {
|
||||
debug("Got msg from a child process containing illegal manifestURL.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "ResourceStats:GetStats":
|
||||
this.getStats(mm, data);
|
||||
break;
|
||||
case "ResourceStats:ClearStats":
|
||||
this.clearStats(mm, data);
|
||||
break;
|
||||
case "ResourceStats:ClearAllStats":
|
||||
this.clearAllStats(mm, data);
|
||||
break;
|
||||
case "ResourceStats:AddAlarm":
|
||||
this.addAlarm(mm, data);
|
||||
break;
|
||||
case "ResourceStats:GetAlarms":
|
||||
this.getAlarms(mm, data);
|
||||
break;
|
||||
case "ResourceStats:RemoveAlarm":
|
||||
this.removeAlarm(mm, data);
|
||||
break;
|
||||
case "ResourceStats:RemoveAllAlarms":
|
||||
this.removeAllAlarms(mm, data);
|
||||
break;
|
||||
case "ResourceStats:GetComponents":
|
||||
this.getComponents(mm, data);
|
||||
break;
|
||||
case "ResourceStats:SampleRate":
|
||||
// This message is sync.
|
||||
return this._db.sampleRate;
|
||||
case "ResourceStats:MaxStorageAge":
|
||||
// This message is sync.
|
||||
return this._db.maxStorageAge;
|
||||
}
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "xpcom-shutdown":
|
||||
if (DEBUG) {
|
||||
debug("Service shutdown " + aData);
|
||||
}
|
||||
|
||||
this.messages.forEach(function(aMessageName) {
|
||||
ppmm.removeMessageListener(aMessageName, this);
|
||||
}, this);
|
||||
|
||||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
// Closure generates callback function for DB request.
|
||||
_createDbCallback: function(aMm, aId, aMessage) {
|
||||
let resolveMsg = aMessage + ":Resolved";
|
||||
let rejectMsg = aMessage + ":Rejected";
|
||||
|
||||
return function(aError, aResult) {
|
||||
if (aError) {
|
||||
aMm.sendAsyncMessage(rejectMsg, { resolverId: aId, reason: aError });
|
||||
return;
|
||||
}
|
||||
aMm.sendAsyncMessage(resolveMsg, { resolverId: aId, value: aResult });
|
||||
};
|
||||
},
|
||||
|
||||
getStats: function(aMm, aData) {
|
||||
if (DEBUG) {
|
||||
debug("getStats(): " + JSON.stringify(aData));
|
||||
}
|
||||
|
||||
// Note: we validate the manifestURL in _db.getStats().
|
||||
let manifestURL = aData.statsOptions.manifestURL || "";
|
||||
let serviceType = aData.statsOptions.serviceType || "";
|
||||
let component = aData.statsOptions.component || "";
|
||||
|
||||
// Execute DB operation.
|
||||
let onStatsGot = this._createDbCallback(aMm, aData.resolverId,
|
||||
"ResourceStats:GetStats");
|
||||
this._db.getStats(aData.type, manifestURL, serviceType, component,
|
||||
aData.start, aData.end, onStatsGot);
|
||||
},
|
||||
|
||||
clearStats: function(aMm, aData) {
|
||||
if (DEBUG) {
|
||||
debug("clearStats(): " + JSON.stringify(aData));
|
||||
}
|
||||
|
||||
// Get appId and check whether manifestURL is a valid app.
|
||||
let appId = 0;
|
||||
let manifestURL = aData.statsOptions.manifestURL || "";
|
||||
if (manifestURL) {
|
||||
appId = appsService.getAppLocalIdByManifestURL(manifestURL);
|
||||
|
||||
if (!appId) {
|
||||
aMm.sendAsyncMessage("ResourceStats:GetStats:Rejected", {
|
||||
resolverId: aData.resolverId,
|
||||
reason: "Invalid manifestURL"
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let serviceType = aData.statsOptions.serviceType || "";
|
||||
let component = aData.statsOptions.component || "";
|
||||
|
||||
// Execute DB operation.
|
||||
let onStatsCleared = this._createDbCallback(aMm, aData.resolverId,
|
||||
"ResourceStats:ClearStats");
|
||||
this._db.clearStats(aData.type, appId, serviceType, component,
|
||||
aData.start, aData.end, onStatsCleared);
|
||||
},
|
||||
|
||||
clearAllStats: function(aMm, aData) {
|
||||
if (DEBUG) {
|
||||
debug("clearAllStats(): " + JSON.stringify(aData));
|
||||
}
|
||||
|
||||
// Execute DB operation.
|
||||
let onAllStatsCleared = this._createDbCallback(aMm, aData.resolverId,
|
||||
"ResourceStats:ClearAllStats");
|
||||
this._db.clearAllStats(aData.type, onAllStatsCleared);
|
||||
},
|
||||
|
||||
addAlarm: function(aMm, aData) {
|
||||
if (DEBUG) {
|
||||
debug("addAlarm(): " + JSON.stringify(aData));
|
||||
}
|
||||
|
||||
// Get appId and check whether manifestURL is a valid app.
|
||||
let manifestURL = aData.statsOptions.manifestURL;
|
||||
if (manifestURL) {
|
||||
let appId = appsService.getAppLocalIdByManifestURL(manifestURL);
|
||||
|
||||
if (!appId) {
|
||||
aMm.sendAsyncMessage("ResourceStats:GetStats:Rejected", {
|
||||
resolverId: aData.resolverId,
|
||||
reason: "Invalid manifestURL"
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Create an alarm object.
|
||||
let newAlarm = {
|
||||
type: aData.type,
|
||||
component: aData.statsOptions.component || "",
|
||||
serviceType: aData.statsOptions.serviceType || "",
|
||||
manifestURL: manifestURL || "",
|
||||
threshold: aData.threshold,
|
||||
startTime: aData.startTime,
|
||||
data: aData.data
|
||||
};
|
||||
|
||||
// Execute DB operation.
|
||||
let onAlarmAdded = this._createDbCallback(aMm, aData.resolverId,
|
||||
"ResourceStats:AddAlarm");
|
||||
this._db.addAlarm(newAlarm, onAlarmAdded);
|
||||
},
|
||||
|
||||
getAlarms: function(aMm, aData) {
|
||||
if (DEBUG) {
|
||||
debug("getAlarms(): " + JSON.stringify(aData));
|
||||
}
|
||||
|
||||
let options = null;
|
||||
let statsOptions = aData.statsOptions;
|
||||
// If all keys in statsOptions are set to null, treat this call as quering
|
||||
// all alarms; otherwise, resolve the statsOptions and perform DB query
|
||||
// according to the resolved result.
|
||||
if (statsOptions.manifestURL || statsOptions.serviceType ||
|
||||
statsOptions.component) {
|
||||
// Validate manifestURL.
|
||||
let manifestURL = statsOptions.manifestURL || "";
|
||||
if (manifestURL) {
|
||||
let appId = appsService.getAppLocalIdByManifestURL(manifestURL);
|
||||
|
||||
if (!appId) {
|
||||
aMm.sendAsyncMessage("ResourceStats:GetStats:Rejected", {
|
||||
resolverId: aData.resolverId,
|
||||
reason: "Invalid manifestURL"
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
options = {
|
||||
manifestURL: manifestURL,
|
||||
serviceType: statsOptions.serviceType || "",
|
||||
component: statsOptions.component || ""
|
||||
};
|
||||
}
|
||||
|
||||
// Execute DB operation.
|
||||
let onAlarmsGot = this._createDbCallback(aMm, aData.resolverId,
|
||||
"ResourceStats:GetAlarms");
|
||||
this._db.getAlarms(aData.type, options, onAlarmsGot);
|
||||
},
|
||||
|
||||
removeAlarm: function(aMm, aData) {
|
||||
if (DEBUG) {
|
||||
debug("removeAlarm(): " + JSON.stringify(aData));
|
||||
}
|
||||
|
||||
// Execute DB operation.
|
||||
let onAlarmRemoved = function(aError, aResult) {
|
||||
if (aError) {
|
||||
aMm.sendAsyncMessage("ResourceStats:RemoveAlarm:Rejected",
|
||||
{ resolverId: aData.resolverId, reason: aError });
|
||||
}
|
||||
|
||||
if (!aResult) {
|
||||
aMm.sendAsyncMessage("ResourceStats:RemoveAlarm:Rejected",
|
||||
{ resolverId: aData.resolverId,
|
||||
reason: "alarm not existed" });
|
||||
}
|
||||
|
||||
aMm.sendAsyncMessage("ResourceStats:RemoveAlarm:Resolved",
|
||||
{ resolverId: aData.resolverId, value: aResult });
|
||||
};
|
||||
|
||||
this._db.removeAlarm(aData.type, aData.alarmId, onAlarmRemoved);
|
||||
},
|
||||
|
||||
removeAllAlarms: function(aMm, aData) {
|
||||
if (DEBUG) {
|
||||
debug("removeAllAlarms(): " + JSON.stringify(aData));
|
||||
}
|
||||
|
||||
// Execute DB operation.
|
||||
let onAllAlarmsRemoved = this._createDbCallback(aMm, aData.resolverId,
|
||||
"ResourceStats:RemoveAllAlarms");
|
||||
this._db.removeAllAlarms(aData.type, onAllAlarmsRemoved);
|
||||
},
|
||||
|
||||
getComponents: function(aMm, aData) {
|
||||
if (DEBUG) {
|
||||
debug("getComponents(): " + JSON.stringify(aData));
|
||||
}
|
||||
|
||||
// Execute DB operation.
|
||||
let onComponentsGot = this._createDbCallback(aMm, aData.resolverId,
|
||||
"ResourceStats:GetComponents");
|
||||
this._db.getComponents(aData.type, onComponentsGot);
|
||||
},
|
||||
};
|
||||
|
||||
this.ResourceStatsService.init();
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'ResourceStatsManager.js',
|
||||
'ResourceStatsManager.manifest',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'ResourceStatsDB.jsm',
|
||||
'ResourceStatsService.jsm',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
if CONFIG['MOZ_B2G_RIL']:
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
|
||||
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
|
|
@ -1,16 +0,0 @@
|
|||
[test_basic.html]
|
||||
skip-if = toolkit != "gonk"
|
||||
[test_network_stats.html]
|
||||
skip-if = toolkit != "gonk"
|
||||
[test_power_stats.html]
|
||||
skip-if = toolkit != "gonk"
|
||||
[test_network_alarm.html]
|
||||
skip-if = toolkit != "gonk"
|
||||
[test_power_alarm.html]
|
||||
skip-if = toolkit != "gonk"
|
||||
[test_disabled_pref.html]
|
||||
skip-if = toolkit != "gonk"
|
||||
[test_no_perm.html]
|
||||
skip-if = toolkit != "gonk"
|
||||
[test_not_supported_type.html]
|
||||
skip-if = toolkit != "gonk"
|
|
@ -1,41 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test accessibility of interfaces</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Test accessibility of interfaces.
|
||||
SpecialPowers.addPermission("resourcestats-manage", true, document);
|
||||
SpecialPowers.pushPrefEnv({ 'set': [
|
||||
["dom.resource_stats.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true]
|
||||
]}, function() {
|
||||
ok(SpecialPowers.hasPermission("resourcestats-manage", document),
|
||||
"Has permission 'resourcestats-manage'.");
|
||||
ok(SpecialPowers.getBoolPref("dom.resource_stats.enabled"),
|
||||
"Preference 'dom.resource_stats.enabled' is true.");
|
||||
|
||||
// Check all interfaces are accessible.
|
||||
ok('ResourceStatsManager' in window, "ResourceStatsManager exist.");
|
||||
ok('ResourceStatsAlarm' in window, "ResourceStatsAlarm exist.");
|
||||
ok('ResourceStats' in window, "ResourceStats exist.");
|
||||
ok('NetworkStatsData' in window, "NetworkStatsData exist.");
|
||||
ok('PowerStatsData' in window, "PowerStatsData exist.");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,41 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test to ensure interface is not accessible when preference is disabled</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Test to ensure interface is not accessible when preference is disabled.
|
||||
SpecialPowers.addPermission("resourcestats-manage", true, document);
|
||||
SpecialPowers.pushPrefEnv({ 'set': [
|
||||
["dom.resource_stats.enabled", false],
|
||||
["dom.ignore_webidl_scope_checks", true]
|
||||
]}, function() {
|
||||
ok(SpecialPowers.hasPermission("resourcestats-manage", document),
|
||||
"Has permission 'resourcestats-manage'.");
|
||||
ok(!(SpecialPowers.getBoolPref("dom.resource_stats.enabled")),
|
||||
"Preference 'dom.resource_stats.enabled' is false.");
|
||||
|
||||
// Check accessibility.
|
||||
is('ResourceStatsManager' in window, false, "ResourceStatsManager should not exist.");
|
||||
is('ResourceStatsAlarm' in window, false, "ResourceStatsAlarm should not exist.");
|
||||
is('ResourceStats' in window, false, "ResourceStats should not exist.");
|
||||
is('NetworkStatsData' in window, false, "NetworkStatsData should not exist.");
|
||||
is('PowerStatsData' in window, false, "PowerStatsData should not exist.");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,356 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for ResourceStats methods realted to network resource control</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
const invalidManifestURL = "app://invalid.gaiamobile.org/manifest.webapp";
|
||||
const wifiComponent = "wifi:0";
|
||||
const mobileComponent = "mobile:1";
|
||||
var networkStatsMgr = null; // ResourceStatsManager for network statistics.
|
||||
|
||||
function errorCb(reason) {
|
||||
ok(false, reason);
|
||||
}
|
||||
|
||||
// Check the content returned by getAlarms.
|
||||
function checkAlarmsArray(alarms) {
|
||||
// Check if data is an array.
|
||||
if (!Array.isArray(alarms)) {
|
||||
throw "getAlarms does not return an array.";
|
||||
} else {
|
||||
ok(true, "getAlarms returns an array.")
|
||||
}
|
||||
|
||||
// Iterate the array and check the type of each element.
|
||||
var obj = null;
|
||||
var message = null; // Message for exception
|
||||
|
||||
for (var i = 0; i < alarms.length; i++) {
|
||||
obj = alarms[i];
|
||||
|
||||
// Check if obj is an instance os ResourceStatsAlarm.
|
||||
if (!(obj instanceof ResourceStatsAlarm)) {
|
||||
message = "The array contains a non-ResourceStatsAlarm object.";
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if obj.type is network.
|
||||
if (obj.type != "network") {
|
||||
message = "The type of a ResourceStatsAlarm object is not network.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (message) {
|
||||
throw message;
|
||||
}
|
||||
|
||||
ok(true, "The return is an array of ResourceStatsAlarm objects.");
|
||||
}
|
||||
|
||||
// Test Cases for testing WebIDL methods related to resource control.
|
||||
var testCases = [
|
||||
function() {
|
||||
// Test removeAllAlarms.
|
||||
var promise = networkStatsMgr.removeAllAlarms();
|
||||
promise.then(function() {
|
||||
ok(true, "removeAllAlarms deleted all network alarms.");
|
||||
testMethods();
|
||||
}, function() {
|
||||
ok(false, "removeAllAlarms failed to delete network alarms.");
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test addAlarm.
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
var promise = networkStatsMgr.addAlarm(threshold,
|
||||
{ 'component': wifiComponent },
|
||||
{ 'startTime': Date.now() });
|
||||
promise.then(function(value) {
|
||||
// Check the value (alarmId).
|
||||
if (value < 0) {
|
||||
ok(false, "addAlarm failed to create an alarm.");
|
||||
} else {
|
||||
ok(true, "addAlarm created an alarm.");
|
||||
testMethods();
|
||||
}
|
||||
}, function() {
|
||||
ok(false, "addAlarm failed to create an alarm.");
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test addAlarm with negative threshold.
|
||||
var threshold = -1;
|
||||
var promise = networkStatsMgr.addAlarm(threshold,
|
||||
{ 'component': wifiComponent },
|
||||
{ 'startTime': Date.now() });
|
||||
promise.then(function() {
|
||||
// Check the value.
|
||||
ok(false,
|
||||
"addAlarm did not throw an exception when negative threshold is set.");
|
||||
}, function() {
|
||||
ok(true, "addAlarm threw an exception when negative threshold is set.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test addAlarm with no threshold.
|
||||
var promise = networkStatsMgr.addAlarm();
|
||||
promise.then(function() {
|
||||
// Check the value.
|
||||
ok(false, "addAlarm did not throw an exception when no threshold.");
|
||||
}, function() {
|
||||
ok(true, "addAlarm threw an exception when no threshold.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test addAlarm with negative startTime.
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
var promise = networkStatsMgr.addAlarm(threshold,
|
||||
{ 'component': wifiComponent },
|
||||
{ 'startTime': -1 });
|
||||
promise.then(function() {
|
||||
// Check the value.
|
||||
ok(false,
|
||||
"addAlarm did not throw an exception when negative startTime is set.");
|
||||
}, function() {
|
||||
ok(true, "addAlarm threw an exception when negative startTime is set.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test addAlarm when manifestURL is invalid.
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
var promise = networkStatsMgr.addAlarm(threshold,
|
||||
{ 'component': wifiComponent,
|
||||
'manifestURL': invalidManifestURL },
|
||||
{ 'startTime': Date.now() });
|
||||
promise.then(function() {
|
||||
// Check the value.
|
||||
ok(false, "addAlarm did not throw an exception when manifestURL is invalid.");
|
||||
}, function() {
|
||||
ok(true, "addAlarm threw an exception when manifestURL is invalid.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test getAlarms.
|
||||
var alarmId;
|
||||
var alarms;
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
|
||||
// Execution steps:
|
||||
// 1. Add a new alarm.
|
||||
var promise = networkStatsMgr.addAlarm(threshold,
|
||||
{ 'component': wifiComponent },
|
||||
{ 'startTime': Date.now() });
|
||||
|
||||
// 2. Test getAlarms if new alarm is added.
|
||||
var runGetAlarms = function(value) {
|
||||
alarmId = value;
|
||||
return networkStatsMgr.getAlarms({ 'component': wifiComponent });
|
||||
};
|
||||
|
||||
// 3. Check the content returned by getAlarms.
|
||||
var checkGetAlarmsReturn = function(value) {
|
||||
alarms = value;
|
||||
checkAlarmsArray(value);
|
||||
};
|
||||
|
||||
// 4. Check the alarm added in step 1 is inside the return of getAlarms.
|
||||
var checkAlarm = function (value) {
|
||||
// Find the alarm.
|
||||
var index = alarms.map(function(e) { return e.alarmId; })
|
||||
.indexOf(alarmId);
|
||||
if (index < 0) {
|
||||
throw "getAlarms does not get the alarm added in previous step.";
|
||||
}
|
||||
var alarm = alarms[index];
|
||||
|
||||
// Evaluate the alarm.
|
||||
ok(alarm.threshold == threshold, "threshold is equal.");
|
||||
ok(alarm.component == wifiComponent, "component is equal.");
|
||||
ok(alarm.serviceType == null, "serviceType should be null.");
|
||||
ok(alarm.manifestURL == null, "manifestURL should be null.");
|
||||
};
|
||||
|
||||
// Create promise chaining.
|
||||
promise.then(runGetAlarms)
|
||||
.then(checkGetAlarmsReturn)
|
||||
.then(checkAlarm)
|
||||
.then(testMethods, errorCb); // Execute next test case.
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test getAlarms with invalid manifestURL.
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
var promise = networkStatsMgr.addAlarm(threshold,
|
||||
{ 'component': wifiComponent,
|
||||
'manifestURL': invalidManifestURL },
|
||||
{ 'startTime': Date.now() });
|
||||
|
||||
promise.then(function() {
|
||||
// Check the value.
|
||||
ok(false, "getAlarms did not throw an exception when manifestURL is invalid.");
|
||||
}, function() {
|
||||
ok(true, "getAlarms threw an exception when manifestURL is invalid.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test getAlarms with incorrect parameter.
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
|
||||
// Execution steps:
|
||||
// 1. Add a new alarm.
|
||||
var promise = networkStatsMgr.addAlarm(threshold,
|
||||
{ 'component': wifiComponent },
|
||||
{ 'startTime': Date.now() });
|
||||
|
||||
// 2. Call getAlarms with incorrect parameter.
|
||||
var runGetAlarms = function() {
|
||||
return networkStatsMgr.getAlarms({ 'component': mobileComponent });
|
||||
};
|
||||
|
||||
// 3. check the content returned by getAlarms.
|
||||
var checkGetAlarmsReturn = function(value) {
|
||||
// Check array length
|
||||
if (value.length) {
|
||||
throw "getAlarms gets an alarm when using incorrect parameter.";
|
||||
} else {
|
||||
ok(true,
|
||||
"getAlarms returns an empty array when using incorrect parameter.");
|
||||
}
|
||||
};
|
||||
|
||||
// Create pomise chaining.
|
||||
promise.then(runGetAlarms)
|
||||
.then(checkGetAlarmsReturn)
|
||||
.then(testMethods, errorCb); // Execute next test case.
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test removeAlarm
|
||||
var alarmId;
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
|
||||
// Execution steps:
|
||||
// 1. Add a new alarm.
|
||||
var promise = networkStatsMgr.addAlarm(threshold,
|
||||
{ 'component': wifiComponent },
|
||||
{ 'startTime': Date.now() });
|
||||
|
||||
// 2. Try to remove the new alarm.
|
||||
var runRemoveAlarm = function(value) {
|
||||
alarmId = value;
|
||||
return networkStatsMgr.removeAlarm(alarmId);
|
||||
}
|
||||
|
||||
// Create promise chaining.
|
||||
promise.then(runRemoveAlarm)
|
||||
.then(function() {
|
||||
ok(true, "removeAlarm deleted the alarm.");
|
||||
testMethods();
|
||||
}, errorCb);
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test removeAlarm with negative alarmId
|
||||
var alarmId = -1;
|
||||
var promise = networkStatsMgr.removeAlarm(alarmId);
|
||||
promise.then(function() {
|
||||
ok(false,
|
||||
"removeAlarm did not throw an exception when negative alarmId is set.");
|
||||
}, function() {
|
||||
ok(true, "removeAlarm threw an exception when negative alarmId is set.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test removeAlarm with invalid alarmId
|
||||
var alarmId;
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
|
||||
// Execution steps:
|
||||
// 1. Add a new alarm.
|
||||
var promise = networkStatsMgr.addAlarm(threshold,
|
||||
{ 'component': wifiComponent },
|
||||
{ 'startTime': Date.now() });
|
||||
|
||||
// 2. Try to remove an invalid alarm.
|
||||
var runRemoveAlarm = function(value) {
|
||||
alarmId = value;
|
||||
// Because alarmId is auto-increment, any alarmId larger than the
|
||||
// latest alarmId should be invalid.
|
||||
return networkStatsMgr.removeAlarm(alarmId + 10);
|
||||
}
|
||||
|
||||
// Create promise chaining.
|
||||
promise.then(runRemoveAlarm)
|
||||
.then(function() {
|
||||
// Input with incorrect alarmId should not be resolved.
|
||||
throw "removeAlarm should fail with invalid alarmId.";
|
||||
}, function(reason) {
|
||||
if (reason == "alarm not existed") {
|
||||
ok(true, "removeAlarm with invalid alarmId should fail.")
|
||||
} else {
|
||||
throw reason;
|
||||
}
|
||||
})
|
||||
.then(testMethods, errorCb);
|
||||
}
|
||||
];
|
||||
|
||||
// Test WebIDL methods related stats operation.
|
||||
function testMethods() {
|
||||
if (!testCases.length) {
|
||||
ok(true, "Done.");
|
||||
SpecialPowers.removePermission("resourcestats-manage", document);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var testCase = testCases.shift();
|
||||
testCase();
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
// Create an instance of ResourceStatsManager for network.
|
||||
networkStatsMgr = new ResourceStatsManager("network");
|
||||
ok(networkStatsMgr, "Create networkStatsMgr.");
|
||||
|
||||
// Test WebIDL methods related to resource control.
|
||||
testMethods();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Enable permission and preference.
|
||||
SpecialPowers.addPermission("resourcestats-manage", true, document);
|
||||
SpecialPowers.pushPrefEnv({ 'set': [
|
||||
["dom.resource_stats.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true]
|
||||
]}, startTest);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,345 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for ResourceStats methods realted to network statistics</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
const invalidManifestURL = "app://invalid.gaiamobile.org/manifest.webapp";
|
||||
var networkStatsMgr = null; // ResourceStatsManager for network statistics.
|
||||
var sampleRate = 0;
|
||||
|
||||
// Test WebIDL attributes.
|
||||
function testAttributes() {
|
||||
// Test sampleRate.
|
||||
ok('sampleRate' in networkStatsMgr,
|
||||
"sampleRate should be a ResourceStatsManager attribute.");
|
||||
sampleRate = networkStatsMgr.sampleRate;
|
||||
ok(sampleRate > 0, "sampleRate is greater than 0.");
|
||||
|
||||
// Test maxStorageAge.
|
||||
ok('maxStorageAge' in networkStatsMgr,
|
||||
"maxStorageAge should be a ResourceStatsManager attribute.");
|
||||
ok(networkStatsMgr.maxStorageAge > 0,
|
||||
"maxStorageAge is greater than 0.");
|
||||
|
||||
// Test whether "network" in resourceTypes array.
|
||||
ok('resourceTypes' in networkStatsMgr,
|
||||
"resourceTypes should be a ResourceStatsManager attribute.");
|
||||
ok(Array.isArray(networkStatsMgr.resourceTypes),
|
||||
"networkStatsMgr.resourceTypes is an array.");
|
||||
ok(networkStatsMgr.resourceTypes.indexOf("network") > -1,
|
||||
"'network' is an element of networkStatsMgr.resourceTypes.");
|
||||
}
|
||||
|
||||
// Check the content returned by ResourceStats.getData().
|
||||
function checkData(data, start, end) {
|
||||
// Check if data is an array.
|
||||
if (!Array.isArray(data)) {
|
||||
ok(false, "getData does not return an array.")
|
||||
return;
|
||||
} else {
|
||||
ok(true, "getData returns an array.")
|
||||
}
|
||||
|
||||
// Iterate the array and check the timestamp and type of each element.
|
||||
var success = true;
|
||||
var obj = null;
|
||||
var timestamp = start;
|
||||
var i = 0;
|
||||
var length = data.length;
|
||||
|
||||
do {
|
||||
obj = data[i++];
|
||||
|
||||
// Check object type.
|
||||
if (!(obj instanceof NetworkStatsData)) {
|
||||
success = false;
|
||||
ok(false, "The array contains a non-NetworkStatsData object.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if the timestamp is continuous.
|
||||
if (obj.timestamp !== timestamp) {
|
||||
success = false;
|
||||
ok(false, "The timestamp of NetworkStatsData object is correct.");
|
||||
break;
|
||||
}
|
||||
|
||||
timestamp += sampleRate;
|
||||
} while (i < length);
|
||||
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the timestamp of the last element is equal to end.
|
||||
if (obj.timestamp != end) {
|
||||
ok(false,
|
||||
"The timestamp of the last element of the array is equal to end.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute next test case.
|
||||
ok(true, "The return of getData is an array of NetworkStatsData objects.");
|
||||
testMethods();
|
||||
}
|
||||
|
||||
// Test Cases for testing WebIDL methods.
|
||||
var testCases = [
|
||||
function() {
|
||||
// Test clearAllStats.
|
||||
var promise = networkStatsMgr.clearAllStats();
|
||||
promise.then(function() {
|
||||
ok(true, "clearAllStats clears the network store.");
|
||||
testMethods();
|
||||
}, function() {
|
||||
ok(false, "clearAllStats fails to clear the network store.");
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test clearStats.
|
||||
var promise = networkStatsMgr.clearStats();
|
||||
promise.then(function() {
|
||||
ok(true, "clearStats clears the network store.");
|
||||
testMethods();
|
||||
}, function() {
|
||||
ok(false, "clearStats fails to clear the network store.");
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Check if clearStats throw exception when start is great than end.
|
||||
var end = Date.now();
|
||||
var start = end + 1000;
|
||||
var promise = networkStatsMgr.clearStats(null, start, end);
|
||||
promise.then(function() {
|
||||
ok(false,
|
||||
"clearStats does not throw exception when start is great than end.");
|
||||
}, function() {
|
||||
ok(true, "clearStats throw exception when start is great than end.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Check if clearStats throw exception when start is less than 0.
|
||||
var end = Date.now();
|
||||
var start = -1;
|
||||
var promise = networkStatsMgr.clearStats(null, start, end);
|
||||
promise.then(function() {
|
||||
ok(false,
|
||||
"clearStats dose not throw exception when start is less than 0.");
|
||||
}, function() {
|
||||
ok(true, "clearStats throw exception when start is less than 0.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Check if clearStats throw exception when manifestURL is invalid.
|
||||
var options = {manifestURL: invalidManifestURL};
|
||||
var promise = networkStatsMgr.clearStats(options);
|
||||
promise.then(function() {
|
||||
ok(false,
|
||||
"clearStats does not throw exception when manifestURL is invalid.");
|
||||
}, function() {
|
||||
ok(true, "clearStats throw exception when manifestURL is invalid.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test getAvailableComponents.
|
||||
var promise = networkStatsMgr.getAvailableComponents();
|
||||
promise.then(function(value) {
|
||||
if (Array.isArray(value)) {
|
||||
ok(true, "getAvailableComponents returns an array.");
|
||||
testMethods();
|
||||
} else {
|
||||
ok(false, "getAvailableComponents does not return an array.");
|
||||
}
|
||||
}, function() {
|
||||
ok(false, "Fail to execute getAvailableComponents.");
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test getStats.
|
||||
ok(true, "Get system stats when start and end are adapted to sampleRate.");
|
||||
|
||||
// Prepare start and end.
|
||||
var offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||
var end = Math.floor((Date.now() - offset) / sampleRate) * sampleRate + offset;
|
||||
var start = end - sampleRate * 10;
|
||||
|
||||
// Launch request.
|
||||
var promise = networkStatsMgr.getStats(null, start, end);
|
||||
promise.then(function(value) {
|
||||
// Check the object type.
|
||||
if (value instanceof ResourceStats) {
|
||||
ok(true, "Get a ResourceStats object.");
|
||||
} else {
|
||||
ok(false, "Fail to get a ResourceStats object.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check attributes of ResourceStats.
|
||||
ok(value.type == "network", "type should be network.");
|
||||
ok(value.component == null, "component should be null.");
|
||||
ok(value.serviceType == null, "serviceType should be null.");
|
||||
ok(value.manifestURL == null, "manifestURL should be null.");
|
||||
|
||||
// Check if the time range of ResourceStats is equal to the request.
|
||||
ok(value.start == start, "start timestamp should be equal.");
|
||||
ok(value.end == end, "end timestamp should be equal.");
|
||||
|
||||
// Check stats stored inside ResourceStats.
|
||||
if ('getData' in value) {
|
||||
checkData(value.getData(), start, end);
|
||||
} else {
|
||||
ok(false, "getData is not a method of ResourceStats.");
|
||||
return;
|
||||
}
|
||||
}, function() {
|
||||
ok(false, "Get network stats failed.");
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test getStats when start and end are not adapted to sampleRate.
|
||||
ok(true,
|
||||
"Get system stats when start and end are not adapted to sampleRate.");
|
||||
|
||||
// Prepare start and end.
|
||||
var end = Date.now();
|
||||
var start = end - sampleRate * 10;
|
||||
|
||||
// Normalize start and end.
|
||||
var offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||
var normEnd = Math.floor((end - offset) / sampleRate)
|
||||
* sampleRate + offset;
|
||||
var normStart = Math.floor((start - offset) / sampleRate)
|
||||
* sampleRate + offset;
|
||||
|
||||
// Launch request.
|
||||
var promise = networkStatsMgr.getStats(null, start, end);
|
||||
promise.then(function(value) {
|
||||
// Check the object type.
|
||||
if (value instanceof ResourceStats) {
|
||||
ok(true, "Get a ResourceStats object.");
|
||||
} else {
|
||||
ok(false, "Fail to get a ResourceStats object.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check attributes of ResourceStats.
|
||||
ok(value.type == "network", "type should be network.");
|
||||
ok(value.component == null, "component should be null.");
|
||||
ok(value.serviceType == null, "serviceType should be null.");
|
||||
ok(value.manifestURL == null, "manifestURL should be null.");
|
||||
|
||||
// Check if time range of ResourceStats are normalized.
|
||||
ok(value.start == normStart, "start timestamp should be normalized.");
|
||||
ok(value.end == normEnd, "end timestamp should be normalized.");
|
||||
|
||||
// Check stats stored inside ResourceStats.
|
||||
if ('getData' in value) {
|
||||
checkData(value.getData(), normStart, normEnd);
|
||||
} else {
|
||||
ok(false, "getData is not a method of ResourceStats.");
|
||||
return;
|
||||
}
|
||||
}, function() {
|
||||
ok(false, "Get network stats failed.");
|
||||
});
|
||||
},
|
||||
|
||||
function () {
|
||||
// Check if getStats throw exception when start is greater than end.
|
||||
var end = Date.now();
|
||||
var start = end + 1000;
|
||||
var promise = networkStatsMgr.getStats(null, start, end);
|
||||
promise.then(function() {
|
||||
ok(false,
|
||||
"getStats dose not throw exception when start is great than end.");
|
||||
}, function() {
|
||||
ok(true, "getStats throw exception when start is great than end.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Check if getStats throw exception when start is less than 0.
|
||||
var end = Date.now();
|
||||
var start = -1;
|
||||
var promise = networkStatsMgr.getStats(null, start, end);
|
||||
promise.then(function() {
|
||||
ok(false,
|
||||
"getStats dose not throw exception when start is less than 0.");
|
||||
}, function() {
|
||||
ok(true, "getStats throw exception when start is less than 0.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Check if getStats throw exception when manifestURL is invalid.
|
||||
var options = {manifestURL: invalidManifestURL};
|
||||
var promise = networkStatsMgr.getStats(options);
|
||||
promise.then(function(value) {
|
||||
ok(false,
|
||||
"getStats does not throw exception when manifestURL is invalid.");
|
||||
}, function() {
|
||||
ok(true, "getStats throw exception when manifestURL is invalid.");
|
||||
testMethods();
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
// Test WebIDL methods related stats operation.
|
||||
function testMethods() {
|
||||
if (!testCases.length) {
|
||||
ok(true, "Done.");
|
||||
SpecialPowers.removePermission("resourcestats-manage", document);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var testCase = testCases.shift();
|
||||
testCase();
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
// Create an instance of ResourceStatsManager for network stats.
|
||||
networkStatsMgr = new window.ResourceStatsManager("network");
|
||||
ok(networkStatsMgr, "Create networkStatsMgr.");
|
||||
|
||||
// Test WebIDL attributes.
|
||||
testAttributes();
|
||||
|
||||
// Test WebIDL methods related to stats operation.
|
||||
testMethods();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Enable permission and preference.
|
||||
SpecialPowers.addPermission("resourcestats-manage", true, document);
|
||||
SpecialPowers.pushPrefEnv({ 'set': [
|
||||
["dom.resource_stats.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true]
|
||||
]}, startTest);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,38 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test to ensure ResourceStatsManager is not accessible without permission</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Test to ensure ResourceStatsManager is not accessible without permission.
|
||||
SpecialPowers.removePermission("resourcestats-manage", document);
|
||||
SpecialPowers.pushPrefEnv({ 'set': [
|
||||
["dom.resource_stats.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true]
|
||||
]}, function() {
|
||||
ok(!(SpecialPowers.hasPermission("resourcestats-manage", document)),
|
||||
"Do not have permission 'resourcestats-manage'.");
|
||||
ok(SpecialPowers.getBoolPref("dom.resource_stats.enabled"),
|
||||
"Preference 'dom.resource_stats.enabled' is true.");
|
||||
|
||||
// Check ResourceStatsManager exist.
|
||||
ok(!('ResourceStatsManager' in window),
|
||||
"Cannot access window.ResourceStatsManager when have no permission");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,41 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test to ensure ResourceStatsManager does not create an instance for non-supported type</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
const type = "non-supported";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Test to ensure ResourceStatsManager does not create an instance for
|
||||
// non-supported type.
|
||||
SpecialPowers.addPermission("resourcestats-manage", true, document);
|
||||
SpecialPowers.pushPrefEnv({ 'set': [
|
||||
["dom.resource_stats.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true]
|
||||
]}, function() {
|
||||
try {
|
||||
var mgr = ResourceStatsManager(type);
|
||||
ok(false,
|
||||
"Creating an instance for non-supported type should throw an exeception.");
|
||||
} catch (ex) {
|
||||
ok(ex,
|
||||
"Got an exception when creating an instance for non-supported type.");
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,355 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for ResourceStats methods realted to power resource control</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
const invalidManifestURL = "app://invalid.gaiamobile.org/manifest.webapp";
|
||||
const cpuComponent = "cpu:0";
|
||||
const gpsComponent = "gps:0";
|
||||
var powerStatsMgr = null; // ResourceStatsManager for power statistics.
|
||||
|
||||
function errorCb(reason) {
|
||||
ok(false, reason);
|
||||
}
|
||||
|
||||
// Check the content returned by getAlarms.
|
||||
function checkAlarmsArray(alarms) {
|
||||
// Check if data is an array.
|
||||
if (!Array.isArray(alarms)) {
|
||||
throw "getAlarms does not return an array.";
|
||||
} else {
|
||||
ok(true, "getAlarms returns an array.")
|
||||
}
|
||||
|
||||
// Iterate the array and check the type of each element.
|
||||
var obj = null;
|
||||
var message = null; // Message for exception
|
||||
|
||||
for (var i = 0; i < alarms.length; i++) {
|
||||
obj = alarms[i];
|
||||
|
||||
// Check if obj is an instance os ResourceStatsAlarm.
|
||||
if (!(obj instanceof ResourceStatsAlarm)) {
|
||||
message = "The array contains a non-ResourceStatsAlarm object.";
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if obj.type is power.
|
||||
if (obj.type != "power") {
|
||||
message = "The type of a ResourceStatsAlarm object is not power.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (message) {
|
||||
throw message;
|
||||
}
|
||||
|
||||
ok(true, "The return is an array of ResourceStatsAlarm objects.");
|
||||
}
|
||||
|
||||
// Test Cases for testing WebIDL methods related to resource control.
|
||||
var testCases = [
|
||||
function() {
|
||||
// Test removeAllAlarms.
|
||||
var promise = powerStatsMgr.removeAllAlarms();
|
||||
promise.then(function() {
|
||||
ok(true, "removeAllAlarms deleted all power alarms.");
|
||||
testMethods();
|
||||
}, function() {
|
||||
ok(false, "removeAllAlarms failed to delete power alarms.");
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test addAlarm.
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
var promise = powerStatsMgr.addAlarm(threshold,
|
||||
{ 'component': cpuComponent },
|
||||
{ 'startTime': Date.now() });
|
||||
promise.then(function(value) {
|
||||
// Check the value (alarmId).
|
||||
if (value < 0) {
|
||||
ok(false, "addAlarm failed to create an alarm.");
|
||||
} else {
|
||||
ok(true, "addAlarm created an alarm.");
|
||||
testMethods();
|
||||
}
|
||||
}, function() {
|
||||
ok(false, "addAlarm failed to create an alarm.");
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test addAlarm with negative threshold.
|
||||
var threshold = -1;
|
||||
var promise = powerStatsMgr.addAlarm(threshold,
|
||||
{ 'component': cpuComponent },
|
||||
{ 'startTime': Date.now() });
|
||||
promise.then(function() {
|
||||
// Check the value.
|
||||
ok(false,
|
||||
"addAlarm did not throw an exception when negative threshold is set.");
|
||||
}, function() {
|
||||
ok(true, "addAlarm threw an exception when negative threshold is set.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test addAlarm with no threshold.
|
||||
var promise = powerStatsMgr.addAlarm();
|
||||
promise.then(function() {
|
||||
// Check the value.
|
||||
ok(false, "addAlarm did not throw an exception when no threshold.");
|
||||
}, function() {
|
||||
ok(true, "addAlarm threw an exception when no threshold.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test addAlarm with negative startTime.
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
var promise = powerStatsMgr.addAlarm(threshold,
|
||||
{ 'component': cpuComponent },
|
||||
{ 'startTime': -1 });
|
||||
promise.then(function() {
|
||||
// Check the value.
|
||||
ok(false,
|
||||
"addAlarm did not throw an exception when negative startTime is set.");
|
||||
}, function() {
|
||||
ok(true, "addAlarm threw an exception when negative startTime is set.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test addAlarm when manifestURL is invalid.
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
var promise = powerStatsMgr.addAlarm(threshold,
|
||||
{ 'component': cpuComponent,
|
||||
'manifestURL': invalidManifestURL },
|
||||
{ 'startTime': Date.now() });
|
||||
promise.then(function() {
|
||||
// Check the value.
|
||||
ok(false, "addAlarm did not throw an exception when manifestURL is invalid.");
|
||||
}, function() {
|
||||
ok(true, "addAlarm threw an exception when manifestURL is invalid.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test getAlarms.
|
||||
var alarmId;
|
||||
var alarms;
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
|
||||
// Execution steps:
|
||||
// 1. Add a new alarm.
|
||||
var promise = powerStatsMgr.addAlarm(threshold,
|
||||
{ 'component': cpuComponent },
|
||||
{ 'startTime': Date.now() });
|
||||
|
||||
// 2. Test getAlarms if new alarm is added.
|
||||
var runGetAlarms = function(value) {
|
||||
alarmId = value;
|
||||
return powerStatsMgr.getAlarms({ 'component': cpuComponent });
|
||||
};
|
||||
|
||||
// 3. Check the content returned by getAlarms.
|
||||
var checkGetAlarmsReturn = function(value) {
|
||||
alarms = value;
|
||||
checkAlarmsArray(value);
|
||||
};
|
||||
|
||||
// 4. Check the alarm added in step 1 is inside the return of getAlarms.
|
||||
var checkAlarm = function (value) {
|
||||
// Find the alarm.
|
||||
var index = alarms.map(function(e) { return e.alarmId; })
|
||||
.indexOf(alarmId);
|
||||
if (index < 0) {
|
||||
throw "getAlarms does not get the alarm added in previous step.";
|
||||
}
|
||||
var alarm = alarms[index];
|
||||
|
||||
// Evaluate the alarm.
|
||||
ok(alarm.threshold == threshold, "threshold is equal.");
|
||||
ok(alarm.component == cpuComponent, "component is equal.");
|
||||
ok(alarm.serviceType == null, "serviceType should be null.");
|
||||
ok(alarm.manifestURL == null, "manifestURL should be null.");
|
||||
};
|
||||
|
||||
// Create promise chaining.
|
||||
promise.then(runGetAlarms)
|
||||
.then(checkGetAlarmsReturn)
|
||||
.then(checkAlarm)
|
||||
.then(testMethods, errorCb); // Execute next test case.
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test getAlarms with invalid manifestURL.
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
var promise = powerStatsMgr.addAlarm(threshold,
|
||||
{ 'component': cpuComponent,
|
||||
'manifestURL': invalidManifestURL },
|
||||
{ 'startTime': Date.now() });
|
||||
|
||||
promise.then(function() {
|
||||
// Check the value.
|
||||
ok(false, "getAlarms did not throw an exception when manifestURL is invalid.");
|
||||
}, function() {
|
||||
ok(true, "getAlarms threw an exception when manifestURL is invalid.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test getAlarms with incorrect parameter.
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
|
||||
// Execution steps:
|
||||
// 1. Add a new alarm.
|
||||
var promise = powerStatsMgr.addAlarm(threshold,
|
||||
{ 'component': cpuComponent },
|
||||
{ 'startTime': Date.now() });
|
||||
|
||||
// 2. Call getAlarms with incorrect parameter.
|
||||
var runGetAlarms = function() {
|
||||
return powerStatsMgr.getAlarms({ 'component': gpsComponent });
|
||||
};
|
||||
|
||||
// 3. check the content returned by getAlarms.
|
||||
var checkGetAlarmsReturn = function(value) {
|
||||
// Check array length
|
||||
if (value.length) {
|
||||
throw "getAlarms gets an alarm when using incorrect parameter.";
|
||||
} else {
|
||||
ok(true,
|
||||
"getAlarms returns an empty array when using incorrect parameter.");
|
||||
}
|
||||
};
|
||||
|
||||
// Create pomise chaining.
|
||||
promise.then(runGetAlarms)
|
||||
.then(checkGetAlarmsReturn)
|
||||
.then(testMethods, errorCb); // Execute next test case.
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test removeAlarm
|
||||
var alarmId;
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
|
||||
// Execution steps:
|
||||
// 1. Add a new alarm.
|
||||
var promise = powerStatsMgr.addAlarm(threshold,
|
||||
{ 'component': cpuComponent },
|
||||
{ 'startTime': Date.now() });
|
||||
|
||||
// 2. Try to remove the new alarm.
|
||||
var runRemoveAlarm = function(value) {
|
||||
alarmId = value;
|
||||
return powerStatsMgr.removeAlarm(alarmId);
|
||||
}
|
||||
|
||||
// Create promise chaining.
|
||||
promise.then(runRemoveAlarm)
|
||||
.then(function() {
|
||||
ok(true, "removeAlarm deleted the alarm.");
|
||||
testMethods();
|
||||
}, errorCb);
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test removeAlarm with negative alarmId
|
||||
var alarmId = -1;
|
||||
var promise = powerStatsMgr.removeAlarm(alarmId);
|
||||
promise.then(function() {
|
||||
ok(false,
|
||||
"removeAlarm did not throw an exception when negative alarmId is set.");
|
||||
}, function() {
|
||||
ok(true, "removeAlarm threw an exception when negative alarmId is set.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test removeAlarm with invalid alarmId
|
||||
var alarmId;
|
||||
var threshold = Math.floor(Math.random() * 1000);
|
||||
|
||||
// Execution steps:
|
||||
// 1. Add a new alarm.
|
||||
var promise = powerStatsMgr.addAlarm(threshold,
|
||||
{ 'component': cpuComponent },
|
||||
{ 'startTime': Date.now() });
|
||||
|
||||
// 2. Try to remove an invalid alarm.
|
||||
var runRemoveAlarm = function(value) {
|
||||
alarmId = value;
|
||||
// Because alarmId is auto-increment, any alarmId larger than the
|
||||
// latest alarmId should be invalid.
|
||||
return powerStatsMgr.removeAlarm(alarmId + 10);
|
||||
}
|
||||
|
||||
// Create promise chaining.
|
||||
promise.then(runRemoveAlarm)
|
||||
.then(function() {
|
||||
// Input with incorrect alarmId should not be resolved.
|
||||
throw "removeAlarm should fail with invalid alarmId.";
|
||||
}, function(reason) {
|
||||
if (reason == "alarm not existed") {
|
||||
ok(true, "removeAlarm with invalid alarmId should fail.")
|
||||
} else {
|
||||
throw reason;
|
||||
}
|
||||
})
|
||||
.then(testMethods, errorCb);
|
||||
}
|
||||
];
|
||||
|
||||
// Test WebIDL methods related stats operation.
|
||||
function testMethods() {
|
||||
if (!testCases.length) {
|
||||
ok(true, "Done.");
|
||||
SpecialPowers.removePermission("resourcestats-manage", document);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var testCase = testCases.shift();
|
||||
testCase();
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
// Create an instance of ResourceStatsManager for power.
|
||||
powerStatsMgr = new ResourceStatsManager("power");
|
||||
ok(powerStatsMgr, "Create powerStatsMgr.");
|
||||
|
||||
// Test WebIDL methods related to resource control.
|
||||
testMethods();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Enable permission and preference.
|
||||
SpecialPowers.addPermission("resourcestats-manage", true, document);
|
||||
SpecialPowers.pushPrefEnv({ 'set': [
|
||||
["dom.resource_stats.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true]
|
||||
]}, startTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,345 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for ResourceStats methods realted to power statistics</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
const invalidManifestURL = "app://invalid.gaiamobile.org/manifest.webapp";
|
||||
var powerStatsMgr = null; // ResourceStatsManager for power statistics.
|
||||
var sampleRate = 0;
|
||||
|
||||
// Test WebIDL attributes.
|
||||
function testAttributes() {
|
||||
// Test sampleRate.
|
||||
ok('sampleRate' in powerStatsMgr,
|
||||
"sampleRate should be a ResourceStatsManager attribute.");
|
||||
sampleRate = powerStatsMgr.sampleRate;
|
||||
ok(sampleRate > 0, "sampleRate is greater than 0.");
|
||||
|
||||
// Test maxStorageAge.
|
||||
ok('maxStorageAge' in powerStatsMgr,
|
||||
"maxStorageAge should be a ResourceStatsManager attribute.");
|
||||
ok(powerStatsMgr.maxStorageAge > 0,
|
||||
"maxStorageAge is greater than 0.");
|
||||
|
||||
// Test whether "power" in resourceTypes array.
|
||||
ok('resourceTypes' in powerStatsMgr,
|
||||
"resourceTypes should be a ResourceStatsManager attribute.");
|
||||
ok(Array.isArray(powerStatsMgr.resourceTypes),
|
||||
"powerStatsMgr.resourceTypes is an array.");
|
||||
ok(powerStatsMgr.resourceTypes.indexOf("power") > -1,
|
||||
"'power' is an element of powerStatsMgr.resourceTypes.");
|
||||
}
|
||||
|
||||
// Check the content returned by ResourceStats.getData().
|
||||
function checkData(data, start, end) {
|
||||
// Check if data is an array.
|
||||
if (!Array.isArray(data)) {
|
||||
ok(false, "getData does not return an array.")
|
||||
return;
|
||||
} else {
|
||||
ok(true, "getData returns an array.")
|
||||
}
|
||||
|
||||
// Iterate the array and check the timestamp and type of each element.
|
||||
var success = true;
|
||||
var obj = null;
|
||||
var timestamp = start;
|
||||
var i = 0;
|
||||
var length = data.length;
|
||||
|
||||
do {
|
||||
obj = data[i++];
|
||||
|
||||
// Check object type.
|
||||
if (!(obj instanceof PowerStatsData)) {
|
||||
success = false;
|
||||
ok(false, "The array contains a non-PowerStatsData object.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if the timestamp is continuous.
|
||||
if (obj.timestamp != timestamp) {
|
||||
success = false;
|
||||
ok(false, "The timestamp of PowerStatsData object is correct.");
|
||||
break;
|
||||
}
|
||||
|
||||
timestamp += sampleRate;
|
||||
} while (i < length);
|
||||
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the timestamp of the last element is equal to end.
|
||||
if (obj.timestamp != end) {
|
||||
ok(false,
|
||||
"The timestamp of the last element of the array is equal to end.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute next test case.
|
||||
ok(true, "The return of getData is an array of PowerStatsData objects.");
|
||||
testMethods();
|
||||
}
|
||||
|
||||
// Test Cases for testing WebIDL methods.
|
||||
var testCases = [
|
||||
function() {
|
||||
// Test clearAllStats.
|
||||
var promise = powerStatsMgr.clearAllStats();
|
||||
promise.then(function() {
|
||||
ok(true, "clearAllStats clears the power store.");
|
||||
testMethods();
|
||||
}, function() {
|
||||
ok(false, "clearAllStats fails to clear the power store.");
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test clearStats.
|
||||
var promise = powerStatsMgr.clearStats();
|
||||
promise.then(function() {
|
||||
ok(true, "clearStats clears the power store.");
|
||||
testMethods();
|
||||
}, function() {
|
||||
ok(false, "clearStats fails to clear the power store.");
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Check if clearStats throw exception when start is great than end.
|
||||
var end = Date.now();
|
||||
var start = end + 1000;
|
||||
var promise = powerStatsMgr.clearStats(null, start, end);
|
||||
promise.then(function() {
|
||||
ok(false,
|
||||
"clearStats does not throw exception when start is great than end.");
|
||||
}, function() {
|
||||
ok(true, "clearStats throw exception when start is great than end.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Check if clearStats throw exception when start is less than 0.
|
||||
var end = Date.now();
|
||||
var start = -1;
|
||||
var promise = powerStatsMgr.clearStats(null, start, end);
|
||||
promise.then(function() {
|
||||
ok(false,
|
||||
"clearStats dose not throw exception when start is less than 0.");
|
||||
}, function() {
|
||||
ok(true, "clearStats throw exception when start is less than 0.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Check if clearStats throw exception when manifestURL is invalid.
|
||||
var options = {manifestURL: invalidManifestURL};
|
||||
var promise = powerStatsMgr.clearStats(options);
|
||||
promise.then(function() {
|
||||
ok(false,
|
||||
"clearStats does not throw exception when manifestURL is invalid.");
|
||||
}, function() {
|
||||
ok(true, "clearStats throw exception when manifestURL is invalid.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test getAvailableComponents.
|
||||
var promise = powerStatsMgr.getAvailableComponents();
|
||||
promise.then(function(value) {
|
||||
if (Array.isArray(value)) {
|
||||
ok(true, "getAvailableComponents returns an array.");
|
||||
testMethods();
|
||||
} else {
|
||||
ok(false, "getAvailableComponents does not return an array.");
|
||||
}
|
||||
}, function() {
|
||||
ok(false, "Fail to execute getAvailableComponents.");
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test getStats.
|
||||
ok(true, "Get system stats when start and end are adapted to sampleRate.");
|
||||
|
||||
// Prepare start and end.
|
||||
var offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||
var end = Math.floor((Date.now() - offset) / sampleRate) * sampleRate + offset;
|
||||
var start = end - sampleRate * 10;
|
||||
|
||||
// Launch request.
|
||||
var promise = powerStatsMgr.getStats(null, start, end);
|
||||
promise.then(function(value) {
|
||||
// Check the object type.
|
||||
if (value instanceof ResourceStats) {
|
||||
ok(true, "Get a ResourceStats object.");
|
||||
} else {
|
||||
ok(false, "Fail to get a ResourceStats object.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check attributes of ResourceStats.
|
||||
ok(value.type == "power", "type should be power.");
|
||||
ok(value.component == null, "component should be null.");
|
||||
ok(value.serviceType == null, "serviceType should be null.");
|
||||
ok(value.manifestURL == null, "manifestURL should be null.");
|
||||
|
||||
// Check if the time range of ResourceStats is equal to the request.
|
||||
ok(value.start == start, "start timestamp should be equal.");
|
||||
ok(value.end == end, "end timestamp should be equal.");
|
||||
|
||||
// Check stats stored inside ResourceStats.
|
||||
if ('getData' in value) {
|
||||
checkData(value.getData(), start, end);
|
||||
} else {
|
||||
ok(false, "getData is not a method of ResourceStats.");
|
||||
return;
|
||||
}
|
||||
}, function() {
|
||||
ok(false, "Get power stats failed.");
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Test getStats when start and end are not adapted to sampleRate.
|
||||
ok(true,
|
||||
"Get system stats when start and end are not adapted to sampleRate.");
|
||||
|
||||
// Prepare start and end.
|
||||
var end = Date.now();
|
||||
var start = end - sampleRate * 10;
|
||||
|
||||
// Normalize start and end.
|
||||
var offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||
var normEnd = Math.floor((end - offset) / sampleRate)
|
||||
* sampleRate + offset;
|
||||
var normStart = Math.floor((start - offset) / sampleRate)
|
||||
* sampleRate + offset;
|
||||
|
||||
// Launch request.
|
||||
var promise = powerStatsMgr.getStats(null, start, end);
|
||||
promise.then(function(value) {
|
||||
// Check the object type.
|
||||
if (value instanceof ResourceStats) {
|
||||
ok(true, "Get a ResourceStats object.");
|
||||
} else {
|
||||
ok(false, "Fail to get a ResourceStats object.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check attributes of ResourceStats.
|
||||
ok(value.type == "power", "type should be power.");
|
||||
ok(value.component == null, "component should be null.");
|
||||
ok(value.serviceType == null, "serviceType should be null.");
|
||||
ok(value.manifestURL == null, "manifestURL should be null.");
|
||||
|
||||
// Check if time range of ResourceStats are normalized.
|
||||
ok(value.start == normStart, "start timestamp should be normalized.");
|
||||
ok(value.end == normEnd, "end timestamp should be normalized.");
|
||||
|
||||
// Check stats stored inside ResourceStats.
|
||||
if ('getData' in value) {
|
||||
checkData(value.getData(), normStart, normEnd);
|
||||
} else {
|
||||
ok(false, "getData is not a method of ResourceStats.");
|
||||
return;
|
||||
}
|
||||
}, function() {
|
||||
ok(false, "Get power stats failed.");
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Check if getStats throw exception when start is greater than end.
|
||||
var end = Date.now();
|
||||
var start = end + 1000;
|
||||
var promise = powerStatsMgr.getStats(null, start, end);
|
||||
promise.then(function() {
|
||||
ok(false,
|
||||
"getStats dose not throw exception when start is great than end.");
|
||||
}, function() {
|
||||
ok(true, "getStats throw exception when start is great than end.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Check if getStats throw exception when start is less than 0.
|
||||
var end = Date.now();
|
||||
var start = -1;
|
||||
var promise = powerStatsMgr.getStats(null, start, end);
|
||||
promise.then(function() {
|
||||
ok(false,
|
||||
"getStats dose not throw exception when start is less than 0.");
|
||||
}, function() {
|
||||
ok(true, "getStats throw exception when start is less than 0.");
|
||||
testMethods();
|
||||
});
|
||||
},
|
||||
|
||||
function() {
|
||||
// Check if getStats throw exception when manifestURL is invalid.
|
||||
var options = {manifestURL: invalidManifestURL};
|
||||
var promise = powerStatsMgr.getStats(options);
|
||||
promise.then(function(value) {
|
||||
ok(false,
|
||||
"getStats does not throw exception when manifestURL is invalid.");
|
||||
}, function() {
|
||||
ok(true, "getStats throw exception when manifestURL is invalid.");
|
||||
testMethods();
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
// Test WebIDL methods related stats operation.
|
||||
function testMethods() {
|
||||
if (!testCases.length) {
|
||||
ok(true, "Done.");
|
||||
SpecialPowers.removePermission("resourcestats-manage", document);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var testCase = testCases.shift();
|
||||
testCase();
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
// Create an instance of ResourceStatsManager for power stats.
|
||||
powerStatsMgr = new ResourceStatsManager("power");
|
||||
ok(powerStatsMgr, "Create powerStatsMgr.");
|
||||
|
||||
// Test WebIDL attributes.
|
||||
testAttributes();
|
||||
|
||||
// Test WebIDL methods related to stats operation.
|
||||
testMethods();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Enable permission and preference.
|
||||
SpecialPowers.addPermission("resourcestats-manage", true, document);
|
||||
SpecialPowers.pushPrefEnv({ 'set': [
|
||||
["dom.resource_stats.enabled", true],
|
||||
["dom.ignore_webidl_scope_checks", true]
|
||||
]}, startTest);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,985 +0,0 @@
|
|||
/* Any: copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/ResourceStatsDB.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const db = new ResourceStatsDB();
|
||||
|
||||
// Components.
|
||||
const wifiComponent = "wifi:0";
|
||||
const mobileComponent = "mobile:1";
|
||||
const cpuComponent = "cpu:0";
|
||||
const gpsComponent = "gps:0";
|
||||
|
||||
// List of available components.
|
||||
const networkComponents = [wifiComponent, mobileComponent];
|
||||
const powerComponents = [cpuComponent, gpsComponent];
|
||||
const offset = (new Date()).getTimezoneOffset() * 60 * 1000;
|
||||
|
||||
// Clear store.
|
||||
function clearStore(store, callback) {
|
||||
db._dbNewTxn(store, "readwrite", function(aTxn, aStore) {
|
||||
aStore.openCursor().onsuccess = function (event) {
|
||||
let cursor = event.target.result;
|
||||
if (cursor){
|
||||
cursor.delete();
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
}, callback);
|
||||
}
|
||||
|
||||
// Clear all stores to avoid starting tests with unknown state.
|
||||
add_test(function prepareDatabase() {
|
||||
clearStore('power_stats_store', function() {
|
||||
clearStore('network_stats_store', function() {
|
||||
clearStore('alarm_store', function() {
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Dump data saved in a store.
|
||||
function dumpStore(store, callback) {
|
||||
db._dbNewTxn(store, "readonly", function(aTxn, aStore) {
|
||||
aStore.mozGetAll().onsuccess = function onsuccess(event) {
|
||||
aTxn.result = event.target.result;
|
||||
};
|
||||
}, callback);
|
||||
}
|
||||
|
||||
// Check sampleRate is unchangeable.
|
||||
add_test(function test_sampleRate() {
|
||||
var sampleRate = db.sampleRate;
|
||||
do_check_true(sampleRate > 0);
|
||||
|
||||
db.sampleRate = 0;
|
||||
sampleRate = db.sampleRate;
|
||||
do_check_true(sampleRate > 0);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Test maxStorageAge is unchangeable.
|
||||
add_test(function test_maxStorageAge() {
|
||||
var maxStorageAge = db.maxStorageAge;
|
||||
do_check_true(maxStorageAge > 0);
|
||||
|
||||
db.maxStorageAge = 0;
|
||||
maxStorageAge = db.maxStorageAge;
|
||||
do_check_true(maxStorageAge > 0);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Normalize timestamp to sampleRate precision.
|
||||
function normalizeTime(aTimeStamp) {
|
||||
var sampleRate = db.sampleRate;
|
||||
|
||||
return Math.floor((aTimeStamp - offset) / sampleRate) * sampleRate;
|
||||
}
|
||||
|
||||
// Generte record as input for saveNetworkStats() as well as the expected
|
||||
// result when calling getStats().
|
||||
function generateNetworkRecord(aAppId, aServiceType, aComponents) {
|
||||
var result = [];
|
||||
var componentStats = {};
|
||||
var receivedBytes;
|
||||
var sentBytes;
|
||||
var totalReceivedBytes = 0;
|
||||
var totalSentBytes = 0;
|
||||
|
||||
aComponents.forEach(function(comp) {
|
||||
// Step 1: generate random value for receivedBytes and sentBytes.
|
||||
receivedBytes = Math.floor(Math.random() * 1000);
|
||||
sentBytes = Math.floor(Math.random() * 1000);
|
||||
totalReceivedBytes += receivedBytes;
|
||||
totalSentBytes += sentBytes;
|
||||
|
||||
// Step 2: add stats to record.componentStats.
|
||||
componentStats[comp] = {
|
||||
receivedBytes: receivedBytes,
|
||||
sentBytes: sentBytes
|
||||
};
|
||||
|
||||
// Step 3: generate expected results.
|
||||
result.push({
|
||||
appId: aAppId,
|
||||
serviceType: aServiceType,
|
||||
component: comp,
|
||||
receivedBytes: receivedBytes,
|
||||
sentBytes: sentBytes
|
||||
});
|
||||
});
|
||||
|
||||
// Step 4: generate expected total stats.
|
||||
result.push({
|
||||
appId: aAppId,
|
||||
serviceType: aServiceType,
|
||||
component: "",
|
||||
receivedBytes: totalReceivedBytes,
|
||||
sentBytes: totalSentBytes
|
||||
});
|
||||
|
||||
// Step 5: get record.
|
||||
var record = { appId: aAppId,
|
||||
serviceType: aServiceType,
|
||||
componentStats: componentStats };
|
||||
|
||||
return { record: record, result: result };
|
||||
}
|
||||
|
||||
// Generte record as input for savePowerStats() as well as the expected
|
||||
// result when calling getStats().
|
||||
function generatePowerRecord(aAppId, aServiceType, aComponents) {
|
||||
var result = [];
|
||||
var componentStats = {};
|
||||
var consumedPower;
|
||||
var totalConsumedPower = 0;
|
||||
|
||||
aComponents.forEach(function(comp) {
|
||||
// Step 1: generate random value for consumedPower.
|
||||
consumedPower = Math.floor(Math.random() * 1000);
|
||||
totalConsumedPower += consumedPower;
|
||||
|
||||
// Step 2: add stats to record.componentStats.
|
||||
componentStats[comp] = consumedPower;
|
||||
|
||||
// Step 3: generate expected results.
|
||||
result.push({
|
||||
appId: aAppId,
|
||||
serviceType: aServiceType,
|
||||
component: comp,
|
||||
consumedPower: consumedPower
|
||||
});
|
||||
});
|
||||
|
||||
// Step 4: generate expected total stats.
|
||||
result.push({
|
||||
appId: aAppId,
|
||||
serviceType: aServiceType,
|
||||
component: "",
|
||||
consumedPower: totalConsumedPower
|
||||
});
|
||||
|
||||
// Step 5: get record.
|
||||
var record = { appId: aAppId,
|
||||
serviceType: aServiceType,
|
||||
componentStats: componentStats };
|
||||
|
||||
return { record: record, result: result };
|
||||
}
|
||||
|
||||
// Compare stats saved in network store with expected results.
|
||||
function checkNetworkStatsStore(aExpectedResult, aDumpResult, aTimestamp) {
|
||||
// Step 1: a quick check for the length of arrays first.
|
||||
do_check_eq(aExpectedResult.length, aDumpResult.length);
|
||||
|
||||
// Step 2: create a map array for search by receivedBytes.
|
||||
var mapArray = aExpectedResult.map(function(e) {return e.receivedBytes;});
|
||||
|
||||
// Step 3: compare each element to make sure both array are equal.
|
||||
var index;
|
||||
var target;
|
||||
|
||||
aDumpResult.forEach(function(stats) {
|
||||
index = 0;
|
||||
// Find the first equal receivedBytes since index.
|
||||
while ((index = mapArray.indexOf(stats.receivedBytes, index)) > -1) {
|
||||
// Compare all attributes.
|
||||
target = aExpectedResult[index];
|
||||
if (target.appId != stats.appId ||
|
||||
target.serviceType != stats.serviceType ||
|
||||
target.component != stats.component ||
|
||||
target.sentBytes != stats.sentBytes ||
|
||||
aTimestamp != stats.timestamp) {
|
||||
index += 1;
|
||||
continue;
|
||||
} else {
|
||||
// If found, remove that element from aExpectedResult and mapArray.
|
||||
aExpectedResult.splice(index, 1);
|
||||
mapArray.splice(index, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
do_check_neq(index, -1);
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Compare stats saved in power store with expected results.
|
||||
function checkPowerStatsStore(aExpectedResult, aDumpResult, aTimestamp) {
|
||||
// Step 1: a quick check for the length of arrays first.
|
||||
do_check_eq(aExpectedResult.length, aDumpResult.length);
|
||||
|
||||
// Step 2: create a map array for search by consumedPower.
|
||||
var mapArray = aExpectedResult.map(function(e) {return e.consumedPower;});
|
||||
|
||||
// Step 3: compare each element to make sure both array are equal.
|
||||
var index;
|
||||
var target;
|
||||
|
||||
aDumpResult.forEach(function(stats) {
|
||||
index = 0;
|
||||
// Find the first equal consumedPower since index.
|
||||
while ((index = mapArray.indexOf(stats.consumedPower, index)) > -1) {
|
||||
// Compare all attributes.
|
||||
target = aExpectedResult[index];
|
||||
if (target.appId != stats.appId ||
|
||||
target.serviceType != stats.serviceType ||
|
||||
target.component != stats.component ||
|
||||
aTimestamp != stats.timestamp) {
|
||||
index += 1;
|
||||
continue;
|
||||
} else {
|
||||
// If found, remove that element from aExpectedResult and mapArray.
|
||||
aExpectedResult.splice(index, 1);
|
||||
mapArray.splice(index, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
do_check_neq(index, -1);
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Prepare network store for testing.
|
||||
function prepareNetworkStatsStore(recordArray, timestamp, callback) {
|
||||
// Step 1: clear store.
|
||||
clearStore("network_stats_store", function() {
|
||||
// Step 2: save record to store.
|
||||
db.saveNetworkStats(recordArray, timestamp, callback);
|
||||
});
|
||||
}
|
||||
|
||||
// Prepare power store for testing.
|
||||
function preparePowerStatsStore(recordArray, timestamp, callback) {
|
||||
// Step 1: clear store.
|
||||
clearStore("power_stats_store", function() {
|
||||
// Step 2: save record to store.
|
||||
db.savePowerStats(recordArray, timestamp, callback);
|
||||
});
|
||||
}
|
||||
|
||||
// Test saveNetworkStats.
|
||||
add_test(function test_saveNetworkStats() {
|
||||
var appId = 1;
|
||||
var serviceType = "";
|
||||
|
||||
// Step 1: generate data saved to store.
|
||||
var { record: record, result: expectedResult } =
|
||||
generateNetworkRecord(appId, serviceType, networkComponents);
|
||||
var recordArray = [record];
|
||||
var timestamp = Date.now();
|
||||
|
||||
// Step 2: save recordArray to network store.
|
||||
prepareNetworkStatsStore(recordArray, timestamp, function(error, callback) {
|
||||
// Step 3: check if the function call succeed.
|
||||
do_check_eq(error, null);
|
||||
// Step 4: dump store for comparison.
|
||||
dumpStore("network_stats_store", function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
checkNetworkStatsStore(expectedResult, result, normalizeTime(timestamp));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test savePowerStats.
|
||||
add_test(function test_savePowerStats() {
|
||||
var appId = 1;
|
||||
var serviceType = "";
|
||||
|
||||
// Step 1: generate data saved to store.
|
||||
var { record: record, result: expectedResult } =
|
||||
generatePowerRecord(appId, serviceType, powerComponents);
|
||||
var recordArray = [record];
|
||||
var timestamp = Date.now();
|
||||
|
||||
// Step 2: save recordArray to power store.
|
||||
preparePowerStatsStore(recordArray, timestamp, function(error, callback) {
|
||||
// Step 3: check if the function call succeed.
|
||||
do_check_eq(error, null);
|
||||
// Step 4: dump store for comparison.
|
||||
dumpStore("power_stats_store", function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
checkPowerStatsStore(expectedResult, result, normalizeTime(timestamp));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test getting network stats via getStats.
|
||||
add_test(function test_getNetworkStats() {
|
||||
var appId = 0;
|
||||
var manifestURL = "";
|
||||
var serviceType = "";
|
||||
var component = "";
|
||||
|
||||
// Step 1: generate data saved to store.
|
||||
var { record: record, result: result } =
|
||||
generateNetworkRecord(appId, serviceType, networkComponents);
|
||||
var recordArray = [record];
|
||||
var expectedStats = result[result.length - 1]; // Check total stats only.
|
||||
var timestamp = Date.now();
|
||||
var end = normalizeTime(timestamp) + offset;
|
||||
var start = end;
|
||||
|
||||
// Step 2: save record and prepare network store.
|
||||
prepareNetworkStatsStore(recordArray, timestamp, function(error, callback) {
|
||||
// Step 3: get network stats.
|
||||
db.getStats("network", manifestURL, serviceType, component, start, end,
|
||||
function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
|
||||
// Step 4: verify result.
|
||||
do_check_eq(result.type, "network");
|
||||
do_check_eq(result.manifestURL, manifestURL);
|
||||
do_check_eq(result.serviceType, serviceType);
|
||||
do_check_eq(result.component, component);
|
||||
do_check_eq(result.start, start);
|
||||
do_check_eq(result.end, end);
|
||||
do_check_eq(result.sampleRate, db.sampleRate);
|
||||
do_check_true(Array.isArray(result.statsData));
|
||||
do_check_eq(result.statsData.length, 1);
|
||||
var stats = result.statsData[0];
|
||||
do_check_eq(stats.receivedBytes, expectedStats.receivedBytes);
|
||||
do_check_eq(stats.sentBytes, expectedStats.sentBytes);
|
||||
|
||||
run_next_test(); // If success, run next test.
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test getting power stats via getStats.
|
||||
add_test(function test_getPowerStats() {
|
||||
var appId = 0;
|
||||
var manifestURL = "";
|
||||
var serviceType = "";
|
||||
var component = "";
|
||||
|
||||
// Step 1: generate data saved to store.
|
||||
var { record: record, result: result } =
|
||||
generatePowerRecord(appId, serviceType, powerComponents);
|
||||
var recordArray = [record];
|
||||
var expectedStats = result[result.length - 1]; // Check total stats only.
|
||||
var timestamp = Date.now();
|
||||
var end = normalizeTime(timestamp) + offset;
|
||||
var start = end;
|
||||
|
||||
// Step 2: save record and prepare power store.
|
||||
preparePowerStatsStore(recordArray, timestamp, function(error, callback) {
|
||||
// Step 3: get power stats.
|
||||
db.getStats("power", manifestURL, serviceType, component, start, end,
|
||||
function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
|
||||
// Step 4: verify result
|
||||
do_check_eq(result.type, "power");
|
||||
do_check_eq(result.manifestURL, manifestURL);
|
||||
do_check_eq(result.serviceType, serviceType);
|
||||
do_check_eq(result.component, component);
|
||||
do_check_eq(result.start, start);
|
||||
do_check_eq(result.end, end);
|
||||
do_check_eq(result.sampleRate, db.sampleRate);
|
||||
do_check_true(Array.isArray(result.statsData));
|
||||
do_check_eq(result.statsData.length, 1);
|
||||
var stats = result.statsData[0];
|
||||
do_check_eq(stats.consumedPower, expectedStats.consumedPower);
|
||||
|
||||
run_next_test(); // If success, run next test.
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test deleting network stats via clearStats.
|
||||
add_test(function test_clearNetworkStats() {
|
||||
var appId = 0;
|
||||
var manifestURL = "";
|
||||
var serviceType = "";
|
||||
var component = "";
|
||||
|
||||
// Step 1: genrate data saved to network store.
|
||||
var { record: record, result: result } =
|
||||
generateNetworkRecord(appId, serviceType, networkComponents);
|
||||
var recordArray = [record];
|
||||
var timestamp = Date.now();
|
||||
var end = normalizeTime(timestamp) + offset;
|
||||
var start = end;
|
||||
|
||||
// Step 2: save record and prepare network store.
|
||||
prepareNetworkStatsStore(recordArray, timestamp, function(error, callback) {
|
||||
// Step 3: clear network stats.
|
||||
db.clearStats("network", appId, serviceType, component, start, end,
|
||||
function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
|
||||
// Step 4: check if the stats is deleted.
|
||||
db.getStats("network", manifestURL, serviceType, component, start, end,
|
||||
function(error, result) {
|
||||
do_check_eq(result.statsData.length, 0);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test deleting power stats via clearStats.
|
||||
add_test(function test_clearPowerStats() {
|
||||
var appId = 0;
|
||||
var manifestURL = "";
|
||||
var serviceType = "";
|
||||
var component = "";
|
||||
|
||||
// Step 1: genrate data saved to power store.
|
||||
var { record: record, result: result } =
|
||||
generatePowerRecord(appId, serviceType, powerComponents);
|
||||
var recordArray = [record];
|
||||
var timestamp = Date.now();
|
||||
var end = normalizeTime(timestamp) + offset;
|
||||
var start = end;
|
||||
|
||||
// Step 2: save record and prepare power store.
|
||||
preparePowerStatsStore(recordArray, timestamp, function(error, callback) {
|
||||
// Step 3: clear power stats.
|
||||
db.clearStats("power", appId, serviceType, component, start, end,
|
||||
function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
|
||||
// Step 4: check if the stats is deleted.
|
||||
db.getStats("power", manifestURL, serviceType, component, start, end,
|
||||
function(error, result) {
|
||||
do_check_eq(result.statsData.length, 0);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test clearing all network stats.
|
||||
add_test(function test_clearAllNetworkStats() {
|
||||
db.clearAllStats("network", function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
// Test clearing all power stats.
|
||||
add_test(function test_clearAllPowerStats() {
|
||||
db.clearAllStats("power", function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
// Test getting network components.
|
||||
add_test(function test_getNetworkComponents() {
|
||||
var appId = 0;
|
||||
var serviceType = "";
|
||||
|
||||
// Step 1: generate data saved to store.
|
||||
var { record: record, result: expectedResult } =
|
||||
generateNetworkRecord(appId, serviceType, networkComponents);
|
||||
var recordArray = [record];
|
||||
var timestamp = Date.now();
|
||||
|
||||
// Step 2: save recordArray to network store.
|
||||
prepareNetworkStatsStore(recordArray, timestamp, function(error, callback) {
|
||||
// Step 3: call getComponents.
|
||||
db.getComponents("network", function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_true(Array.isArray(result));
|
||||
do_check_eq(result.length, networkComponents.length);
|
||||
|
||||
// Check each element in result array is an element of networkComponents.
|
||||
result.forEach(function(component) {
|
||||
do_check_true(networkComponents.indexOf(component) > -1);
|
||||
});
|
||||
|
||||
run_next_test(); // If success, run next test.
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test getting power components.
|
||||
add_test(function test_getPowerComponents() {
|
||||
var appId = 0;
|
||||
var serviceType = "";
|
||||
|
||||
// Step 1: generate data saved to store.
|
||||
var { record: record, result: expectedResult } =
|
||||
generatePowerRecord(appId, serviceType, powerComponents);
|
||||
var recordArray = [record];
|
||||
var timestamp = Date.now();
|
||||
|
||||
// Step 2: save recordArray to power store.
|
||||
preparePowerStatsStore(recordArray, timestamp, function(error, callback) {
|
||||
// Step 3: call getComponents.
|
||||
db.getComponents("power", function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_true(Array.isArray(result));
|
||||
do_check_eq(result.length, powerComponents.length);
|
||||
|
||||
// Check each element in result array is an element of powerComponents.
|
||||
result.forEach(function(component) {
|
||||
do_check_true(powerComponents.indexOf(component) > -1);
|
||||
});
|
||||
|
||||
run_next_test(); // If success, run next test.
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Generate alarm object for addAlarm().
|
||||
function generateAlarmObject(aType, aManifestURL, aServiceType, aComponent) {
|
||||
let alarm = {
|
||||
type: aType,
|
||||
component: aComponent,
|
||||
serviceType: aServiceType,
|
||||
manifestURL: aManifestURL,
|
||||
threshold: Math.floor(Math.random() * 1000),
|
||||
startTime: Math.floor(Math.random() * 1000),
|
||||
data: null
|
||||
};
|
||||
|
||||
return alarm;
|
||||
}
|
||||
|
||||
// Test adding a network alarm.
|
||||
add_test(function test_addNetowrkAlarm() {
|
||||
var manifestURL = "";
|
||||
var serviceType = "";
|
||||
|
||||
// Step 1: generate a network alarm.
|
||||
var alarm =
|
||||
generateAlarmObject("network", manifestURL, serviceType, wifiComponent);
|
||||
|
||||
// Step 2: clear store.
|
||||
clearStore("alarm_store", function() {
|
||||
// Step 3: save the alarm to store.
|
||||
db.addAlarm(alarm, function(error, result) {
|
||||
// Step 4: check if the function call succeed.
|
||||
do_check_eq(error, null);
|
||||
do_check_true(result > -1);
|
||||
let alarmId = result;
|
||||
|
||||
// Step 5: dump store for comparison.
|
||||
dumpStore("alarm_store", function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_true(Array.isArray(result));
|
||||
do_check_true(result.length == 1);
|
||||
do_check_eq(result[0].type, alarm.type);
|
||||
do_check_eq(result[0].manifestURL, alarm.manifestURL);
|
||||
do_check_eq(result[0].serviceType, alarm.serviceType);
|
||||
do_check_eq(result[0].component, alarm.component);
|
||||
do_check_eq(result[0].threshold, alarm.threshold);
|
||||
do_check_eq(result[0].startTime, alarm.startTime);
|
||||
do_check_eq(result[0].data, alarm.data);
|
||||
do_check_eq(result[0].alarmId, alarmId);
|
||||
|
||||
run_next_test(); // If success, run next test.
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test adding a power alarm.
|
||||
add_test(function test_addPowerAlarm() {
|
||||
var manifestURL = "";
|
||||
var serviceType = "";
|
||||
|
||||
// Step 1: generate a power alarm.
|
||||
var alarm =
|
||||
generateAlarmObject("power", manifestURL, serviceType, cpuComponent);
|
||||
|
||||
// Step 2: clear store.
|
||||
clearStore("alarm_store", function() {
|
||||
// Step 3: save the alarm to store.
|
||||
db.addAlarm(alarm, function(error, result) {
|
||||
// Step 4: check if the function call succeed.
|
||||
do_check_eq(error, null);
|
||||
do_check_true(result > -1);
|
||||
let alarmId = result;
|
||||
|
||||
// Step 5: dump store for comparison.
|
||||
dumpStore("alarm_store", function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_true(Array.isArray(result));
|
||||
do_check_true(result.length == 1);
|
||||
do_check_eq(result[0].type, alarm.type);
|
||||
do_check_eq(result[0].manifestURL, alarm.manifestURL);
|
||||
do_check_eq(result[0].serviceType, alarm.serviceType);
|
||||
do_check_eq(result[0].component, alarm.component);
|
||||
do_check_eq(result[0].threshold, alarm.threshold);
|
||||
do_check_eq(result[0].startTime, alarm.startTime);
|
||||
do_check_eq(result[0].data, alarm.data);
|
||||
do_check_eq(result[0].alarmId, alarmId);
|
||||
|
||||
run_next_test(); // If success, run next test.
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Add multiple alarms to store and record the obtained alarmId in each
|
||||
// alarm object.
|
||||
function addAlarmsToStore(alarms, index, callback) {
|
||||
var alarm = alarms[index++];
|
||||
if (index < alarms.length) {
|
||||
db.addAlarm(alarm, function(error, result) {
|
||||
alarm.alarmId = result;
|
||||
addAlarmsToStore(alarms, index, callback);
|
||||
});
|
||||
} else {
|
||||
db.addAlarm(alarm, function(error, result) {
|
||||
alarm.alarmId = result;
|
||||
callback(error, result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare alarm store for testging.
|
||||
function prepareAlarmStore(alarms, callback) {
|
||||
// Step 1: clear srore.
|
||||
clearStore("alarm_store", function() {
|
||||
// Step 2: save alarms to store one by one.
|
||||
addAlarmsToStore(alarms, 0, callback);
|
||||
});
|
||||
}
|
||||
|
||||
// Compare alrams returned by getAlarms().
|
||||
function compareAlarms(aExpectedResult, aResult) {
|
||||
// Step 1: a quick check for the length of arrays first.
|
||||
do_check_eq(aExpectedResult.length, aResult.length);
|
||||
|
||||
// Step 2: create a map array for search by threshold.
|
||||
var mapArray = aExpectedResult.map(function(e) {return e.threshold;});
|
||||
|
||||
// Step 3: compare each element to make sure both array are equal.
|
||||
var index;
|
||||
var target;
|
||||
|
||||
aResult.forEach(function(alarm) {
|
||||
index = 0;
|
||||
// Find the first equal receivedBytes since index.
|
||||
while ((index = mapArray.indexOf(alarm.threshold, index)) > -1) {
|
||||
// Compare all attributes.
|
||||
target = aExpectedResult[index];
|
||||
if (target.alarmId != alarm.alarmId ||
|
||||
target.type != alarm.type ||
|
||||
target.manifestURL != alarm.manifestURL ||
|
||||
target.serviceType != alarm.serviceType ||
|
||||
target.component != alarm.component ||
|
||||
target.data != alarm.data) {
|
||||
index += 1;
|
||||
continue;
|
||||
} else {
|
||||
// If found, remove that element from aExpectedResult and mapArray.
|
||||
aExpectedResult.splice(index, 1);
|
||||
mapArray.splice(index, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
do_check_neq(index, -1);
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Test getting designate network alarms from store.
|
||||
add_test(function test_getNetworkAlarms() {
|
||||
var manifestURL = "";
|
||||
var serviceType = "";
|
||||
var alarms = [];
|
||||
|
||||
// Step 1: generate two network alarms using same parameters.
|
||||
alarms.push(generateAlarmObject("network", manifestURL, serviceType,
|
||||
wifiComponent));
|
||||
alarms.push(generateAlarmObject("network", manifestURL, serviceType,
|
||||
wifiComponent));
|
||||
|
||||
// Step 2: generate another network alarm using different parameters.
|
||||
alarms.push(generateAlarmObject("network", manifestURL, serviceType,
|
||||
mobileComponent));
|
||||
|
||||
// Step 3: clear alarm store and save new alarms to store.
|
||||
prepareAlarmStore(alarms, function(error, result) {
|
||||
// Step 4: call getAlarms.
|
||||
let options = {
|
||||
manifestURL: manifestURL,
|
||||
serviceType: serviceType,
|
||||
component: wifiComponent
|
||||
};
|
||||
db.getAlarms("network", options, function(error, result) {
|
||||
// Step 5: check if the function call succeed.
|
||||
do_check_eq(error, null);
|
||||
|
||||
// Step 6: check results.
|
||||
// The last element in alarms array is not our expected result,
|
||||
// so pop that out first.
|
||||
alarms.pop();
|
||||
compareAlarms(alarms, result);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test getting designate power alarms from store.
|
||||
add_test(function test_getPowerAlarms() {
|
||||
var manifestURL = "";
|
||||
var serviceType = "";
|
||||
var alarms = [];
|
||||
|
||||
// Step 1: generate two power alarms using same parameters.
|
||||
alarms.push(generateAlarmObject("power", manifestURL, serviceType,
|
||||
cpuComponent));
|
||||
alarms.push(generateAlarmObject("power", manifestURL, serviceType,
|
||||
cpuComponent));
|
||||
|
||||
// Step 2: generate another power alarm using different parameters.
|
||||
alarms.push(generateAlarmObject("power", manifestURL, serviceType,
|
||||
gpsComponent));
|
||||
|
||||
// Step 3: clear alarm store and save new alarms to store.
|
||||
prepareAlarmStore(alarms, function(error, result) {
|
||||
// Step 4: call getAlarms.
|
||||
let options = {
|
||||
manifestURL: manifestURL,
|
||||
serviceType: serviceType,
|
||||
component: cpuComponent
|
||||
};
|
||||
db.getAlarms("power", options, function(error, result) {
|
||||
// Step 5: check if the function call succeed.
|
||||
do_check_eq(error, null);
|
||||
|
||||
// Step 6: check results.
|
||||
// The last element in alarms array is not our expected result,
|
||||
// so pop that out first.
|
||||
alarms.pop();
|
||||
compareAlarms(alarms, result);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test getting all network alarms from store.
|
||||
add_test(function test_getAllNetworkAlarms() {
|
||||
var manifestURL = "";
|
||||
var serviceType = "";
|
||||
var alarms = [];
|
||||
|
||||
// Step 1: generate two network alarms.
|
||||
alarms.push(generateAlarmObject("network", manifestURL, serviceType,
|
||||
wifiComponent));
|
||||
alarms.push(generateAlarmObject("network", manifestURL, serviceType,
|
||||
mobileComponent));
|
||||
|
||||
// Step 2: generate another power alarm.
|
||||
alarms.push(generateAlarmObject("power", manifestURL, serviceType,
|
||||
cpuComponent));
|
||||
|
||||
// Step 3: clear alarm store and save new alarms to store.
|
||||
prepareAlarmStore(alarms, function(error, result) {
|
||||
// Step 4: call getAlarms.
|
||||
let options = null;
|
||||
db.getAlarms("network", options, function(error, result) {
|
||||
// Step 5: check if the function call succeed.
|
||||
do_check_eq(error, null);
|
||||
|
||||
// Step 6: check results.
|
||||
// The last element in alarms array is not our expected result,
|
||||
// so pop that out first.
|
||||
alarms.pop();
|
||||
compareAlarms(alarms, result);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test getting all power alarms from store.
|
||||
add_test(function test_getAllPowerAlarms() {
|
||||
var manifestURL = "";
|
||||
var serviceType = "";
|
||||
var alarms = [];
|
||||
|
||||
// Step 1: generate two power alarms.
|
||||
alarms.push(generateAlarmObject("power", manifestURL, serviceType,
|
||||
cpuComponent));
|
||||
alarms.push(generateAlarmObject("power", manifestURL, serviceType,
|
||||
gpsComponent));
|
||||
|
||||
// Step 2: generate another network alarm.
|
||||
alarms.push(generateAlarmObject("network", manifestURL, serviceType,
|
||||
wifiComponent));
|
||||
|
||||
// Step 3: clear alarm store and save new alarms to store.
|
||||
prepareAlarmStore(alarms, function(error, result) {
|
||||
// Step 4: call getAlarms.
|
||||
let options = null;
|
||||
db.getAlarms("power", options, function(error, result) {
|
||||
// Step 5: check if the function call succeed.
|
||||
do_check_eq(error, null);
|
||||
|
||||
// Step 6: check results.
|
||||
// The last element in alarms array is not our expected result,
|
||||
// so pop that out first.
|
||||
alarms.pop();
|
||||
compareAlarms(alarms, result);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test removing designate network alarm from store.
|
||||
add_test(function test_removeNetworkAlarm() {
|
||||
var manifestURL = "";
|
||||
var serviceType = "";
|
||||
var alarms = [];
|
||||
|
||||
// Step 1: generate one network alarm.
|
||||
alarms.push(generateAlarmObject("network", manifestURL, serviceType,
|
||||
wifiComponent));
|
||||
|
||||
// Step 2: clear alarm store and save new alarms to store.
|
||||
prepareAlarmStore(alarms, function(error, result) {
|
||||
var alarmId = result;
|
||||
// Step 3: remove the alarm.
|
||||
db.removeAlarm("network", alarmId, function(error, result) {
|
||||
// Step 4: check if the function call succeed.
|
||||
do_check_eq(result, true);
|
||||
|
||||
// Step 5: dump store to check if the alarm is removed.
|
||||
dumpStore("alarm_store", function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_true(Array.isArray(result));
|
||||
do_check_true(result.length === 0);
|
||||
|
||||
run_next_test(); // If success, run next test.
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test removing designate power alarm from store.
|
||||
add_test(function test_removePowerAlarm() {
|
||||
var manifestURL = "";
|
||||
var serviceType = "";
|
||||
var alarms = [];
|
||||
|
||||
// Step 1: generate one power alarm.
|
||||
alarms.push(generateAlarmObject("power", manifestURL, serviceType,
|
||||
cpuComponent));
|
||||
|
||||
// Step 2: clear alarm store and save new alarms to store.
|
||||
prepareAlarmStore(alarms, function(error, result) {
|
||||
var alarmId = result;
|
||||
// Step 3: remove the alarm.
|
||||
db.removeAlarm("power", alarmId, function(error, result) {
|
||||
// Step 4: check if the function call succeed.
|
||||
do_check_eq(result, true);
|
||||
|
||||
// Step 5: dump store to check if the alarm is removed.
|
||||
dumpStore("alarm_store", function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_true(Array.isArray(result));
|
||||
do_check_true(result.length === 0);
|
||||
|
||||
run_next_test(); // If success, run next test.
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test removing designate network alarm from store.
|
||||
add_test(function test_removeAllNetworkAlarms() {
|
||||
var manifestURL = "";
|
||||
var serviceType = "";
|
||||
var alarms = [];
|
||||
|
||||
// Step 1: generate two network alarms.
|
||||
alarms.push(generateAlarmObject("network", manifestURL, serviceType,
|
||||
wifiComponent));
|
||||
alarms.push(generateAlarmObject("network", manifestURL, serviceType,
|
||||
mobileComponent));
|
||||
|
||||
// Step 2: generate another power alarm.
|
||||
alarms.push(generateAlarmObject("power", manifestURL, serviceType,
|
||||
cpuComponent));
|
||||
|
||||
// Step 3: clear alarm store and save new alarms to store.
|
||||
prepareAlarmStore(alarms, function(error, result) {
|
||||
// Step 4: remove all network alarms.
|
||||
db.removeAllAlarms("network", function(error, result) {
|
||||
// Step 5: check if the function call succeed.
|
||||
do_check_eq(error, null);
|
||||
|
||||
// Step 6: dump store for comparison.
|
||||
// Because the power alarm should not be removed, so it would be the
|
||||
// only result returned by dumpStore.
|
||||
var alarm = alarms.pop(); // The expected result.
|
||||
dumpStore("alarm_store", function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_true(Array.isArray(result));
|
||||
do_check_true(result.length == 1);
|
||||
do_check_eq(result[0].type, alarm.type);
|
||||
do_check_eq(result[0].manifestURL, alarm.manifestURL);
|
||||
do_check_eq(result[0].serviceType, alarm.serviceType);
|
||||
do_check_eq(result[0].component, alarm.component);
|
||||
do_check_eq(result[0].threshold, alarm.threshold);
|
||||
do_check_eq(result[0].startTime, alarm.startTime);
|
||||
do_check_eq(result[0].data, alarm.data);
|
||||
do_check_eq(result[0].alarmId, alarm.alarmId);
|
||||
|
||||
run_next_test(); // If success, run next test.
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test removing designate power alarm from store.
|
||||
add_test(function test_removeAllPowerAlarms() {
|
||||
var manifestURL = "";
|
||||
var serviceType = "";
|
||||
var alarms = [];
|
||||
|
||||
// Step 1: generate two power alarms.
|
||||
alarms.push(generateAlarmObject("power", manifestURL, serviceType,
|
||||
cpuComponent));
|
||||
alarms.push(generateAlarmObject("power", manifestURL, serviceType,
|
||||
gpsComponent));
|
||||
|
||||
// Step 2: generate another network alarm.
|
||||
alarms.push(generateAlarmObject("network", manifestURL, serviceType,
|
||||
wifiComponent));
|
||||
|
||||
// Step 3: clear alarm store and save new alarms to store.
|
||||
prepareAlarmStore(alarms, function(error, result) {
|
||||
// Step 4: remove all power alarms.
|
||||
db.removeAllAlarms("power", function(error, result) {
|
||||
// Step 5: check if the function call succeed.
|
||||
do_check_eq(error, null);
|
||||
|
||||
// Step 6: dump store for comparison.
|
||||
// Because the network alarm should not be removed, so it would be the
|
||||
// only result returned by dumpStore.
|
||||
var alarm = alarms.pop(); // The expected result.
|
||||
dumpStore("alarm_store", function(error, result) {
|
||||
do_check_eq(error, null);
|
||||
do_check_true(Array.isArray(result));
|
||||
do_check_true(result.length == 1);
|
||||
do_check_eq(result[0].type, alarm.type);
|
||||
do_check_eq(result[0].manifestURL, alarm.manifestURL);
|
||||
do_check_eq(result[0].serviceType, alarm.serviceType);
|
||||
do_check_eq(result[0].component, alarm.component);
|
||||
do_check_eq(result[0].threshold, alarm.threshold);
|
||||
do_check_eq(result[0].startTime, alarm.startTime);
|
||||
do_check_eq(result[0].data, alarm.data);
|
||||
do_check_eq(result[0].alarmId, alarm.alarmId);
|
||||
|
||||
run_next_test(); // If success, run next test.
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
run_next_test();
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
[DEFAULT]
|
||||
head =
|
||||
tail =
|
||||
|
||||
[test_resourcestats_db.js]
|
|
@ -564,6 +564,8 @@ var interfaceNamesInGlobalScope =
|
|||
"HTMLUnknownElement",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"HTMLVideoElement",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IdleDeadline",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IDBCursor",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
/* -*- Mode: IDL; 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/.
|
||||
*
|
||||
* The origin of this IDL file is:
|
||||
* https://w3c.github.io/requestidlecallback/
|
||||
*/
|
||||
|
||||
[Pref="dom.requestIdleCallback.enabled"]
|
||||
interface IdleDeadline {
|
||||
DOMHighResTimeStamp timeRemaining();
|
||||
readonly attribute boolean didTimeout;
|
||||
};
|
|
@ -409,6 +409,3 @@ partial interface Navigator {
|
|||
interface NavigatorConcurrentHardware {
|
||||
readonly attribute unsigned long long hardwareConcurrency;
|
||||
};
|
||||
|
||||
partial interface Navigator {
|
||||
};
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
/* -*- Mode: IDL; 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/.
|
||||
*/
|
||||
|
||||
[ChromeOnly,
|
||||
Pref="dom.resource_stats.enabled",
|
||||
JSImplementation="@mozilla.org/networkStatsData;1"]
|
||||
interface NetworkStatsData
|
||||
{
|
||||
readonly attribute unsigned long long receivedBytes;
|
||||
readonly attribute unsigned long long sentBytes;
|
||||
readonly attribute DOMTimeStamp timestamp; // timestamp of the record
|
||||
};
|
||||
|
||||
[ChromeOnly,
|
||||
Pref="dom.resource_stats.enabled",
|
||||
JSImplementation="@mozilla.org/powerStatsData;1"]
|
||||
interface PowerStatsData
|
||||
{
|
||||
readonly attribute unsigned long long consumedPower; // unit: mW
|
||||
readonly attribute DOMTimeStamp timestamp; // timestamp of the record
|
||||
};
|
||||
|
||||
[ChromeOnly,
|
||||
Pref="dom.resource_stats.enabled",
|
||||
JSImplementation="@mozilla.org/resourceStats;1"]
|
||||
interface ResourceStats
|
||||
{
|
||||
/**
|
||||
* Type of statistics/
|
||||
*/
|
||||
readonly attribute ResourceType type;
|
||||
|
||||
/**
|
||||
* The |component| specifies statistics belongs to. This will be null if
|
||||
* the ResourceStatsOptions.component argument passed to getStats() is null.
|
||||
*/
|
||||
readonly attribute DOMString? component;
|
||||
|
||||
/**
|
||||
* |serviceType| specifies the system service. This will be null if the
|
||||
* ResourceStatsOptions.serviceType argument passed to getStats() is null.
|
||||
*/
|
||||
readonly attribute SystemService? serviceType;
|
||||
|
||||
/**
|
||||
* |manifestURL| specifies the manifestURL of an application. This will be
|
||||
* null if the ResourceStatsOptions.manifestURL argument passed to getStats()
|
||||
* is null.
|
||||
*/
|
||||
readonly attribute DOMString? manifestURL;
|
||||
|
||||
/**
|
||||
* Statistics, one element per day
|
||||
*/
|
||||
sequence<(NetworkStatsData or PowerStatsData)> getData();
|
||||
|
||||
/**
|
||||
* Date range
|
||||
*/
|
||||
readonly attribute DOMTimeStamp start; // start timestamp
|
||||
readonly attribute DOMTimeStamp end; // end timestamp
|
||||
};
|
|
@ -1,226 +0,0 @@
|
|||
/* -*- Mode: IDL; 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/.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Supported resource statistics
|
||||
*/
|
||||
enum ResourceType {
|
||||
"network",
|
||||
"power"
|
||||
};
|
||||
|
||||
/**
|
||||
* List of system services supporting resource statistics
|
||||
*/
|
||||
enum SystemService {
|
||||
"ota",
|
||||
"tethering"
|
||||
};
|
||||
|
||||
dictionary ResourceStatsOptions
|
||||
{
|
||||
/**
|
||||
* |component| specifies which component's resource usage will be returned.
|
||||
* If null or undefined, sum of all components' usage is returned.
|
||||
*
|
||||
* |component| is expressed in "<component>:<id>", where <component> is the
|
||||
* name of the component and <id> is used to identify different entities.
|
||||
*
|
||||
* The <id> field is mainly used in specifying the identity of different SIMs
|
||||
* when quering mobile network usage, e.g. "mobile:<iccid>".
|
||||
*
|
||||
* Quering statistics of other components should specify the |component| to
|
||||
* "<component>:0", such as "cpu:0" or "wifi:0".
|
||||
*/
|
||||
DOMString? component = null;
|
||||
|
||||
/**
|
||||
* |manifestURL| specifies the manifestURL of an application.
|
||||
* |systemService| specifies the system service.
|
||||
*
|
||||
* If both |systemService| and |manifestURL| are null or undefined, then a
|
||||
* system-wide resource statistics is returned.
|
||||
*
|
||||
* If |manifestURL| is specified, then the resource statistics of the
|
||||
* specified application is returned.
|
||||
*
|
||||
* If |systemService| is specified, then the resource statistics of the
|
||||
* specified system service is returned.
|
||||
*
|
||||
* If |systemService| and |manifestURL| are both specified, then the return
|
||||
* statistics indicates the resources that the system service consumed for
|
||||
* the application.
|
||||
*/
|
||||
SystemService? serviceType = null;
|
||||
DOMString? manifestURL = null;
|
||||
};
|
||||
|
||||
dictionary ResourceStatsAlarmOptions
|
||||
{
|
||||
/**
|
||||
* |startTime| indicates the start time of counting the resource usage.
|
||||
*
|
||||
* |data| is used to reflect in the alarm object when the alarm is triggered.
|
||||
* |data| should be copied using the structured clone algorithm.
|
||||
*/
|
||||
[EnforceRange] DOMTimeStamp startTime; // time in milliseconds since Epoch
|
||||
any data;
|
||||
};
|
||||
|
||||
[ChromeOnly,
|
||||
Pref="dom.resource_stats.enabled",
|
||||
JSImplementation="@mozilla.org/resourceStatsAlarm;1"]
|
||||
interface ResourceStatsAlarm
|
||||
{
|
||||
/**
|
||||
* ID of the alarm
|
||||
*/
|
||||
readonly attribute unsigned long alarmId;
|
||||
|
||||
/**
|
||||
* Type of resource this alarm monitor
|
||||
*/
|
||||
readonly attribute ResourceType type;
|
||||
|
||||
/**
|
||||
* The target component this alarm monitor.
|
||||
*/
|
||||
readonly attribute DOMString? component;
|
||||
|
||||
/**
|
||||
* |manifestURL| specifies the manifestURL of an application.
|
||||
* |systemService| specifies the system service.
|
||||
*
|
||||
* Both attributes are null means that this alarm monitors a system-wide
|
||||
* resource usage.
|
||||
*/
|
||||
readonly attribute SystemService? serviceType;
|
||||
readonly attribute DOMString? manifestURL;
|
||||
|
||||
/**
|
||||
* |threshold| specifies the limit of resource usage.
|
||||
*/
|
||||
readonly attribute unsigned long long threshold;
|
||||
|
||||
/**
|
||||
* |data| is used to reflect in the alarm object when the alarm is triggered.
|
||||
*/
|
||||
readonly attribute any data;
|
||||
};
|
||||
|
||||
[ChromeOnly,
|
||||
Pref="dom.resource_stats.enabled",
|
||||
Constructor(ResourceType type),
|
||||
JSImplementation="@mozilla.org/resourceStatsManager;1"]
|
||||
interface ResourceStatsManager
|
||||
{
|
||||
/**
|
||||
* Query resource statistics.
|
||||
*
|
||||
* |statsOptions| specifies the detail of statistics of interest.
|
||||
*
|
||||
* |start| and |end| specifies the time range of interest, both included.
|
||||
* If |start| is null or undefined, retrieve the stats since measurements.
|
||||
* If |end| is null or undefined. retrieve the stats until the current time.
|
||||
*
|
||||
* If success, the fulfillment value is a ResourceStats object.
|
||||
*/
|
||||
Promise<ResourceStats> getStats(optional ResourceStatsOptions statsOptions,
|
||||
[EnforceRange] optional DOMTimeStamp? start = null,
|
||||
[EnforceRange] optional DOMTimeStamp? end = null);
|
||||
|
||||
/**
|
||||
* Clear resource statistics stored in database.
|
||||
*
|
||||
* |statsOptions| specifies the detail of statistics to delete.
|
||||
*
|
||||
* |start| and |end| specifies the time range of interest, both included.
|
||||
* If |start| is null or undefined, delete the stats since measurements.
|
||||
* If |end| is null or undefined. delete the stats until the current time.
|
||||
*/
|
||||
// XXXbz What is this promise resolved with?
|
||||
Promise<any> clearStats(optional ResourceStatsOptions statsOptions,
|
||||
[EnforceRange] optional DOMTimeStamp? start = null,
|
||||
[EnforceRange] optional DOMTimeStamp? end = null);
|
||||
|
||||
/**
|
||||
* Clear all resource statistics stored in database.
|
||||
*/
|
||||
// XXXbz What is this promise resolved with?
|
||||
Promise<any> clearAllStats();
|
||||
|
||||
/**
|
||||
* Install an alarm to monitor resource usage.
|
||||
*
|
||||
* The |threshold| specifies the limit of resource usage. When resource
|
||||
* usage reaches the threshold, a "resourceStats-alarm" system message
|
||||
* is sent to the application.
|
||||
*
|
||||
* |statsOptions| specifies the detail of statistics of interest.
|
||||
*
|
||||
* |alarmOptions| is a ResourceStatsAlarmOptions object.
|
||||
*
|
||||
* If success, the fulfillment value is an alarm ID.
|
||||
*/
|
||||
Promise<unsigned long> addAlarm([EnforceRange] unsigned long long threshold,
|
||||
optional ResourceStatsOptions statsOptions,
|
||||
optional ResourceStatsAlarmOptions alarmOptions);
|
||||
|
||||
/**
|
||||
* Obtain alarms.
|
||||
*
|
||||
* If |statsOptions| is provided, then only the alarms monitoring that
|
||||
* resource are returned. Otherwise, all alarms set for this resource type
|
||||
* is returned.
|
||||
*
|
||||
* If success, the fulfillment value is an array of ResourceStatsAlarm.
|
||||
*/
|
||||
Promise<sequence<ResourceStatsAlarm>> getAlarms(optional ResourceStatsOptions statsOptions);
|
||||
|
||||
/**
|
||||
* Remove the specified alarm.
|
||||
*
|
||||
* |alarmId| specifies the alarm to be removed.
|
||||
*/
|
||||
// XXXbz What is this promise resolved with?
|
||||
Promise<any> removeAlarm([EnforceRange] unsigned long alarmId);
|
||||
|
||||
/**
|
||||
* Remove all alarms.
|
||||
*/
|
||||
// XXXbz What is this promise resolved with?
|
||||
Promise<any> removeAllAlarms();
|
||||
|
||||
/**
|
||||
* Enumerate components that have stored statistics in database.
|
||||
*
|
||||
* If success, the fulfillment value is an array of DOMString.
|
||||
*/
|
||||
Promise<sequence<DOMString>> getAvailableComponents();
|
||||
|
||||
/**
|
||||
* Return supporting resource statistics, i.e. ["Network", "Power"]
|
||||
*
|
||||
* This should be specified as static attribute after Bug 863952 is resolved.
|
||||
*/
|
||||
[Cached, Pure]
|
||||
readonly attribute sequence<DOMString> resourceTypes;
|
||||
|
||||
/**
|
||||
* Time in milliseconds between statistics stored in database.
|
||||
*
|
||||
* This should be specified as static attribute after Bug 863952 is resolved.
|
||||
*/
|
||||
readonly attribute unsigned long sampleRate;
|
||||
|
||||
/**
|
||||
* Time in milliseconds recorded by the API until present time. All
|
||||
* statistics older than maxStorageAge from now are deleted.
|
||||
*
|
||||
* This should be specified as static attribute after Bug 863952 is resolved.
|
||||
*/
|
||||
readonly attribute unsigned long long maxStorageAge;
|
||||
};
|
|
@ -14,6 +14,7 @@
|
|||
* https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html
|
||||
* http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
|
||||
* https://w3c.github.io/webappsec-secure-contexts/#monkey-patching-global-object
|
||||
* https://w3c.github.io/requestidlecallback/
|
||||
*/
|
||||
|
||||
interface ApplicationCache;
|
||||
|
@ -490,3 +491,17 @@ partial interface Window {
|
|||
|
||||
Window implements ChromeWindow;
|
||||
Window implements WindowOrWorkerGlobalScope;
|
||||
|
||||
partial interface Window {
|
||||
[Throws, Pref="dom.requestIdleCallback.enabled"]
|
||||
unsigned long requestIdleCallback(IdleRequestCallback callback,
|
||||
optional IdleRequestOptions options);
|
||||
[Pref="dom.requestIdleCallback.enabled"]
|
||||
void cancelIdleCallback(unsigned long handle);
|
||||
};
|
||||
|
||||
dictionary IdleRequestOptions {
|
||||
unsigned long timeout;
|
||||
};
|
||||
|
||||
callback IdleRequestCallback = void (IdleDeadline deadline);
|
||||
|
|
|
@ -270,6 +270,7 @@ WEBIDL_FILES = [
|
|||
'IDBRequest.webidl',
|
||||
'IDBTransaction.webidl',
|
||||
'IDBVersionChangeEvent.webidl',
|
||||
'IdleDeadline.webidl',
|
||||
'IIRFilterNode.webidl',
|
||||
'ImageBitmap.webidl',
|
||||
'ImageBitmapRenderingContext.webidl',
|
||||
|
@ -400,8 +401,6 @@ WEBIDL_FILES = [
|
|||
'Range.webidl',
|
||||
'Rect.webidl',
|
||||
'Request.webidl',
|
||||
'ResourceStats.webidl',
|
||||
'ResourceStatsManager.webidl',
|
||||
'Response.webidl',
|
||||
'RGBColor.webidl',
|
||||
'RTCStatsReport.webidl',
|
||||
|
|
|
@ -151,6 +151,13 @@ using namespace mozilla::gfx;
|
|||
#define FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME "layout.css.float-logical-values.enabled"
|
||||
#define BG_CLIP_TEXT_ENABLED_PREF_NAME "layout.css.background-clip-text.enabled"
|
||||
|
||||
// The time in number of frames that we estimate for a refresh driver
|
||||
// to be quiescent
|
||||
#define DEFAULT_QUIESCENT_FRAMES 2
|
||||
// The time (milliseconds) we estimate is needed between the end of an
|
||||
// idle time and the next Tick.
|
||||
#define DEFAULT_IDLE_PERIOD_TIME_LIMIT 3.0f
|
||||
|
||||
#ifdef DEBUG
|
||||
// TODO: remove, see bug 598468.
|
||||
bool nsLayoutUtils::gPreventAssertInCompareTreePosition = false;
|
||||
|
@ -174,6 +181,8 @@ typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox;
|
|||
#ifdef MOZ_STYLO
|
||||
/* static */ bool nsLayoutUtils::sStyloEnabled;
|
||||
#endif
|
||||
/* static */ uint32_t nsLayoutUtils::sIdlePeriodDeadlineLimit;
|
||||
/* static */ uint32_t nsLayoutUtils::sQuiescentFramesBeforeIdlePeriod;
|
||||
|
||||
static ViewID sScrollIdCounter = FrameMetrics::START_SCROLL_ID;
|
||||
|
||||
|
@ -7871,6 +7880,12 @@ nsLayoutUtils::Initialize()
|
|||
Preferences::AddBoolVarCache(&sStyloEnabled,
|
||||
"layout.css.servo.enabled");
|
||||
#endif
|
||||
Preferences::AddUintVarCache(&sIdlePeriodDeadlineLimit,
|
||||
"layout.idle_period.time_limit",
|
||||
DEFAULT_IDLE_PERIOD_TIME_LIMIT);
|
||||
Preferences::AddUintVarCache(&sQuiescentFramesBeforeIdlePeriod,
|
||||
"layout.idle_period.required_quiescent_frames",
|
||||
DEFAULT_QUIESCENT_FRAMES);
|
||||
|
||||
for (auto& callback : kPrefCallbacks) {
|
||||
Preferences::RegisterCallbackAndCall(callback.func, callback.name);
|
||||
|
|
|
@ -2429,6 +2429,14 @@ public:
|
|||
#endif
|
||||
}
|
||||
|
||||
static uint32_t IdlePeriodDeadlineLimit() {
|
||||
return sIdlePeriodDeadlineLimit;
|
||||
}
|
||||
|
||||
static uint32_t QuiescentFramesBeforeIdlePeriod() {
|
||||
return sQuiescentFramesBeforeIdlePeriod;
|
||||
}
|
||||
|
||||
/**
|
||||
* See comment above "font.size.inflation.mappingIntercept" in
|
||||
* modules/libpref/src/init/all.js .
|
||||
|
@ -2885,6 +2893,8 @@ private:
|
|||
#ifdef MOZ_STYLO
|
||||
static bool sStyloEnabled;
|
||||
#endif
|
||||
static uint32_t sIdlePeriodDeadlineLimit;
|
||||
static uint32_t sQuiescentFramesBeforeIdlePeriod;
|
||||
|
||||
/**
|
||||
* Helper function for LogTestDataForPaint().
|
||||
|
|
|
@ -138,6 +138,7 @@ class RefreshDriverTimer {
|
|||
public:
|
||||
RefreshDriverTimer()
|
||||
: mLastFireEpoch(0)
|
||||
, mLastFireSkipped(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -221,6 +222,35 @@ public:
|
|||
aNewTimer->mLastFireTime = mLastFireTime;
|
||||
}
|
||||
|
||||
virtual TimeDuration GetTimerRate() = 0;
|
||||
|
||||
bool LastTickSkippedAnyPaints() const
|
||||
{
|
||||
return mLastFireSkipped;
|
||||
}
|
||||
|
||||
Maybe<TimeStamp> GetIdleDeadlineHint()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (LastTickSkippedAnyPaints()) {
|
||||
return Some(TimeStamp());
|
||||
}
|
||||
|
||||
TimeStamp mostRecentRefresh = MostRecentRefresh();
|
||||
TimeDuration refreshRate = GetTimerRate();
|
||||
TimeStamp idleEnd = mostRecentRefresh + refreshRate;
|
||||
|
||||
if (idleEnd +
|
||||
refreshRate * nsLayoutUtils::QuiescentFramesBeforeIdlePeriod() <
|
||||
TimeStamp::Now()) {
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
return Some(idleEnd - TimeDuration::FromMilliseconds(
|
||||
nsLayoutUtils::IdlePeriodDeadlineLimit()));
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void StartTimer() = 0;
|
||||
virtual void StopTimer() = 0;
|
||||
|
@ -262,6 +292,8 @@ protected:
|
|||
}
|
||||
|
||||
TickDriver(driver, aJsNow, aNow);
|
||||
|
||||
mLastFireSkipped = mLastFireSkipped || driver->mSkippedPaints;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,6 +306,7 @@ protected:
|
|||
|
||||
mLastFireEpoch = jsnow;
|
||||
mLastFireTime = now;
|
||||
mLastFireSkipped = false;
|
||||
|
||||
LOG("[%p] ticking drivers...", this);
|
||||
// RD is short for RefreshDriver
|
||||
|
@ -293,6 +326,7 @@ protected:
|
|||
}
|
||||
|
||||
int64_t mLastFireEpoch;
|
||||
bool mLastFireSkipped;
|
||||
TimeStamp mLastFireTime;
|
||||
TimeStamp mTargetTime;
|
||||
|
||||
|
@ -345,9 +379,14 @@ public:
|
|||
return mRateMilliseconds;
|
||||
}
|
||||
|
||||
virtual TimeDuration GetTimerRate() override
|
||||
{
|
||||
return mRateDuration;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
virtual void StartTimer()
|
||||
virtual void StartTimer() override
|
||||
{
|
||||
// pretend we just fired, and we schedule the next tick normally
|
||||
mLastFireEpoch = JS_Now();
|
||||
|
@ -359,7 +398,7 @@ protected:
|
|||
mTimer->InitWithFuncCallback(TimerTick, this, delay, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
virtual void StopTimer()
|
||||
virtual void StopTimer() override
|
||||
{
|
||||
mTimer->Cancel();
|
||||
}
|
||||
|
@ -386,6 +425,7 @@ public:
|
|||
RefPtr<mozilla::gfx::VsyncSource> vsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync();
|
||||
MOZ_ALWAYS_TRUE(mVsyncDispatcher = vsyncSource->GetRefreshTimerVsyncDispatcher());
|
||||
mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
|
||||
mVsyncRate = vsyncSource->GetGlobalDisplay().GetVsyncRate();
|
||||
}
|
||||
|
||||
explicit VsyncRefreshDriverTimer(VsyncChild* aVsyncChild)
|
||||
|
@ -396,6 +436,23 @@ public:
|
|||
MOZ_ASSERT(mVsyncChild);
|
||||
mVsyncObserver = new RefreshDriverVsyncObserver(this);
|
||||
mVsyncChild->SetVsyncObserver(mVsyncObserver);
|
||||
mVsyncRate = mVsyncChild->GetVsyncRate();
|
||||
}
|
||||
|
||||
virtual TimeDuration GetTimerRate() override
|
||||
{
|
||||
if (mVsyncRate != TimeDuration::Forever()) {
|
||||
return mVsyncRate;
|
||||
}
|
||||
|
||||
if (mVsyncChild) {
|
||||
mVsyncRate = mVsyncChild->GetVsyncRate();
|
||||
}
|
||||
|
||||
// If hardware queries fail / are unsupported, we have to just guess.
|
||||
return mVsyncRate != TimeDuration::Forever()
|
||||
? mVsyncRate
|
||||
: TimeDuration::FromMilliseconds(1000.0 / 60.0);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -458,7 +515,6 @@ private:
|
|||
mLastChildTick = TimeStamp::Now();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~RefreshDriverVsyncObserver() {}
|
||||
|
||||
|
@ -613,6 +669,7 @@ private:
|
|||
// The mVsyncChild will be always available before VsncChild::ActorDestroy().
|
||||
// After ActorDestroy(), StartTimer() and StopTimer() calls will be non-op.
|
||||
RefPtr<VsyncChild> mVsyncChild;
|
||||
TimeDuration mVsyncRate;
|
||||
}; // VsyncRefreshDriverTimer
|
||||
|
||||
/**
|
||||
|
@ -677,7 +734,7 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
virtual void AddRefreshDriver(nsRefreshDriver* aDriver)
|
||||
virtual void AddRefreshDriver(nsRefreshDriver* aDriver) override
|
||||
{
|
||||
RefreshDriverTimer::AddRefreshDriver(aDriver);
|
||||
|
||||
|
@ -695,13 +752,18 @@ public:
|
|||
StartTimer();
|
||||
}
|
||||
|
||||
virtual TimeDuration GetTimerRate() override
|
||||
{
|
||||
return TimeDuration::FromMilliseconds(mNextTickDuration);
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t GetRefreshDriverCount()
|
||||
{
|
||||
return mContentRefreshDrivers.Length() + mRootRefreshDrivers.Length();
|
||||
}
|
||||
|
||||
virtual void StartTimer()
|
||||
virtual void StartTimer() override
|
||||
{
|
||||
mLastFireEpoch = JS_Now();
|
||||
mLastFireTime = TimeStamp::Now();
|
||||
|
@ -712,12 +774,12 @@ protected:
|
|||
mTimer->InitWithFuncCallback(TimerTickOne, this, delay, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
virtual void StopTimer()
|
||||
virtual void StopTimer() override
|
||||
{
|
||||
mTimer->Cancel();
|
||||
}
|
||||
|
||||
virtual void ScheduleNextTick(TimeStamp aNowTime)
|
||||
virtual void ScheduleNextTick(TimeStamp aNowTime) override
|
||||
{
|
||||
if (mDisableAfterMilliseconds > 0.0 &&
|
||||
mNextTickDuration > mDisableAfterMilliseconds)
|
||||
|
@ -752,6 +814,7 @@ protected:
|
|||
|
||||
mLastFireEpoch = jsnow;
|
||||
mLastFireTime = now;
|
||||
mLastFireSkipped = false;
|
||||
|
||||
nsTArray<RefPtr<nsRefreshDriver> > drivers(mContentRefreshDrivers);
|
||||
drivers.AppendElements(mRootRefreshDrivers);
|
||||
|
@ -760,6 +823,7 @@ protected:
|
|||
!drivers[mNextDriverIndex]->IsTestControllingRefreshesEnabled())
|
||||
{
|
||||
TickDriver(drivers[mNextDriverIndex], jsnow, now);
|
||||
mLastFireSkipped = mLastFireSkipped || drivers[mNextDriverIndex]->SkippedPaints();
|
||||
}
|
||||
|
||||
mNextDriverIndex++;
|
||||
|
@ -2228,6 +2292,24 @@ nsRefreshDriver::CancelPendingEvents(nsIDocument* aDocument)
|
|||
}
|
||||
}
|
||||
|
||||
/* static */ Maybe<TimeStamp>
|
||||
nsRefreshDriver::GetIdleDeadlineHint()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!sRegularRateTimer) {
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
// For computing idleness of refresh drivers we only care about
|
||||
// sRegularRateTimer, since we consider refresh drivers attached to
|
||||
// sThrottledRateTimer to be inactive. This implies that tasks
|
||||
// resulting from a tick on the sRegularRateTimer counts as being
|
||||
// busy but tasks resulting from a tick on sThrottledRateTimer
|
||||
// counts as being idle.
|
||||
return sRegularRateTimer->GetIdleDeadlineHint();
|
||||
}
|
||||
|
||||
void
|
||||
nsRefreshDriver::Disconnect()
|
||||
{
|
||||
|
|
|
@ -329,6 +329,47 @@ public:
|
|||
NS_IMETHOD_(MozExternalRefCountType) Release(void) override { return TransactionIdAllocator::Release(); }
|
||||
virtual void WillRefresh(mozilla::TimeStamp aTime) override;
|
||||
|
||||
/**
|
||||
* Compute the time when the currently active refresh driver timer
|
||||
* will start its next tick.
|
||||
*
|
||||
* Returns 'Nothing' if the refresh driver timer hasn't been
|
||||
* initialized or if we can't tell when the next tick will happen.
|
||||
*
|
||||
* Returns Some(TimeStamp()), i.e. the null time, if the next tick is late.
|
||||
*
|
||||
* Otherwise returns Some(TimeStamp(t)), where t is the time of the next tick.
|
||||
*
|
||||
* Using these three types of return values it is possible to
|
||||
* estimate three different things about the idleness of the
|
||||
* currently active group of refresh drivers. This information is
|
||||
* used by nsThread to schedule lower priority "idle tasks".
|
||||
*
|
||||
* The 'Nothing' return value indicates to nsThread that the
|
||||
* currently active refresh drivers will be idle for a time
|
||||
* significantly longer than the current refresh rate and that it is
|
||||
* free to schedule longer periods for executing idle tasks. This is the
|
||||
* expected result when we aren't animating.
|
||||
|
||||
* Returning the null time indicates to nsThread that we are very
|
||||
* busy and that it should definitely not schedule idle tasks at
|
||||
* all. This is the expected result when we are animating, but
|
||||
* aren't able to keep up with the animation and hence need to skip
|
||||
* paints. Since catching up to missed paints will happen as soon as
|
||||
* possible, this is the expected result if any of the refresh
|
||||
* drivers attached to the current refresh driver misses a paint.
|
||||
*
|
||||
* Returning Some(TimeStamp(t)) indicates to nsThread that we will
|
||||
* be idle until. This is usually the case when we're animating
|
||||
* without skipping paints.
|
||||
*/
|
||||
static mozilla::Maybe<mozilla::TimeStamp> GetIdleDeadlineHint();
|
||||
|
||||
bool SkippedPaints() const
|
||||
{
|
||||
return mSkippedPaints;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef nsTObserverArray<nsARefreshObserver*> ObserverArray;
|
||||
typedef nsTHashtable<nsISupportsHashKey> RequestTable;
|
||||
|
@ -345,7 +386,6 @@ private:
|
|||
void DispatchPendingEvents();
|
||||
void DispatchAnimationEvents();
|
||||
void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
|
||||
|
||||
void Tick(int64_t aNowEpoch, mozilla::TimeStamp aNowTime);
|
||||
|
||||
enum EnsureTimerStartedFlags {
|
||||
|
|
|
@ -476,7 +476,6 @@ $(1): $$(call mkdir_deps,$(filter-out ./,$(dir $(3) $(4) $(5)))) $(2)
|
|||
$(if $(ANDROID_EXTRA_PACKAGES),--extra-packages $$(subst $$(NULL) ,:,$$(strip $$(ANDROID_EXTRA_PACKAGES)))) \
|
||||
$(if $(ANDROID_EXTRA_RES_DIRS),$$(addprefix -S ,$$(ANDROID_EXTRA_RES_DIRS))) \
|
||||
--custom-package org.mozilla.gecko \
|
||||
--non-constant-id \
|
||||
--no-version-vectors \
|
||||
-F $(3) \
|
||||
-J $(4) \
|
||||
|
|
|
@ -77,7 +77,7 @@ public abstract class HomeFragment extends Fragment {
|
|||
protected OnUrlOpenListener mUrlOpenListener;
|
||||
|
||||
// Helper for opening a tab in the background.
|
||||
private OnUrlOpenInBackgroundListener mUrlOpenInBackgroundListener;
|
||||
protected OnUrlOpenInBackgroundListener mUrlOpenInBackgroundListener;
|
||||
|
||||
protected PanelStateChangeListener mPanelStateChangeListener = null;
|
||||
|
||||
|
|
|
@ -64,8 +64,8 @@ public class ActivityStream extends FrameLayout {
|
|||
tileMargin = resources.getDimensionPixelSize(R.dimen.activity_stream_base_margin);
|
||||
}
|
||||
|
||||
void setOnUrlOpenListener(HomePager.OnUrlOpenListener listener) {
|
||||
adapter.setOnUrlOpenListener(listener);
|
||||
void setOnUrlOpenListeners(HomePager.OnUrlOpenListener onUrlOpenListener, HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener) {
|
||||
adapter.setOnUrlOpenListeners(onUrlOpenListener, onUrlOpenInBackgroundListener);
|
||||
}
|
||||
|
||||
public void load(LoaderManager lm) {
|
||||
|
|
|
@ -31,7 +31,7 @@ public class ActivityStreamHomeFragment
|
|||
@Nullable Bundle savedInstanceState) {
|
||||
if (activityStream == null) {
|
||||
activityStream = (ActivityStream) inflater.inflate(R.layout.activity_stream, container, false);
|
||||
activityStream.setOnUrlOpenListener(mUrlOpenListener);
|
||||
activityStream.setOnUrlOpenListeners(mUrlOpenListener, mUrlOpenInBackgroundListener);
|
||||
}
|
||||
|
||||
return activityStream;
|
||||
|
|
|
@ -7,12 +7,14 @@ package org.mozilla.gecko.home.activitystream;
|
|||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.TouchDelegate;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
@ -21,6 +23,7 @@ import android.widget.TextView;
|
|||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
import org.mozilla.gecko.home.HomePager;
|
||||
import org.mozilla.gecko.home.activitystream.menu.ActivityStreamContextMenu;
|
||||
import org.mozilla.gecko.home.activitystream.topsites.CirclePageIndicator;
|
||||
import org.mozilla.gecko.home.activitystream.topsites.TopSitesPagerAdapter;
|
||||
import org.mozilla.gecko.icons.IconCallback;
|
||||
|
@ -71,6 +74,9 @@ public abstract class StreamItem extends RecyclerView.ViewHolder {
|
|||
public static class HighlightItem extends StreamItem implements IconCallback {
|
||||
public static final int LAYOUT_ID = R.layout.activity_stream_card_history_item;
|
||||
|
||||
String title;
|
||||
String url;
|
||||
|
||||
final FaviconView vIconView;
|
||||
final TextView vLabel;
|
||||
final TextView vTimeSince;
|
||||
|
@ -81,7 +87,9 @@ public abstract class StreamItem extends RecyclerView.ViewHolder {
|
|||
private Future<IconResponse> ongoingIconLoad;
|
||||
private int tilesMargin;
|
||||
|
||||
public HighlightItem(View itemView) {
|
||||
public HighlightItem(final View itemView,
|
||||
final HomePager.OnUrlOpenListener onUrlOpenListener,
|
||||
final HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener) {
|
||||
super(itemView);
|
||||
|
||||
tilesMargin = itemView.getResources().getDimensionPixelSize(R.dimen.activity_stream_base_margin);
|
||||
|
@ -93,18 +101,49 @@ public abstract class StreamItem extends RecyclerView.ViewHolder {
|
|||
vPageView = (TextView) itemView.findViewById(R.id.page);
|
||||
vSourceIconView = (ImageView) itemView.findViewById(R.id.source_icon);
|
||||
|
||||
ImageView menuButton = (ImageView) itemView.findViewById(R.id.menu);
|
||||
final ImageView menuButton = (ImageView) itemView.findViewById(R.id.menu);
|
||||
|
||||
menuButton.setImageDrawable(
|
||||
DrawableUtil.tintDrawable(menuButton.getContext(), R.drawable.menu, Color.LTGRAY));
|
||||
|
||||
itemView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Rect delegateArea = new Rect();
|
||||
menuButton.getHitRect(delegateArea);
|
||||
|
||||
final int targetHitArea = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, itemView.getContext().getResources().getDisplayMetrics());;
|
||||
|
||||
final int widthDelta = (targetHitArea - delegateArea.width()) / 2;
|
||||
delegateArea.right += widthDelta;
|
||||
delegateArea.left -= widthDelta;
|
||||
|
||||
final int heightDelta = (targetHitArea - delegateArea.height()) / 2;
|
||||
delegateArea.bottom += heightDelta;
|
||||
delegateArea.top -= heightDelta;
|
||||
|
||||
TouchDelegate touchDelegate = new TouchDelegate(delegateArea, menuButton);
|
||||
itemView.setTouchDelegate(touchDelegate);
|
||||
}
|
||||
});
|
||||
|
||||
menuButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ActivityStreamContextMenu.show(v.getContext(), title, url, onUrlOpenListener, onUrlOpenInBackgroundListener, vIconView.getWidth(), vIconView.getHeight());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void bind(Cursor cursor, int tilesWidth, int tilesHeight) {
|
||||
|
||||
final long time = cursor.getLong(cursor.getColumnIndexOrThrow(BrowserContract.Highlights.DATE));
|
||||
final String ago = DateUtils.getRelativeTimeSpanString(time, System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, 0).toString();
|
||||
final String url = cursor.getString(cursor.getColumnIndexOrThrow(BrowserContract.Combined.URL));
|
||||
|
||||
vLabel.setText(cursor.getString(cursor.getColumnIndexOrThrow(BrowserContract.History.TITLE)));
|
||||
title = cursor.getString(cursor.getColumnIndexOrThrow(BrowserContract.History.TITLE));
|
||||
url = cursor.getString(cursor.getColumnIndexOrThrow(BrowserContract.Combined.URL));
|
||||
|
||||
vLabel.setText(title);
|
||||
vTimeSince.setText(ago);
|
||||
|
||||
ViewGroup.LayoutParams layoutParams = vIconView.getLayoutParams();
|
||||
|
|
|
@ -24,13 +24,15 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamItem> impl
|
|||
private Cursor topSitesCursor;
|
||||
|
||||
private HomePager.OnUrlOpenListener onUrlOpenListener;
|
||||
private HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener;
|
||||
|
||||
private int tiles;
|
||||
private int tilesWidth;
|
||||
private int tilesHeight;
|
||||
|
||||
void setOnUrlOpenListener(HomePager.OnUrlOpenListener onUrlOpenListener) {
|
||||
void setOnUrlOpenListeners(HomePager.OnUrlOpenListener onUrlOpenListener, HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener) {
|
||||
this.onUrlOpenListener = onUrlOpenListener;
|
||||
this.onUrlOpenInBackgroundListener = onUrlOpenInBackgroundListener;
|
||||
}
|
||||
|
||||
public void setTileSize(int tiles, int tilesWidth, int tilesHeight) {
|
||||
|
@ -57,7 +59,7 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamItem> impl
|
|||
if (type == TopPanel.LAYOUT_ID) {
|
||||
return new TopPanel(inflater.inflate(type, parent, false), onUrlOpenListener);
|
||||
} else if (type == HighlightItem.LAYOUT_ID) {
|
||||
return new HighlightItem(inflater.inflate(type, parent, false));
|
||||
return new HighlightItem(inflater.inflate(type, parent, false), onUrlOpenListener, onUrlOpenInBackgroundListener);
|
||||
} else {
|
||||
throw new IllegalStateException("Missing inflation for ViewType " + type);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
package org.mozilla.gecko.home.activitystream.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.design.widget.BottomSheetBehavior;
|
||||
import android.support.design.widget.BottomSheetDialog;
|
||||
import android.support.design.widget.NavigationView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.IntentHelper;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.TelemetryContract;
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.home.HomePager;
|
||||
import org.mozilla.gecko.icons.IconCallback;
|
||||
import org.mozilla.gecko.icons.IconResponse;
|
||||
import org.mozilla.gecko.icons.Icons;
|
||||
import org.mozilla.gecko.util.Clipboard;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.util.UIAsyncTask;
|
||||
import org.mozilla.gecko.widget.FaviconView;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import static org.mozilla.gecko.activitystream.ActivityStream.extractLabel;
|
||||
|
||||
public class ActivityStreamContextMenu
|
||||
extends BottomSheetDialog
|
||||
implements NavigationView.OnNavigationItemSelectedListener {
|
||||
final Context context;
|
||||
|
||||
final String title;
|
||||
final String url;
|
||||
|
||||
final HomePager.OnUrlOpenListener onUrlOpenListener;
|
||||
final HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener;
|
||||
|
||||
boolean isAlreadyBookmarked = false;
|
||||
|
||||
private ActivityStreamContextMenu(final Context context, final String title, @NonNull final String url,
|
||||
HomePager.OnUrlOpenListener onUrlOpenListener,
|
||||
HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener,
|
||||
final int tilesWidth, final int tilesHeight) {
|
||||
super(context);
|
||||
|
||||
this.context = context;
|
||||
|
||||
this.title = title;
|
||||
this.url = url;
|
||||
this.onUrlOpenListener = onUrlOpenListener;
|
||||
this.onUrlOpenInBackgroundListener = onUrlOpenInBackgroundListener;
|
||||
|
||||
final LayoutInflater inflater = LayoutInflater.from(context);
|
||||
|
||||
final View content = inflater.inflate(R.layout.activity_stream_contextmenu_layout, null);
|
||||
setContentView(content);
|
||||
|
||||
((TextView) findViewById(R.id.title)).setText(title);
|
||||
final String label = extractLabel(url, false);
|
||||
((TextView) findViewById(R.id.url)).setText(label);
|
||||
|
||||
// Copy layouted parameters from the Highlights / TopSites items to ensure consistency
|
||||
final FaviconView faviconView = (FaviconView) findViewById(R.id.icon);
|
||||
ViewGroup.LayoutParams layoutParams = faviconView.getLayoutParams();
|
||||
layoutParams.width = tilesWidth;
|
||||
layoutParams.height = tilesHeight;
|
||||
faviconView.setLayoutParams(layoutParams);
|
||||
|
||||
Icons.with(context)
|
||||
.pageUrl(url)
|
||||
.skipNetwork()
|
||||
.build()
|
||||
.execute(new IconCallback() {
|
||||
@Override
|
||||
public void onIconResponse(IconResponse response) {
|
||||
faviconView.updateImage(response);
|
||||
}
|
||||
});
|
||||
|
||||
NavigationView navigationView = (NavigationView) findViewById(R.id.menu);
|
||||
navigationView.setNavigationItemSelectedListener(this);
|
||||
|
||||
// Disable the bookmark item until we know its bookmark state
|
||||
final MenuItem bookmarkItem = navigationView.getMenu().findItem(R.id.bookmark);
|
||||
bookmarkItem.setEnabled(false);
|
||||
|
||||
(new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
|
||||
@Override
|
||||
protected Void doInBackground() {
|
||||
isAlreadyBookmarked = BrowserDB.from(context).isBookmark(context.getContentResolver(), url);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
if (isAlreadyBookmarked) {
|
||||
bookmarkItem.setTitle(R.string.bookmark_remove);
|
||||
}
|
||||
|
||||
bookmarkItem.setEnabled(true);
|
||||
}
|
||||
}).execute();
|
||||
|
||||
// Only show the "remove from history" item if a page actually has history
|
||||
final MenuItem deleteHistoryItem = navigationView.getMenu().findItem(R.id.delete);
|
||||
deleteHistoryItem.setVisible(false);
|
||||
|
||||
|
||||
(new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
|
||||
boolean hasHistory;
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() {
|
||||
final Cursor cursor = BrowserDB.from(context).getHistoryForURL(context.getContentResolver(), url);
|
||||
try {
|
||||
if (cursor != null &&
|
||||
cursor.getCount() == 1) {
|
||||
hasHistory = true;
|
||||
} else {
|
||||
hasHistory = false;
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
if (hasHistory) {
|
||||
deleteHistoryItem.setVisible(true);
|
||||
}
|
||||
}
|
||||
}).execute();
|
||||
|
||||
BottomSheetBehavior<View> bsBehaviour = BottomSheetBehavior.from((View) content.getParent());
|
||||
bsBehaviour.setPeekHeight(context.getResources().getDimensionPixelSize(R.dimen.activity_stream_contextmenu_peek_height));
|
||||
}
|
||||
|
||||
public static ActivityStreamContextMenu show(Context context, final String title, @NonNull final String url,
|
||||
HomePager.OnUrlOpenListener onUrlOpenListener,
|
||||
HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener,
|
||||
final int tilesWidth, final int tilesHeight) {
|
||||
final ActivityStreamContextMenu menu = new ActivityStreamContextMenu(context, title, url,
|
||||
onUrlOpenListener, onUrlOpenInBackgroundListener,
|
||||
tilesWidth, tilesHeight);
|
||||
menu.show();
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNavigationItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.share:
|
||||
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST, "menu");
|
||||
IntentHelper.openUriExternal(url, "text/plain", "", "", Intent.ACTION_SEND, title, false);
|
||||
break;
|
||||
|
||||
case R.id.bookmark:
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final BrowserDB db = BrowserDB.from(context);
|
||||
|
||||
if (isAlreadyBookmarked) {
|
||||
db.removeBookmarksWithURL(context.getContentResolver(), url);
|
||||
} else {
|
||||
db.addBookmark(context.getContentResolver(), title, url);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case R.id.copy_url:
|
||||
Clipboard.setText(url);
|
||||
break;
|
||||
|
||||
case R.id.add_homescreen:
|
||||
GeckoAppShell.createShortcut(title, url);
|
||||
break;
|
||||
|
||||
case R.id.open_new_tab:
|
||||
onUrlOpenInBackgroundListener.onUrlOpenInBackground(url, EnumSet.noneOf(HomePager.OnUrlOpenInBackgroundListener.Flags.class));
|
||||
break;
|
||||
|
||||
case R.id.open_new_private_tab:
|
||||
onUrlOpenInBackgroundListener.onUrlOpenInBackground(url, EnumSet.of(HomePager.OnUrlOpenInBackgroundListener.Flags.PRIVATE));
|
||||
break;
|
||||
|
||||
case R.id.dismiss:
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
BrowserDB.from(context)
|
||||
.blockActivityStreamSite(context.getContentResolver(),
|
||||
url);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case R.id.delete:
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
BrowserDB.from(context)
|
||||
.removeHistoryEntry(context.getContentResolver(),
|
||||
url);
|
||||
}
|
||||
});
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Menu item with ID=" + item.getItemId() + " not handled");
|
||||
}
|
||||
|
||||
dismiss();
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -841,3 +841,8 @@ Stream list for highlights sourced from th user's bookmarks. -->
|
|||
Stream list for highlights sourced from th user's bookmarks. -->
|
||||
<!ENTITY activity_stream_highlight_label_visited "Visited">
|
||||
|
||||
<!-- LOCALIZATION NOTE (activity_stream_dismiss): This label is shown in the Activity Stream context menu,
|
||||
and allows hiding a URL/page from highlights or topsites. The page remains in history/bookmarks, but
|
||||
is simply hidden from the Activity Stream panel. -->
|
||||
<!ENTITY activity_stream_dismiss "Dismiss">
|
||||
<!ENTITY activity_stream_delete_history "Delete from History">
|
||||
|
|
|
@ -438,6 +438,7 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
|
|||
'home/activitystream/ActivityStream.java',
|
||||
'home/activitystream/ActivityStreamHomeFragment.java',
|
||||
'home/activitystream/ActivityStreamHomeScreen.java',
|
||||
'home/activitystream/menu/ActivityStreamContextMenu.java',
|
||||
'home/activitystream/StreamItem.java',
|
||||
'home/activitystream/StreamRecyclerAdapter.java',
|
||||
'home/activitystream/topsites/CirclePageIndicator.java',
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M20.75,5L16,5v-2.75a1.252,1.252 0,0 0,-1.25 -1.25h-5.5a1.252,1.252 0,0 0,-1.25 1.25L8,5h-4.75a1.25,1.25 0,0 0,0 2.5L5,7.5v14.25a1.252,1.252 0,0 0,1.25 1.25h11.5a1.252,1.252 0,0 0,1.25 -1.25L19,7.5h1.75A1.25,1.25 0,0 0,20.75 5ZM10.5,3.5h3v1.5h-3v-1.5ZM16.5,20.5h-9v-13h9v13ZM10.5,18h0a0.5,0.5 0,0 1,-0.5 -0.5v-7a0.5,0.5 0,0 1,0.5 -0.5h0a0.5,0.5 0,0 1,0.5 0.5v7A0.5,0.5 0,0 1,10.5 18ZM13.5,18h0a0.5,0.5 0,0 1,-0.5 -0.5v-7a0.5,0.5 0,0 1,0.5 -0.5h0a0.5,0.5 0,0 1,0.5 0.5v7A0.5,0.5 0,0 1,13.5 18Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12.037,5.333l1.706,3.361 0.569,1.121 1.239,0.211 3.617,0.619 -2.617,2.851 -0.81,0.884 0.181,1.185 0.583,3.8 -3.312,-1.758 -1.179,-0.626 -1.176,0.632 -3.232,1.736 0.582,-3.788 0.184,-1.194 -0.822,-0.886 -2.633,-2.84 3.676,-0.619 1.272,-0.214 0.563,-1.161 1.609,-3.319M12.01,1a1.335,1.335 0,0 0,-1.085 0.895l-2.747,5.667 -5.877,0.99c-1.345,0.219 -1.679,1.186 -0.744,2.148l4.16,4.486 -0.969,6.311c-0.147,0.948 0.242,1.5 0.925,1.5a2,2 0,0 0,0.891 -0.249l5.457,-2.931 5.521,2.931a2,2 0,0 0,0.892 0.249c0.683,0 1.07,-0.555 0.926,-1.5l-0.966,-6.311 4.111,-4.481c0.936,-0.966 0.6,-1.934 -0.744,-2.153l-5.789,-0.99L13.1,1.9A1.333,1.333 0,0 0,12.01 1h0Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetLeft="72dp">
|
||||
<shape>
|
||||
<size
|
||||
android:height="1dp"/>
|
||||
<solid android:color="@color/disabled_grey"/>
|
||||
</shape>
|
||||
</inset>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M21.75,23h-12.5a1.25,1.25 0,0 1,-1.25 -1.25v-14.5a1.25,1.25 0,0 1,1.25 -1.25h9a1.252,1.252 0,0 1,0.884 0.366l3.5,3.5a1.252,1.252 0,0 1,0.366 0.884v11A1.25,1.25 0,0 1,21.75 23ZM10.5,20.5h10v-9.232l-2.768,-2.768L10.5,8.5v12ZM7,15.5h-3.5v-12h7.232l1.5,1.5h3.511a1.16,1.16 0,0 0,-0.109 -0.134l-3.5,-3.5A1.252,1.252 0,0 0,11.25 1h-9a1.25,1.25 0,0 0,-1.25 1.25v14.5a1.25,1.25 0,0 0,1.25 1.25L7,18v-2.5ZM18.5,12L17,12v-1.5a0.5,0.5 0,0 0,-1 0v2a0.5,0.5 0,0 0,0.5 0.5h2A0.5,0.5 0,0 0,18.5 12Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M13.779,12l7.867,-7.866a1.25,1.25 0,0 0,-1.768 -1.768l-7.866,7.866 -7.866,-7.866a1.25,1.25 0,1 0,-1.768 1.768L10.244,12l-7.866,7.866a1.25,1.25 0,0 0,1.768 1.768l7.866,-7.866 7.866,7.866a1.25,1.25 0,0 0,1.768 -1.768Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M18.012,22.969h-4v-6h-4v6h-4a1.075,1.075 0,0 1,-1 -1v-8.5a1.091,1.091 0,0 1,0.5 -1l5.5,-5.5a1.413,1.413 0,0 1,2 0l5.5,5.5a1.538,1.538 0,0 1,0.5 1v8.5A1.075,1.075 0,0 1,18.012 22.969ZM22.012,13.281a1.246,1.246 0,0 1,-0.884 -0.366l-9.116,-9.116 -9.116,9.116a1.25,1.25 0,0 1,-1.768 -1.768l10,-10a1.251,1.251 0,0 1,1.768 0l10,10A1.25,1.25 0,0 1,22.012 13.281Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M18,17.823c-2.29,0 -3.873,-2.692 -6.069,-2.692s-3.916,2.692 -6.069,2.692c-2.826,0 -4.912,-2.616 -4.946,-7.1 -0.021,-2.783 0.829,-3.671 4.5,-3.671s4.742,1.468 6.519,1.468 2.852,-1.468 6.519,-1.468 4.517,0.888 4.5,3.671C22.909,15.207 20.823,17.823 18,17.823ZM7.21,10.481c-2.229,0.1 -3.147,1.393 -3.147,1.713s1.478,1.224 2.923,1.224 3.147,-0.518 3.147,-0.979A2.611,2.611 0,0 0,7.207 10.481ZM16.652,10.481a2.611,2.611 0,0 0,-2.923 1.958c0,0.461 1.7,0.979 3.147,0.979s2.923,-0.9 2.923,-1.224S18.878,10.576 16.649,10.481Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19,15a3.987,3.987 0,0 0,-2.839 1.18l-7.208,-3.6a3.6,3.6 0,0 0,0 -1.16l7.208,-3.6A4,4 0,1 0,15 5a3.936,3.936 0,0 0,0.047 0.58l-7.208,3.6a4,4 0,1 0,0 5.64l7.208,3.6a3.936,3.936 0,0 0,-0.047 0.58A4,4 0,1 0,19 15Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M22,20.015L2,20.015a1.266,1.266 0,0 1,-1.266 -1.266v-2.531A1.266,1.266 0,0 1,2 14.952c0.618,0 1.248,-3.239 1.484,-4.459 0.622,-3.2 1.266,-6.508 3.971,-6.508h9.09c2.705,0 3.349,3.309 3.971,6.508 0.236,1.22 0.866,4.459 1.484,4.459a1.266,1.266 0,0 1,1.266 1.266v2.531A1.266,1.266 0,0 1,22 20.015ZM3.266,17.483h17.468v-0.3c-1.668,-0.883 -2.193,-3.583 -2.7,-6.21 -0.237,-1.219 -0.867,-4.459 -1.485,-4.459h-9.09c-0.618,0 -1.248,3.24 -1.485,4.459 -0.511,2.627 -1.036,5.327 -2.7,6.21v0.3Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:gecko="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/info_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp">
|
||||
|
||||
<org.mozilla.gecko.widget.FaviconView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="@dimen/favicon_bg"
|
||||
android:layout_height="@dimen/favicon_bg"
|
||||
android:layout_gravity="center"
|
||||
gecko:enableRoundCorners="false"
|
||||
tools:background="@drawable/favicon_globe"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/url"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:layout_toRightOf="@id/icon"
|
||||
android:paddingLeft="@dimen/activity_stream_base_margin"
|
||||
android:paddingStart="@dimen/activity_stream_base_margin"
|
||||
android:textColor="@color/activity_stream_subtitle"
|
||||
android:textSize="12sp"
|
||||
tools:text="twitter"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/url"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:layout_toRightOf="@id/icon"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="3"
|
||||
android:paddingLeft="@dimen/activity_stream_base_margin"
|
||||
android:paddingStart="@dimen/activity_stream_base_margin"
|
||||
android:textColor="#ff000000"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="Descriptive title of a page that is veeeeeeery long - maybe even too long?"/>
|
||||
</RelativeLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:background="@color/disabled_grey"
|
||||
android:padding="4dp"/>
|
||||
|
||||
<android.support.v4.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/activity_stream_contextmenu_max_menu_height">
|
||||
|
||||
<android.support.design.widget.NavigationView
|
||||
android:id="@+id/menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/ActivityStreamContextMenuStyle"
|
||||
app:itemTextAppearance="@style/ActivityStreamContextMenuText"
|
||||
app:menu="@menu/activitystream_contextmenu"/>
|
||||
|
||||
</android.support.v4.widget.NestedScrollView>
|
||||
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Group ID's are required, otherwise NavigationView won't show any dividers. The ID's are unused, but still required. -->
|
||||
<group android:id="@+id/group0">
|
||||
<item
|
||||
android:id="@+id/bookmark"
|
||||
android:icon="@drawable/as_bookmark"
|
||||
android:title="@string/bookmark"/>
|
||||
<item
|
||||
android:id="@+id/share"
|
||||
android:icon="@drawable/as_share"
|
||||
android:title="@string/share"/>
|
||||
<item
|
||||
android:id="@+id/copy_url"
|
||||
android:icon="@drawable/as_copy"
|
||||
android:title="@string/contextmenu_copyurl"/>
|
||||
<item
|
||||
android:id="@+id/add_homescreen"
|
||||
android:icon="@drawable/as_home"
|
||||
android:title="@string/contextmenu_add_to_launcher"/>
|
||||
</group>
|
||||
|
||||
<group android:id="@+id/group1">
|
||||
<item
|
||||
android:id="@+id/open_new_tab"
|
||||
android:icon="@drawable/as_tab"
|
||||
android:title="@string/contextmenu_open_new_tab"/>
|
||||
<item
|
||||
android:id="@+id/open_new_private_tab"
|
||||
android:icon="@drawable/as_private"
|
||||
android:title="@string/contextmenu_open_private_tab"/>
|
||||
</group>
|
||||
|
||||
|
||||
<group android:id="@+id/group2">
|
||||
<item
|
||||
android:id="@+id/dismiss"
|
||||
android:icon="@drawable/as_dimiss"
|
||||
android:title="@string/activity_stream_dismiss"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/delete"
|
||||
android:icon="@drawable/as_bin"
|
||||
android:visible="false"
|
||||
android:title="@string/activity_stream_delete_history"/>
|
||||
</group>
|
||||
</menu>
|
|
@ -221,4 +221,9 @@
|
|||
<item name="activity_stream_desired_tile_width" type="dimen">90dp</item>
|
||||
<item name="activity_stream_desired_tile_height" type="dimen">70dp</item>
|
||||
<item name="activity_stream_top_sites_text_height" type="dimen">30dp</item>
|
||||
|
||||
<item name="activity_stream_contextmenu_peek_height" type="dimen">380dp</item>
|
||||
<!-- note: max_menu_height only affects the scrolling menu, but doesnt' take into consideration
|
||||
the header above it. -->
|
||||
<item name="activity_stream_contextmenu_max_menu_height" type="dimen">350dp</item>
|
||||
</resources>
|
||||
|
|
|
@ -819,4 +819,14 @@
|
|||
</style>
|
||||
|
||||
<style name="TabQueueActivity" parent="android:style/Theme.NoDisplay" />
|
||||
|
||||
<style name="ActivityStreamContextMenuText">
|
||||
<item name="android:textSize">16sp</item>
|
||||
</style>
|
||||
|
||||
<!-- We use this style to provide our own divider that has an inset on the left side -->
|
||||
<style name="ActivityStreamContextMenuStyle">
|
||||
<item name="android:listDivider">@drawable/as_contextmenu_divider</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -634,4 +634,6 @@
|
|||
<string name="activity_stream_highlights">&activity_stream_highlights;</string>
|
||||
<string name="activity_stream_highlight_label_bookmarked">&activity_stream_highlight_label_bookmarked;</string>
|
||||
<string name="activity_stream_highlight_label_visited">&activity_stream_highlight_label_visited;</string>
|
||||
<string name="activity_stream_dismiss">&activity_stream_dismiss;</string>
|
||||
<string name="activity_stream_delete_history">&activity_stream_delete_history;</string>
|
||||
</resources>
|
||||
|
|
|
@ -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/.
|
||||
|
||||
MOZ_APP_DISPLAYNAME=Aurora
|
||||
MOZ_APP_DISPLAYNAME="Firefox Aurora"
|
||||
ANDROID_PACKAGE_NAME=org.mozilla.fennec_aurora
|
||||
MOZ_UPDATER=1
|
||||
MOZ_ANDROID_ANR_REPORTER=1
|
||||
|
|
|
@ -188,6 +188,13 @@ pref("dom.enable_performance_observer", true);
|
|||
pref("dom.enable_performance_observer", false);
|
||||
#endif
|
||||
|
||||
// Enable requestIdleCallback API
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("dom.requestIdleCallback.enabled", true);
|
||||
#else
|
||||
pref("dom.requestIdleCallback.enabled", false);
|
||||
#endif
|
||||
|
||||
// Whether the Gamepad API is enabled
|
||||
pref("dom.gamepad.enabled", true);
|
||||
pref("dom.gamepad.test.enabled", false);
|
||||
|
@ -2694,6 +2701,14 @@ pref("layout.spammy_warnings.enabled", false);
|
|||
// Should we fragment floats inside CSS column layout?
|
||||
pref("layout.float-fragments-inside-column.enabled", true);
|
||||
|
||||
// The number of frames times the frame rate is the time required to
|
||||
// pass without painting used to guess that we'll not paint again soon
|
||||
pref("layout.idle_period.required_quiescent_frames", 2);
|
||||
|
||||
// The amount of time (milliseconds) needed between an idle period's
|
||||
// end and the start of the next tick to avoid jank.
|
||||
pref("layout.idle_period.time_limit", 3);
|
||||
|
||||
// Is support for the Web Animations API enabled?
|
||||
// Before enabling this by default, make sure also CSSPseudoElement interface
|
||||
// has been spec'ed properly, or we should add a separate pref for
|
||||
|
@ -2743,6 +2758,12 @@ pref("dom.global_stop_script", true);
|
|||
// If true, ArchiveReader will be enabled
|
||||
pref("dom.archivereader.enabled", false);
|
||||
|
||||
// Time (milliseconds) between throttled idle callbacks.
|
||||
pref("dom.idle_period.throttled_length", 10000);
|
||||
|
||||
// The amount of idle time (milliseconds) reserved for a long idle period
|
||||
pref("idle_queue.long_period", 50);
|
||||
|
||||
// Hang monitor timeout after which we kill the browser, in seconds
|
||||
// (0 is disabled)
|
||||
// Disabled on all platforms per bug 705748 until the found issues are
|
||||
|
|
|
@ -3957,14 +3957,6 @@ if test -n "$MOZ_NO_SMART_CARDS"; then
|
|||
fi
|
||||
AC_SUBST(MOZ_NO_SMART_CARDS)
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Disable EV certificate verification
|
||||
dnl ========================================================
|
||||
if test -n "$MOZ_NO_EV_CERTS"; then
|
||||
AC_DEFINE(MOZ_NO_EV_CERTS)
|
||||
fi
|
||||
AC_SUBST(MOZ_NO_EV_CERTS)
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Sandboxing support
|
||||
dnl ========================================================
|
||||
|
|
|
@ -439,7 +439,7 @@ JOB_DETAILS = {
|
|||
None)),
|
||||
'android-api-15-debug': (AndroidArtifactJob, ('public/build/target.apk',
|
||||
None)),
|
||||
'android-x86': (AndroidArtifactJob, ('public/build/fennec-(.*)-i386\.apk',
|
||||
'android-x86-opt': (AndroidArtifactJob, ('public/build/target.apk',
|
||||
None)),
|
||||
'linux-opt': (LinuxArtifactJob, ('public/build/firefox-(.*)\.linux-i686\.tar\.bz2',
|
||||
'public/build/firefox-(.*)\.common\.tests\.zip')),
|
||||
|
@ -806,7 +806,7 @@ class Artifacts(object):
|
|||
|
||||
if self._substs.get('MOZ_BUILD_APP', '') == 'mobile/android':
|
||||
if self._substs['ANDROID_CPU_ARCH'] == 'x86':
|
||||
return 'android-x86'
|
||||
return 'android-x86-opt'
|
||||
return 'android-api-15' + target_suffix
|
||||
|
||||
target_64bit = False
|
||||
|
|
|
@ -462,7 +462,6 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
|
||||
rv = Result::ERROR_UNKNOWN_ERROR;
|
||||
|
||||
#ifndef MOZ_NO_EV_CERTS
|
||||
// Try to validate for EV first.
|
||||
NSSCertDBTrustDomain::OCSPFetching evOCSPFetching
|
||||
= (mOCSPDownloadConfig == ocspOff) ||
|
||||
|
@ -537,7 +536,6 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
if (rv == Success) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (flags & FLAG_MUST_BE_EV) {
|
||||
rv = Result::ERROR_POLICY_VALIDATION_FAILED;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "certdb.h"
|
||||
#include "hasht.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "pk11pub.h"
|
||||
|
@ -1249,8 +1250,8 @@ CertIsAuthoritativeForEVPolicy(const UniqueCERTCertificate& cert,
|
|||
return false;
|
||||
}
|
||||
|
||||
static PRStatus
|
||||
IdentityInfoInit()
|
||||
nsresult
|
||||
LoadExtendedValidationInfo()
|
||||
{
|
||||
static const char* sCABForumOIDString = "2.23.140.1.1";
|
||||
static const char* sCABForumOIDDescription = "CA/Browser Forum EV OID";
|
||||
|
@ -1258,28 +1259,35 @@ IdentityInfoInit()
|
|||
mozilla::ScopedAutoSECItem cabforumOIDItem;
|
||||
if (SEC_StringToOID(nullptr, &cabforumOIDItem, sCABForumOIDString, 0)
|
||||
!= SECSuccess) {
|
||||
return PR_FAILURE;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
sCABForumEVOIDTag = RegisterOID(cabforumOIDItem, sCABForumOIDDescription);
|
||||
if (sCABForumEVOIDTag == SEC_OID_UNKNOWN) {
|
||||
return PR_FAILURE;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
for (size_t iEV = 0; iEV < mozilla::ArrayLength(myTrustedEVInfos); ++iEV) {
|
||||
nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV];
|
||||
|
||||
SECStatus srv;
|
||||
#ifdef DEBUG
|
||||
// This section of code double-checks that we calculated the correct
|
||||
// certificate hash given the issuer and serial number and that it is
|
||||
// actually present in our loaded root certificates module. It is
|
||||
// unnecessary to check this in non-debug builds since we will safely fall
|
||||
// back to DV if the EV information is incorrect.
|
||||
mozilla::ScopedAutoSECItem derIssuer;
|
||||
SECStatus rv = ATOB_ConvertAsciiToItem(&derIssuer, entry.issuer_base64);
|
||||
PR_ASSERT(rv == SECSuccess);
|
||||
if (rv != SECSuccess) {
|
||||
return PR_FAILURE;
|
||||
srv = ATOB_ConvertAsciiToItem(&derIssuer, entry.issuer_base64);
|
||||
MOZ_ASSERT(srv == SECSuccess, "Could not base64-decode built-in EV issuer");
|
||||
if (srv != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mozilla::ScopedAutoSECItem serialNumber;
|
||||
rv = ATOB_ConvertAsciiToItem(&serialNumber, entry.serial_base64);
|
||||
PR_ASSERT(rv == SECSuccess);
|
||||
if (rv != SECSuccess) {
|
||||
return PR_FAILURE;
|
||||
srv = ATOB_ConvertAsciiToItem(&serialNumber, entry.serial_base64);
|
||||
MOZ_ASSERT(srv == SECSuccess, "Could not base64-decode built-in EV serial");
|
||||
if (srv != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
CERTIssuerAndSN ias;
|
||||
|
@ -1293,66 +1301,41 @@ IdentityInfoInit()
|
|||
|
||||
// If an entry is missing in the NSS root database, it may be because the
|
||||
// root database is out of sync with what we expect (e.g. a different
|
||||
// version of system NSS is installed). We assert on debug builds, but
|
||||
// silently continue on release builds. In both cases, the root cert does
|
||||
// not get EV treatment.
|
||||
// version of system NSS is installed).
|
||||
if (!cert) {
|
||||
#ifdef DEBUG
|
||||
// The debug CA structs are at positions 0 to NUM_TEST_EV_ROOTS - 1, and
|
||||
// are NOT in the NSS root DB.
|
||||
if (iEV < NUM_TEST_EV_ROOTS) {
|
||||
continue;
|
||||
// The entries for the debug EV roots are at indices 0 through
|
||||
// NUM_TEST_EV_ROOTS - 1. Since they're not built-in, they probably
|
||||
// haven't been loaded yet.
|
||||
MOZ_ASSERT(iEV < NUM_TEST_EV_ROOTS, "Could not find built-in EV root");
|
||||
} else {
|
||||
unsigned char certFingerprint[SHA256_LENGTH];
|
||||
srv = PK11_HashBuf(SEC_OID_SHA256, certFingerprint, cert->derCert.data,
|
||||
AssertedCast<int32_t>(cert->derCert.len));
|
||||
MOZ_ASSERT(srv == SECSuccess, "Could not hash EV root");
|
||||
if (srv != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
bool same = PodEqual(certFingerprint, entry.ev_root_sha256_fingerprint);
|
||||
MOZ_ASSERT(same, "EV root fingerprint mismatch");
|
||||
if (!same) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
PR_NOT_REACHED("Could not find EV root in NSS storage");
|
||||
continue;
|
||||
// This is the code that actually enables these roots for EV.
|
||||
mozilla::ScopedAutoSECItem evOIDItem;
|
||||
srv = SEC_StringToOID(nullptr, &evOIDItem, entry.dotted_oid, 0);
|
||||
MOZ_ASSERT(srv == SECSuccess, "SEC_StringToOID failed");
|
||||
if (srv != SECSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
unsigned char certFingerprint[SHA256_LENGTH];
|
||||
rv = PK11_HashBuf(SEC_OID_SHA256, certFingerprint, cert->derCert.data,
|
||||
AssertedCast<int32_t>(cert->derCert.len));
|
||||
PR_ASSERT(rv == SECSuccess);
|
||||
if (rv == SECSuccess) {
|
||||
bool same = !memcmp(certFingerprint, entry.ev_root_sha256_fingerprint,
|
||||
sizeof(certFingerprint));
|
||||
PR_ASSERT(same);
|
||||
if (same) {
|
||||
mozilla::ScopedAutoSECItem evOIDItem;
|
||||
rv = SEC_StringToOID(nullptr, &evOIDItem, entry.dotted_oid, 0);
|
||||
PR_ASSERT(rv == SECSuccess);
|
||||
if (rv == SECSuccess) {
|
||||
entry.oid_tag = RegisterOID(evOIDItem, entry.oid_name);
|
||||
if (entry.oid_tag == SEC_OID_UNKNOWN) {
|
||||
rv = SECFailure;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PR_SetError(SEC_ERROR_BAD_DATA, 0);
|
||||
rv = SECFailure;
|
||||
}
|
||||
}
|
||||
|
||||
if (rv != SECSuccess) {
|
||||
entry.oid_tag = SEC_OID_UNKNOWN;
|
||||
return PR_FAILURE;
|
||||
entry.oid_tag = RegisterOID(evOIDItem, entry.oid_name);
|
||||
if (entry.oid_tag == SEC_OID_UNKNOWN) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
static PRCallOnceType sIdentityInfoCallOnce;
|
||||
|
||||
void
|
||||
EnsureIdentityInfoLoaded()
|
||||
{
|
||||
(void) PR_CallOnce(&sIdentityInfoCallOnce, IdentityInfoInit);
|
||||
}
|
||||
|
||||
void
|
||||
CleanupIdentityInfo()
|
||||
{
|
||||
memset(&sIdentityInfoCallOnce, 0, sizeof(PRCallOnceType));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Find the first policy OID that is known to be an EV policy OID.
|
||||
|
|
|
@ -14,9 +14,7 @@ namespace mozilla { namespace pkix { struct CertPolicyId; } }
|
|||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
#ifndef MOZ_NO_EV_CERTS
|
||||
void EnsureIdentityInfoLoaded();
|
||||
void CleanupIdentityInfo();
|
||||
nsresult LoadExtendedValidationInfo();
|
||||
SECStatus GetFirstEVPolicy(CERTCertificate* cert,
|
||||
/*out*/ mozilla::pkix::CertPolicyId& policy,
|
||||
/*out*/ SECOidTag& policyOidTag);
|
||||
|
@ -25,7 +23,6 @@ SECStatus GetFirstEVPolicy(CERTCertificate* cert,
|
|||
// or distrusted.
|
||||
bool CertIsAuthoritativeForEVPolicy(const UniqueCERTCertificate& cert,
|
||||
const mozilla::pkix::CertPolicyId& policy);
|
||||
#endif
|
||||
|
||||
} } // namespace mozilla::psm
|
||||
|
||||
|
|
|
@ -173,12 +173,6 @@ NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
|||
Input candidateCertDER,
|
||||
/*out*/ TrustLevel& trustLevel)
|
||||
{
|
||||
#ifdef MOZ_NO_EV_CERTS
|
||||
if (!policy.IsAnyPolicy()) {
|
||||
return Result::ERROR_POLICY_VALIDATION_FAILED;
|
||||
}
|
||||
#endif
|
||||
|
||||
// XXX: This would be cleaner and more efficient if we could get the trust
|
||||
// information without constructing a CERTCertificate here, but NSS doesn't
|
||||
// expose it in any other easy-to-use fashion. The use of
|
||||
|
@ -250,12 +244,10 @@ NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
|||
trustLevel = TrustLevel::TrustAnchor;
|
||||
return Success;
|
||||
}
|
||||
#ifndef MOZ_NO_EV_CERTS
|
||||
if (CertIsAuthoritativeForEVPolicy(candidateCert, policy)) {
|
||||
trustLevel = TrustLevel::TrustAnchor;
|
||||
return Success;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1758,37 +1758,6 @@ AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer)
|
|||
return SECFailure;
|
||||
}
|
||||
|
||||
#ifndef MOZ_NO_EV_CERTS
|
||||
class InitializeIdentityInfo : public CryptoTask
|
||||
{
|
||||
virtual nsresult CalculateResult() override
|
||||
{
|
||||
EnsureIdentityInfoLoaded();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual void ReleaseNSSResources() override { } // no-op
|
||||
virtual void CallCallback(nsresult rv) override { } // no-op
|
||||
};
|
||||
#endif
|
||||
|
||||
void EnsureServerVerificationInitialized()
|
||||
{
|
||||
#ifndef MOZ_NO_EV_CERTS
|
||||
// Should only be called from socket transport thread due to the static
|
||||
// variable and the reference to gCertVerificationThreadPool
|
||||
|
||||
static bool triggeredCertVerifierInit = false;
|
||||
if (triggeredCertVerifierInit)
|
||||
return;
|
||||
triggeredCertVerifierInit = true;
|
||||
|
||||
RefPtr<InitializeIdentityInfo> initJob = new InitializeIdentityInfo();
|
||||
if (gCertVerificationThreadPool)
|
||||
gCertVerificationThreadPool->Dispatch(initJob, NS_DISPATCH_NORMAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
SSLServerCertVerificationResult::SSLServerCertVerificationResult(
|
||||
nsNSSSocketInfo* infoObject, PRErrorCode errorCode,
|
||||
Telemetry::ID telemetryID, uint32_t telemetryValue,
|
||||
|
|
|
@ -14,11 +14,6 @@ namespace mozilla { namespace psm {
|
|||
SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd,
|
||||
PRBool checkSig, PRBool isServer);
|
||||
|
||||
// EnsureServerVerificationInitialized() posts an event to a cert
|
||||
// verification thread to run nsINSSComponent::EnsureIdentityInfoLoaded()
|
||||
// exactly once. It must be called from socket thread.
|
||||
void EnsureServerVerificationInitialized();
|
||||
|
||||
} } // namespace mozilla::psm
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1968,15 +1968,15 @@ nsNSSCertificate::CreateTBSCertificateASN1Struct(nsIASN1Sequence **retSequence,
|
|||
if (mCert->extensions) {
|
||||
SECOidTag ev_oid_tag = SEC_OID_UNKNOWN;
|
||||
|
||||
#ifndef MOZ_NO_EV_CERTS
|
||||
bool validEV;
|
||||
rv = hasValidEVOidTag(ev_oid_tag, validEV);
|
||||
if (NS_FAILED(rv))
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!validEV)
|
||||
if (!validEV) {
|
||||
ev_oid_tag = SEC_OID_UNKNOWN;
|
||||
#endif
|
||||
}
|
||||
|
||||
rv = ProcessExtensions(mCert->extensions, sequence, ev_oid_tag, nssComponent);
|
||||
if (NS_FAILED(rv))
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче