Merge fx-team to central, a=merge

This commit is contained in:
Wes Kocher 2015-11-02 14:26:35 -08:00
commit f969d53eca
35 changed files with 675 additions and 122 deletions

View File

@ -1292,11 +1292,11 @@ DownloadsPlacesView.prototype = {
goUpdateCommand("downloadsCmd_clearDownloads");
break;
default: {
// Slicing the array to get a freezed list of selected items. Otherwise,
// the selectedItems array is live and doCommand may alter the selection
// while we are trying to do one particular action, like removing items
// from history.
let selectedElements = this._richlistbox.selectedItems.slice();
// Cloning the nodelist into an array to get a frozen list of selected items.
// Otherwise, the selectedItems nodelist is live and doCommand may alter the
// selection while we are trying to do one particular action, like removing
// items from history.
let selectedElements = [... this._richlistbox.selectedItems];
for (let element of selectedElements) {
element._shell.doCommand(aCommand);
}

View File

@ -72,6 +72,9 @@ const MESSAGES = [
// A crashed tab was revived by navigating to a different page. Remove its
// browser from the list of crashed browsers to stop ignoring its messages.
"SessionStore:crashedTabRevived",
// The content script encountered an error.
"SessionStore:error",
];
// The list of messages we accept from <xul:browser>s that have no tab
@ -85,6 +88,9 @@ const NOTAB_MESSAGES = new Set([
// For a description see above.
"SessionStore:update",
// For a description see above.
"SessionStore:error",
]);
// The list of messages we accept without an "epoch" parameter.
@ -95,6 +101,9 @@ const NOEPOCH_MESSAGES = new Set([
// For a description see above.
"SessionStore:crashedTabRevived",
// For a description see above.
"SessionStore:error",
]);
// The list of messages we want to receive even during the short period after a
@ -106,6 +115,9 @@ const CLOSED_MESSAGES = new Set([
// For a description see above.
"SessionStore:update",
// For a description see above.
"SessionStore:error",
]);
// These are tab events that we listen to.
@ -808,6 +820,9 @@ var SessionStoreInternal = {
case "SessionStore:crashedTabRevived":
this._crashedBrowsers.delete(browser.permanentKey);
break;
case "SessionStore:error":
this.reportInternalError(data);
break;
default:
throw new Error(`received unknown message '${aMessage.name}'`);
break;
@ -3848,6 +3863,19 @@ var SessionStoreInternal = {
*/
resetEpoch(browser) {
this._browserEpochs.delete(browser.permanentKey);
},
/**
* Handle an error report from a content process.
*/
reportInternalError(data) {
// For the moment, we only report errors through Telemetry.
if (data.telemetry) {
for (let key of Object.keys(data.telemetry)) {
let histogram = Telemetry.getHistogramById(key);
histogram.add(data.telemetry[key]);
}
}
}
};

View File

@ -29,10 +29,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "SessionHistory",
XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage",
"resource:///modules/sessionstore/SessionStorage.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsISyncMessageSender");
Cu.import("resource:///modules/sessionstore/FrameTree.jsm", this);
var gFrameTree = new FrameTree(this);
@ -692,12 +688,21 @@ var MessageQueue = {
FX_SESSION_RESTORE_CONTENT_COLLECT_DATA_LONGEST_OP_MS: durationMs
}
// Send all data to the parent process.
sendMessage("SessionStore:update", {
id: this._id, data, telemetry, flushID,
isFinal: options.isFinal || false,
epoch: gCurrentEpoch
});
try {
// Send all data to the parent process.
sendMessage("SessionStore:update", {
id: this._id, data, telemetry, flushID,
isFinal: options.isFinal || false,
epoch: gCurrentEpoch
});
} catch (ex if ex && ex.result == Cr.NS_ERROR_OUT_OF_MEMORY) {
let telemetry = {
FX_SESSION_RESTORE_SEND_UPDATE_CAUSED_OOM: 1
};
sendMessage("SessionStore:error", {
telemetry
});
}
// Increase our unique message ID.
this._id++;

View File

@ -211,3 +211,4 @@ skip-if = true
skip-if = os == "mac"
[browser_911547.js]
[browser_send_async_message_oom.js]

View File

@ -0,0 +1,71 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
const HISTOGRAM_NAME = "FX_SESSION_RESTORE_SEND_UPDATE_CAUSED_OOM";
/**
* Test that an OOM in sendAsyncMessage in a framescript will be reported
* to Telemetry.
*/
add_task(function* init() {
Services.telemetry.canRecordExtended = true;
});
function frameScript() {
// Make send[A]syncMessage("SessionStore:update", ...) simulate OOM.
// Other operations are unaffected.
let mm = docShell.sameTypeRootTreeItem.
QueryInterface(Ci.nsIDocShell).
QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIContentFrameMessageManager);
let wrap = function(original) {
return function(name, ...args) {
if (name != "SessionStore:update") {
return original(name, ...args);
}
throw new Components.Exception("Simulated OOM", Cr.NS_ERROR_OUT_OF_MEMORY);
}
}
mm.sendAsyncMessage = wrap(mm.sendAsyncMessage);
mm.sendSyncMessage = wrap(mm.sendSyncMessage);
}
add_task(function*() {
// Capture original state.
let snapshot = Services.telemetry.getHistogramById(HISTOGRAM_NAME).snapshot();
// Open a browser, configure it to cause OOM.
let newTab = gBrowser.addTab("about:robots");
let browser = newTab.linkedBrowser;
yield ContentTask.spawn(browser, null, frameScript);
let promiseReported = new Promise(resolve => {
browser.messageManager.addMessageListener("SessionStore:error", resolve);
});
// Attempt to flush. This should fail.
let promiseFlushed = TabStateFlusher.flush(browser);
promiseFlushed.then(() => {throw new Error("Flush should have failed")});
// The frame script should report an error.
yield promiseReported;
// Give us some time to handle that error.
yield new Promise(resolve => setTimeout(resolve, 10));
// By now, Telemetry should have been updated.
let snapshot2 = Services.telemetry.getHistogramById(HISTOGRAM_NAME).snapshot();
gBrowser.removeTab(newTab);
Assert.ok(snapshot2.sum > snapshot.sum);
});
add_task(function* cleanup() {
Services.telemetry.canRecordExtended = false;
});

View File

@ -440,12 +440,16 @@ Section "-Application" APP_IDX
${If} $R0 == "true"
; Only proceed if we have HKLM write access
${AndIf} $TmpVal == "HKLM"
; On Windows 2000 we do not install the maintenance service.
${AndIf} ${AtLeastWinXP}
; The user is an admin so we should default to install service yes
StrCpy $InstallMaintenanceService "1"
; On Windows < XP SP3 we do not install the maintenance service.
${If} ${IsWinXP}
${AndIf} ${AtMostServicePack} 2
StrCpy $InstallMaintenanceService "0"
${Else}
; The user is an admin, so we should default to installing the service.
StrCpy $InstallMaintenanceService "1"
${EndIf}
${Else}
; The user is not admin so we should default to install service no
; The user is not admin, so we can't install the service.
StrCpy $InstallMaintenanceService "0"
${EndIf}
${EndIf}
@ -916,10 +920,11 @@ Function preComponents
Abort
${EndIf}
; On Windows 2000 we do not install the maintenance service.
${Unless} ${AtLeastWinXP}
; On Windows < XP SP3 we do not install the maintenance service.
${If} ${IsWinXP}
${AndIf} ${AtMostServicePack} 2
Abort
${EndUnless}
${EndIf}
; Don't show the custom components page if the
; user is not an admin

View File

@ -1021,9 +1021,19 @@ Function createOptions
${NSD_Check} $CheckboxSendPing
!ifdef MOZ_MAINTENANCE_SERVICE
; Only show the maintenance service checkbox if we have write access to HKLM
; We can only install the maintenance service if the user is an admin.
Call IsUserAdmin
Pop $0
; Only show the maintenance service checkbox if we're on XP SP3 or higher;
; we don't ever want to install it on XP without at least SP3 installed.
${If} $0 == "true"
${AndIf} ${IsWinXP}
${AndIf} ${AtMostServicePack} 2
StrCpy $0 "false"
${EndIf}
; Only show the maintenance service checkbox if we have write access to HKLM
ClearErrors
WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
"Write Test"

View File

@ -7,7 +7,7 @@
"use strict";
loader.lazyRequireGetter(this, "React",
"resource://devtools/client/shared/vendor/react.js");
"devtools/client/shared/vendor/react");
loader.lazyRequireGetter(this, "TargetListComponent",
"devtools/client/aboutdebugging/components/target-list", true);
loader.lazyRequireGetter(this, "Services");

View File

@ -7,7 +7,7 @@
"use strict";
loader.lazyRequireGetter(this, "React",
"resource://devtools/client/shared/vendor/react.js");
"devtools/client/shared/vendor/react");
loader.lazyRequireGetter(this, "TargetComponent",
"devtools/client/aboutdebugging/components/target", true);
loader.lazyRequireGetter(this, "Services");

View File

@ -8,7 +8,7 @@
"use strict";
loader.lazyRequireGetter(this, "React",
"resource://devtools/client/shared/vendor/react.js");
"devtools/client/shared/vendor/react");
loader.lazyRequireGetter(this, "TargetFactory",
"devtools/client/framework/target", true);
loader.lazyRequireGetter(this, "Toolbox",

View File

@ -9,7 +9,7 @@
loader.lazyRequireGetter(this, "Ci",
"chrome", true);
loader.lazyRequireGetter(this, "React",
"resource://devtools/client/shared/vendor/react.js");
"devtools/client/shared/vendor/react");
loader.lazyRequireGetter(this, "TargetListComponent",
"devtools/client/aboutdebugging/components/target-list", true);
loader.lazyRequireGetter(this, "Services");

View File

@ -34,6 +34,9 @@ const L10N = new ViewHelpers.L10N(STRINGS_URI);
const MILLIS_TIME_FORMAT_MAX_DURATION = 4000;
// The minimum spacing between 2 time graduation headers in the timeline (px).
const TIME_GRADUATION_MIN_SPACING = 40;
// The size of the fast-track icon (for compositor-running animations), this is
// used to position the icon correctly.
const FAST_TRACK_ICON_SIZE = 20;
/**
* UI component responsible for displaying a preview of the target dom node of
@ -631,7 +634,9 @@ AnimationsTimeline.prototype = {
parent: this.animationsEl,
nodeType: "li",
attributes: {
"class": "animation"
"class": "animation" + (animation.state.isRunningOnCompositor
? " fast-track"
: "")
}
});
@ -801,6 +806,8 @@ AnimationTimeBlock.prototype = {
let x = TimeScale.startTimeToDistance(start + (delay / rate), width);
let w = TimeScale.durationToDistance(duration / rate, width);
let iterationW = w * (count || 1);
let delayW = TimeScale.durationToDistance(Math.abs(delay) / rate, width);
let iterations = createNode({
parent: this.containerEl,
@ -809,7 +816,7 @@ AnimationTimeBlock.prototype = {
// Individual iterations are represented by setting the size of the
// repeating linear-gradient.
"style": `left:${x}px;
width:${w * (count || 1)}px;
width:${iterationW}px;
background-size:${Math.max(w, 2)}px 100%;`
}
});
@ -817,15 +824,17 @@ AnimationTimeBlock.prototype = {
// The animation name is displayed over the iterations.
// Note that in case of negative delay, we push the name towards the right
// so the delay can be shown.
let negativeDelayW = delay < 0 ? delayW : 0;
createNode({
parent: iterations,
attributes: {
"class": "name",
"title": this.getTooltipText(state),
"style": delay < 0
? "margin-left:" +
TimeScale.durationToDistance(Math.abs(delay), width) + "px"
: ""
// Position the fast-track icon with background-position, and make space
// for the negative delay with a margin-left.
"style": "background-position:" +
(iterationW - FAST_TRACK_ICON_SIZE - negativeDelayW) +
"px center;margin-left:" + negativeDelayW + "px"
},
textContent: state.name
});
@ -835,9 +844,6 @@ AnimationTimeBlock.prototype = {
// Negative delays need to start at 0.
let delayX = TimeScale.durationToDistance(
(delay < 0 ? 0 : delay) / rate, width);
let delayW = TimeScale.durationToDistance(
Math.abs(delay) / rate, width);
createNode({
parent: iterations,
attributes: {
@ -864,6 +870,9 @@ AnimationTimeBlock.prototype = {
let iterations = L10N.getStr("player.animationIterationCountLabel") + " " +
(state.iterationCount ||
L10N.getStr("player.infiniteIterationCountText"));
return [title, duration, iterations, delay].join("\n");
let compositor = state.isRunningOnCompositor
? L10N.getStr("player.runningOnCompositorTooltip")
: "";
return [title, duration, iterations, delay, compositor].join("\n");
}
};

View File

@ -20,6 +20,7 @@ support-files =
[browser_animation_refresh_on_added_animation.js]
[browser_animation_refresh_on_removed_animation.js]
[browser_animation_refresh_when_active.js]
[browser_animation_running_on_compositor.js]
[browser_animation_same_nb_of_playerWidgets_and_playerFronts.js]
[browser_animation_shows_player_on_valid_node.js]
[browser_animation_target_highlight_select.js]

View File

@ -0,0 +1,43 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that when animations displayed in the timeline are running on the
// compositor, they get a special icon and information in the tooltip.
const STRINGS_URI = "chrome://browser/locale/devtools/animationinspector.properties";
const L10N = new ViewHelpers.L10N(STRINGS_URI);
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {inspector, panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
info("Select a test node we know has an animation running on the compositor");
yield selectNode(".animated", inspector);
let animationEl = timeline.animationsEl.querySelector(".animation");
ok(animationEl.classList.contains("fast-track"),
"The animation element has the fast-track css class");
ok(hasTooltip(animationEl),
"The animation element has the right tooltip content");
info("Select a node we know doesn't have an animation on the compositor");
yield selectNode(".no-compositor", inspector);
animationEl = timeline.animationsEl.querySelector(".animation");
ok(!animationEl.classList.contains("fast-track"),
"The animation element does not have the fast-track css class");
ok(!hasTooltip(animationEl),
"The animation element has the right tooltip content");
});
function hasTooltip(animationEl) {
let el = animationEl.querySelector(".name");
let tooltip = el.getAttribute("title");
let expected = L10N.getStr("player.runningOnCompositorTooltip");
return tooltip.indexOf(expected) !== -1;
}

View File

@ -74,6 +74,14 @@
animation-fill-mode: forwards;
}
.no-compositor {
top: 0;
right: 10px;
background: gold;
animation: no-compositor 10s cubic-bezier(.57,-0.02,1,.31) forwards;
}
@keyframes simple-animation {
100% {
transform: translateX(300px);
@ -85,6 +93,12 @@
background: blue;
}
}
@keyframes no-compositor {
100% {
margin-right: 600px;
}
}
</style>
</head>
<body>
@ -97,5 +111,6 @@
<div class="ball short"></div>
<div class="ball long"></div>
<div class="ball negative-delay"></div>
<div class="ball no-compositor"></div>
</body>
</html>

View File

@ -341,6 +341,12 @@ body {
padding: 0 2px;
}
.animation-timeline .fast-track .name {
/* Animations running on the compositor have the fast-track background image*/
background-image: url("images/animation-fast-track.svg");
background-repeat: no-repeat;
}
.animation-timeline .animation .delay {
position: absolute;
top: 0;

View File

@ -4,6 +4,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="9" height="12">
<g transform="matrix(1.0251088,0,0,0.85613344,-3.1546734,-888.94343)">
<path d="m 5.1284819,1038.3667 6.4950901,0 -2.7147491,4.6651 2.9438561,0 -8.1148915,9.3081 1.6126718,-6.8973 -2.2701022,0 z" style="fill:#4cb0e1;"/>
<path d="m 5.1284819,1038.3667 6.4950901,0 -2.7147491,4.6651 2.9438561,0 -8.1148915,9.3081 1.6126718,-6.8973 -2.2701022,0 z" style="fill:white;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 516 B

After

Width:  |  Height:  |  Size: 514 B

View File

@ -306,6 +306,8 @@ var interfaceNamesInGlobalScope =
"ChannelSplitterNode",
// IMPORTANT: Do not change this list without review from a DOM peer!
"CharacterData",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "ChromeNodeList", xbl: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "ChromeWindow", xbl: true},
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -4,7 +4,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
[ChromeOnly, Constructor]
[Constructor, Func="IsChromeOrXBL"]
interface ChromeNodeList : NodeList {
[Throws]
void append(Node aNode);

View File

@ -602,6 +602,8 @@ public class GeckoEvent {
int value) {
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.TELEMETRY_HISTOGRAM_ADD);
event.mCharacters = histogram;
// Set the extras with null so that it cannot be mistaken with a keyed histogram.
event.mCharactersExtra = null;
event.mCount = value;
return event;
}

View File

@ -267,6 +267,10 @@ public class LayerView extends ScrollView implements Tabs.OnTabsChangedListener
@Override
protected void onAttachedToWindow() {
// We are adding descendants to this LayerView, but we don't want the
// descendants to affect the way LayerView retains its focus.
setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
// This check should not be done before the view is attached to a window
// as hardware acceleration will not be enabled at that point.
// We must create and add the SurfaceView instance before the view tree

View File

@ -146,6 +146,8 @@ public class TabQueueService extends Service {
TabQueueHelper.removeURLFromFile(applicationContext, intentUrl, TabQueueHelper.FILE_NAME);
}
openNow(safeIntent.getUnsafe());
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "tabqueue-doubletap");
stopSelfResult(startId);
}
});
@ -186,6 +188,8 @@ public class TabQueueService extends Service {
stopServiceRunnable = null;
removeView();
openNow(intent);
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "tabqueue-now");
stopSelfResult(startId);
}
});
@ -207,8 +211,6 @@ public class TabQueueService extends Service {
.remove(GeckoPreferences.PREFS_TAB_QUEUE_LAST_TIME)
.apply();
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "tabqueue-now");
executorService.submit(new Runnable() {
@Override
public void run() {

View File

@ -130,6 +130,7 @@ skip-if = android_version == "10" || android_version == "18"
# disabled on 4.3, bug 1158363
skip-if = android_version == "18"
[src/org/mozilla/gecko/tests/testUITelemetry.java]
[src/org/mozilla/gecko/tests/testBug1217581.java]
[src/org/mozilla/gecko/tests/testVideoControls.java]
# disabled on Android 2.3 due to video playback issues, bug 1088038; on 4.3, bug 1098532
skip-if = android_version == "10" || android_version == "18"

View File

@ -0,0 +1,31 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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.tests;
import org.mozilla.gecko.Telemetry;
public class testBug1217581 extends BaseTest {
// Take arbitrary histogram names used by Fennec.
private static final String TEST_HISTOGRAM_NAME = "FENNEC_SYNC_NUMBER_OF_SYNCS_COMPLETED";
private static final String TEST_KEYED_HISTOGRAM_NAME = "ABOUT_ACCOUNTS_CONTENT_SERVER_LOAD_STARTED_COUNT";
private static final String TEST_KEY_NAME = "testBug1217581";
public void testBug1217581() {
blockForGeckoReady();
mAsserter.ok(true, "Checking that adding to a keyed histogram then adding to a normal histogram does not cause a crash.", "");
Telemetry.addToKeyedHistogram(TEST_KEYED_HISTOGRAM_NAME, TEST_KEY_NAME, 1);
Telemetry.addToHistogram(TEST_HISTOGRAM_NAME, 1);
mAsserter.ok(true, "Adding to a keyed histogram then to a normal histogram was a success!", "");
mAsserter.ok(true, "Checking that adding to a normal histogram then adding to a keyed histogram does not cause a crash.", "");
Telemetry.addToHistogram(TEST_HISTOGRAM_NAME, 1);
Telemetry.addToKeyedHistogram(TEST_KEYED_HISTOGRAM_NAME, TEST_KEY_NAME, 1);
mAsserter.ok(true, "Adding to a normal histogram then to a keyed histogram was a success!", "");
}
}

View File

@ -41,12 +41,10 @@
#include <mach/mach_types.h>
#include <mach/message.h>
#include <mach/thread_info.h>
#endif // defined(XP_MACOSX)
#if defined(XP_LINUX)
#elif defined(XP_UNIX)
#include <sys/time.h>
#include <sys/resource.h>
#endif // defined(XP_LINUX)
#endif // defined(XP_UNIX)
/* ------------------------------------------------------
*
* Utility functions.

View File

@ -4352,6 +4352,12 @@
"extended_statistics_ok": true,
"description": "Session restore: Time spent blocking the main thread while restoring a window state (ms)"
},
"FX_SESSION_RESTORE_SEND_UPDATE_CAUSED_OOM": {
"alert_emails": ["session-restore-telemetry-alerts@mozilla.com"],
"expires_in_version": "default",
"kind": "count",
"description": "Count of messages sent by SessionRestore from child frames to the parent and that cannot be transmitted as they eat up too much memory."
},
"FX_TABLETMODE_PAGE_LOAD": {
"expires_in_version": "47",
"kind": "exponential",
@ -4725,6 +4731,7 @@
"kind": "linear",
"high": "13",
"n_buckets": 12,
"bug_numbers": [1162538],
"description": "Number of directories in the archive at scan"
},
"TELEMETRY_ARCHIVE_OLDEST_DIRECTORY_AGE": {
@ -4733,6 +4740,7 @@
"kind": "linear",
"high": "13",
"n_buckets": 12,
"bug_numbers": [1162538],
"description": "The age of the oldest Telemetry archive directory in months"
},
"TELEMETRY_ARCHIVE_SCAN_PING_COUNT": {
@ -4741,12 +4749,14 @@
"kind": "exponential",
"high": "100000",
"n_buckets": 100,
"bug_numbers": [1162538],
"description": "Number of Telemetry pings in the archive at scan"
},
"TELEMETRY_ARCHIVE_SESSION_PING_COUNT": {
"alert_emails": ["telemetry-client-dev@mozilla.com"],
"expires_in_version": "never",
"kind": "count",
"bug_numbers": [1162538],
"description": "Number of Telemetry pings added to the archive during the session"
},
"TELEMETRY_ARCHIVE_SIZE_MB": {
@ -4755,6 +4765,7 @@
"kind": "linear",
"high": "300",
"n_buckets": 60,
"bug_numbers": [1162538],
"description": "The size of the Telemetry archive (MB)"
},
"TELEMETRY_ARCHIVE_EVICTED_OVER_QUOTA": {
@ -4763,6 +4774,7 @@
"kind": "exponential",
"high": "100000",
"n_buckets": 100,
"bug_numbers": [1162538],
"description": "Number of Telemetry pings evicted from the archive during cleanup, because they were over the quota"
},
"TELEMETRY_ARCHIVE_EVICTED_OLD_DIRS": {
@ -4771,6 +4783,7 @@
"kind": "linear",
"high": "13",
"n_buckets": 12,
"bug_numbers": [1162538],
"description": "Number of Telemetry directories evicted from the archive during cleanup, because they were too old"
},
"TELEMETRY_ARCHIVE_EVICTING_DIRS_MS": {
@ -4779,6 +4792,7 @@
"kind": "exponential",
"high": "300000",
"n_buckets": 20,
"bug_numbers": [1162538],
"description": "Time (ms) it takes for evicting old directories"
},
"TELEMETRY_ARCHIVE_CHECKING_OVER_QUOTA_MS": {
@ -4787,6 +4801,7 @@
"kind": "exponential",
"high": "300000",
"n_buckets": 20,
"bug_numbers": [1162538],
"description": "Time (ms) it takes for checking if the archive is over-quota"
},
"TELEMETRY_ARCHIVE_EVICTING_OVER_QUOTA_MS": {
@ -4795,6 +4810,7 @@
"kind": "exponential",
"high": "300000",
"n_buckets": 20,
"bug_numbers": [1162538],
"description": "Time (ms) it takes for evicting over-quota pings"
},
"TELEMETRY_PENDING_LOAD_FAILURE_READ": {

View File

@ -0,0 +1,189 @@
[
"MEMORY_RESIDENT",
"MEMORY_JS_MAIN_RUNTIME_TEMPORARY_PEAK",
"MEMORY_JS_GC_HEAP",
"MEMORY_HEAP_ALLOCATED",
"SYSTEM_FONT_FALLBACK_SCRIPT",
"HTTP_REQUEST_PER_PAGE_FROM_CACHE",
"SSL_TIME_UNTIL_READY",
"SSL_TIME_UNTIL_HANDSHAKE_FINISHED",
"CERT_VALIDATION_HTTP_REQUEST_CANCELED_TIME",
"CERT_VALIDATION_HTTP_REQUEST_SUCCEEDED_TIME",
"CERT_VALIDATION_HTTP_REQUEST_FAILED_TIME",
"SSL_OBSERVED_END_ENTITY_CERTIFICATE_LIFETIME",
"SPDY_SERVER_INITIATED_STREAMS",
"STS_POLL_AND_EVENTS_CYCLE",
"STS_POLL_CYCLE",
"STS_POLL_AND_EVENT_THE_LAST_CYCLE",
"STS_POLL_BLOCK_TIME",
"PRCONNECT_BLOCKING_TIME_NORMAL",
"PRCONNECT_BLOCKING_TIME_SHUTDOWN",
"PRCONNECT_BLOCKING_TIME_CONNECTIVITY_CHANGE",
"PRCONNECT_BLOCKING_TIME_LINK_CHANGE",
"PRCONNECT_BLOCKING_TIME_OFFLINE",
"PRCONNECTCONTINUE_BLOCKING_TIME_NORMAL",
"PRCONNECTCONTINUE_BLOCKING_TIME_SHUTDOWN",
"PRCONNECTCONTINUE_BLOCKING_TIME_CONNECTIVITY_CHANGE",
"PRCONNECTCONTINUE_BLOCKING_TIME_LINK_CHANGE",
"PRCONNECTCONTINUE_BLOCKING_TIME_OFFLINE",
"PRCLOSE_TCP_BLOCKING_TIME_NORMAL",
"PRCLOSE_TCP_BLOCKING_TIME_SHUTDOWN",
"PRCLOSE_TCP_BLOCKING_TIME_CONNECTIVITY_CHANGE",
"PRCLOSE_TCP_BLOCKING_TIME_LINK_CHANGE",
"PRCLOSE_TCP_BLOCKING_TIME_OFFLINE",
"PRCLOSE_UDP_BLOCKING_TIME_NORMAL",
"PRCLOSE_UDP_BLOCKING_TIME_SHUTDOWN",
"PRCLOSE_UDP_BLOCKING_TIME_CONNECTIVITY_CHANGE",
"PRCLOSE_UDP_BLOCKING_TIME_LINK_CHANGE",
"PRCLOSE_UDP_BLOCKING_TIME_OFFLINE",
"UPDATE_PREF_UPDATE_CANCELATIONS_EXTERNAL",
"UPDATE_PREF_UPDATE_CANCELATIONS_NOTIFY",
"UPDATE_STATUS_ERROR_CODE_COMPLETE_STARTUP",
"UPDATE_STATUS_ERROR_CODE_PARTIAL_STARTUP",
"UPDATE_STATUS_ERROR_CODE_UNKNOWN_STARTUP",
"UPDATE_STATUS_ERROR_CODE_COMPLETE_STAGE",
"UPDATE_STATUS_ERROR_CODE_PARTIAL_STAGE",
"UPDATE_STATUS_ERROR_CODE_UNKNOWN_STAGE",
"SECURITY_UI",
"HEALTHREPORT_PAYLOAD_UNCOMPRESSED_BYTES",
"HEALTHREPORT_PAYLOAD_COMPRESSED_BYTES",
"CRASH_STORE_COMPRESSED_BYTES",
"SHUMWAY_FEATURE_USED",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_RELOAD_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_RELOAD_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_NAVIGATETO_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_NAVIGATETO_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_EVENTLISTENERS_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_EVENTLISTENERS_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_DETACH_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_DETACH_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_RESUME_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_RESUME_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_INTERRUPT_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_INTERRUPT_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_CLIENTEVALUATE_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_CLIENTEVALUATE_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_RELEASEMANY_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_RELEASEMANY_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_THREADGRIPS_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_THREADGRIPS_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_SOURCES_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_SOURCES_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_FRAMES_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_FRAMES_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_PARAMETERNAMES_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_PARAMETERNAMES_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_OWNPROPERTYNAMES_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_OWNPROPERTYNAMES_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_PROTOTYPEANDPROPERTIES_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_PROTOTYPEANDPROPERTIES_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_ENUMPROPERTIES_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_ENUMPROPERTIES_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_PROTOTYPESANDPROPERTIES_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_PROTOTYPESANDPROPERTIES_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_PROPERTY_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_PROPERTY_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_PROTOTYPE_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_PROTOTYPE_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_DISPLAYSTRING_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_DISPLAYSTRING_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_SUBSTRING_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_SUBSTRING_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_RELEASE_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_RELEASE_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_LISTTABS_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_LISTTABS_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_PROTOCOLDESCRIPTION_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_PROTOCOLDESCRIPTION_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_LISTADDONS_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_LISTADDONS_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_LISTWORKERS_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_LISTWORKERS_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_LISTPROCESSES_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_LISTPROCESSES_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_DELETE_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_DELETE_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_THREADDETACH_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_THREADDETACH_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_ADDONDETACH_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_ADDONDETACH_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_TABDETACH_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_TABDETACH_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_WORKERDETACH_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_WORKERDETACH_MS",
"DEVTOOLS_DEBUGGER_DISPLAY_SOURCE_LOCAL_MS",
"DEVTOOLS_DEBUGGER_DISPLAY_SOURCE_REMOTE_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_RECONFIGURETAB_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_RECONFIGURETAB_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_RECONFIGURETHREAD_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_RECONFIGURETHREAD_MS",
"MEDIA_WMF_DECODE_ERROR",
"VIDEO_CANPLAYTYPE_H264_CONSTRAINT_SET_FLAG",
"VIDEO_CANPLAYTYPE_H264_PROFILE",
"VIDEO_DECODED_H264_SPS_CONSTRAINT_SET_FLAG",
"VIDEO_DECODED_H264_SPS_PROFILE",
"WEBRTC_CANDIDATE_TYPES_GIVEN_SUCCESS",
"WEBRTC_CANDIDATE_TYPES_GIVEN_FAILURE",
"WEBRTC_AVSYNC_WHEN_AUDIO_LAGS_VIDEO_MS",
"WEBRTC_AVSYNC_WHEN_VIDEO_LAGS_AUDIO_MS",
"WEBRTC_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS",
"WEBRTC_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS",
"WEBRTC_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS",
"WEBRTC_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS",
"WEBRTC_AUDIO_QUALITY_INBOUND_JITTER",
"WEBRTC_VIDEO_QUALITY_OUTBOUND_JITTER",
"WEBRTC_AUDIO_QUALITY_OUTBOUND_JITTER",
"WEBRTC_VIDEO_ERROR_RECOVERY_MS",
"WEBRTC_VIDEO_RECOVERY_BEFORE_ERROR_PER_MIN",
"WEBRTC_VIDEO_RECOVERY_AFTER_ERROR_PER_MIN",
"WEBRTC_VIDEO_QUALITY_OUTBOUND_RTT",
"WEBRTC_AUDIO_QUALITY_OUTBOUND_RTT",
"WEBRTC_CALL_DURATION",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_TRACERDETACH_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_TRACERDETACH_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_STARTTRACE_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_STARTTRACE_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_STOPTRACE_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_STOPTRACE_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_GET_EXECUTABLE_LINES_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_GET_EXECUTABLE_LINES_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_BLACKBOX_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_BLACKBOX_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_UNBLACKBOX_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_UNBLACKBOX_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_SCOPE_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_SCOPE_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_BINDINGS_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_BINDINGS_MS",
"DEVTOOLS_DEBUGGER_RDP_LOCAL_ASSIGN_MS",
"DEVTOOLS_DEBUGGER_RDP_REMOTE_ASSIGN_MS",
"DEVTOOLS_SAVE_HEAP_SNAPSHOT_MS",
"DEVTOOLS_READ_HEAP_SNAPSHOT_MS",
"DEVTOOLS_HEAP_SNAPSHOT_NODE_COUNT",
"DEVTOOLS_HEAP_SNAPSHOT_EDGE_COUNT",
"NETWORK_CACHE_HIT_RATE_PER_CACHE_SIZE",
"NETWORK_CACHE_METADATA_FIRST_READ_SIZE",
"NETWORK_CACHE_METADATA_SIZE",
"NETWORK_CACHE_HASH_STATS",
"SSL_CIPHER_SUITE_FULL",
"SSL_CIPHER_SUITE_RESUMED",
"SSL_REASONS_FOR_NOT_FALSE_STARTING",
"SSL_CERT_VERIFICATION_ERRORS",
"CERT_VALIDATION_SUCCESS_BY_CA",
"CERT_PINNING_FAILURES_BY_CA",
"CERT_PINNING_MOZ_RESULTS_BY_HOST",
"CERT_PINNING_MOZ_TEST_RESULTS_BY_HOST",
"LOOP_CANDIDATE_TYPES_GIVEN_SUCCESS",
"LOOP_CANDIDATE_TYPES_GIVEN_FAILURE",
"LOOP_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS",
"LOOP_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS",
"LOOP_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS",
"LOOP_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS",
"LOOP_VIDEO_ERROR_RECOVERY_MS",
"LOOP_VIDEO_RECOVERY_BEFORE_ERROR_PER_MIN",
"LOOP_VIDEO_RECOVERY_AFTER_ERROR_PER_MIN",
"LOOP_VIDEO_QUALITY_OUTBOUND_RTT",
"LOOP_AUDIO_QUALITY_OUTBOUND_RTT",
"LOOP_CALL_DURATION",
"GFX_CRASH"
]

View File

@ -75,7 +75,20 @@ def exponential_buckets(dmin, dmax, n_buckets):
return ret_array
always_allowed_keys = ['kind', 'description', 'cpp_guard', 'expires_in_version',
'alert_emails', 'keyed', 'releaseChannelCollection']
'alert_emails', 'keyed', 'releaseChannelCollection',
'bug_numbers']
n_buckets_whitelist = None;
try:
whitelist_path = os.path.join(os.path.abspath(os.path.realpath(os.path.dirname(__file__))), 'bucket-whitelist.json')
with open(whitelist_path, 'r') as f:
try:
n_buckets_whitelist = set(json.load(f))
except ValueError, e:
raise BaseException, 'error parsing bucket whitelist (%s)' % whitelist_path
except IOError:
n_buckets_whitelist = None
print 'Unable to parse whitelist (%s). Assuming all histograms are acceptable.' % whitelist_path
class Histogram:
"""A class for representing a histogram definition."""
@ -209,6 +222,7 @@ is enabled."""
lambda allowed_keys: Histogram.check_keys(name, definition, allowed_keys))
Histogram.check_expiration(name, definition)
Histogram.check_bug_numbers(name, definition)
def check_name(self, name):
if '#' in name:
@ -228,6 +242,18 @@ is enabled."""
definition['expires_in_version'] = expiration
@staticmethod
def check_bug_numbers(name, definition):
bug_numbers = definition.get('bug_numbers')
if not bug_numbers:
return
if not isinstance(bug_numbers, list):
raise ValueError, 'bug_numbers field for "%s" should be an array' % (name)
if not all(type(num) is int for num in bug_numbers):
raise ValueError, 'bug_numbers array for "%s" should only contain integers' % (name)
@staticmethod
def check_keys(name, definition, allowed_keys):
for key in definition.iterkeys():
@ -243,6 +269,11 @@ is enabled."""
self._low = try_to_coerce_to_number(low)
self._high = try_to_coerce_to_number(high)
self._n_buckets = try_to_coerce_to_number(n_buckets)
if n_buckets_whitelist is not None and self._n_buckets > 100 and type(self._n_buckets) is int:
if self._name not in n_buckets_whitelist:
raise KeyError, ('New histogram %s is not permitted to have more than 100 buckets. '
'Histograms with large numbers of buckets use disproportionately high amounts of resources. '
'Contact :vladan or the Perf team if you think an exception ought to be made.' % self._name)
@staticmethod
def boolean_flag_bucket_parameters(definition):

View File

@ -158,7 +158,8 @@ this.PageThumbs = {
*/
getThumbnailURL: function PageThumbs_getThumbnailURL(aUrl) {
return this.scheme + "://" + this.staticHost +
"/?url=" + encodeURIComponent(aUrl);
"/?url=" + encodeURIComponent(aUrl) +
"&revision=" + PageThumbsStorage.getRevision(aUrl);
},
/**
@ -542,6 +543,44 @@ this.PageThumbsStorage = {
return OS.Path.join(this.path, this.getLeafNameForURL(aURL));
},
_revisionTable: {},
// Generate an arbitrary revision tag, i.e. one that can't be used to
// infer URL frecency.
_updateRevision(aURL) {
// Initialize with a random value and increment on each update. Wrap around
// modulo _revisionRange, so that even small values carry no meaning.
let rev = this._revisionTable[aURL];
if (rev == null)
rev = Math.floor(Math.random() * this._revisionRange);
this._revisionTable[aURL] = (rev + 1) % this._revisionRange;
},
// If two thumbnails with the same URL and revision are in cache at the
// same time, the image loader may pick the stale thumbnail in some cases.
// Therefore _revisionRange must be large enough to prevent this, e.g.
// in the pathological case image.cache.size (5MB by default) could fill
// with (abnormally small) 10KB thumbnail images if the browser session
// runs long enough (though this is unlikely as thumbnails are usually
// only updated every MAX_THUMBNAIL_AGE_SECS).
_revisionRange: 8192,
/**
* Return a revision tag for the thumbnail stored for a given URL.
*
* @param aURL The URL spec string
* @return A revision tag for the corresponding thumbnail. Returns a changed
* value whenever the stored thumbnail changes.
*/
getRevision(aURL) {
let rev = this._revisionTable[aURL];
if (rev == null) {
this._updateRevision(aURL);
rev = this._revisionTable[aURL];
}
return rev;
},
/**
* Write the contents of a thumbnail, off the main thread.
*
@ -571,7 +610,7 @@ this.PageThumbsStorage = {
msg /*we don't want that message garbage-collected,
as OS.Shared.Type.void_t.in_ptr.toMsg uses C-level
memory tricks to enforce zero-copy*/).
then(null, this._eatNoOverwriteError(aNoOverwrite));
then(() => this._updateRevision(aURL), this._eatNoOverwriteError(aNoOverwrite));
},
/**
@ -590,7 +629,7 @@ this.PageThumbsStorage = {
let targetFile = this.getFilePathForURL(aTargetURL);
let options = { noOverwrite: aNoOverwrite };
return PageThumbsWorker.post("copy", [sourceFile, targetFile, options]).
then(null, this._eatNoOverwriteError(aNoOverwrite));
then(() => this._updateRevision(aTargetURL), this._eatNoOverwriteError(aNoOverwrite));
},
/**

View File

@ -10,9 +10,10 @@
*
* URL structure:
*
* moz-page-thumb://thumbnail/?url=http%3A%2F%2Fwww.mozilla.org%2F
* moz-page-thumb://thumbnail/?url=http%3A%2F%2Fwww.mozilla.org%2F&revision=XX
*
* This URL requests an image for 'http://www.mozilla.org/'.
* The value of the revision key may change when the stored thumbnail changes.
*/
"use strict";

View File

@ -88,11 +88,6 @@ function capIfStaleErrorResponseUpdateTest() {
yield addTab(URL);
yield captureAndCheckColor(0, 255, 0, "we have a green thumbnail");
// image cache entry timestamps have second resolution
// so make sure the second part of this test takes part in a different second.
yield wait(2000);
// update the thumbnail to be stale, then re-request it. The server will
// return a 400 response and a red thumbnail.
// The service should not save the thumbnail - so we (a) check the thumbnail
@ -124,11 +119,6 @@ function capIfStaleGoodResponseUpdateTest() {
let browser = gBrowser.selectedBrowser;
yield captureAndCheckColor(0, 255, 0, "we have a green thumbnail");
// image cache entry timestamps have second resolution
// so make sure the second part of this test takes part in a different second.
yield wait(2000);
// update the thumbnail to be stale, then re-request it. The server will
// return a 200 response and a red thumbnail - so that new thumbnail should
// end up captured.
@ -158,11 +148,6 @@ function regularCapErrorResponseUpdateTest() {
yield addTab(URL);
yield captureAndCheckColor(0, 255, 0, "we have a green thumbnail");
gBrowser.removeTab(gBrowser.selectedTab);
// image cache entry timestamps have second resolution
// so make sure the second part of this test takes part in a different second.
yield wait(2000);
// do it again - the server will return a 400, so the foreground service
// should not update it.
yield addTab(URL);
@ -177,11 +162,6 @@ function regularCapGoodResponseUpdateTest() {
yield addTab(URL);
yield captureAndCheckColor(0, 255, 0, "we have a green thumbnail");
gBrowser.removeTab(gBrowser.selectedTab);
// image cache entry timestamps have second resolution
// so make sure the second part of this test takes part in a different second.
yield wait(2000);
// do it again - the server will return a 200, so the foreground service
// should update it.
yield addTab(URL);

View File

@ -145,33 +145,21 @@ function captureAndCheckColor(aRed, aGreen, aBlue, aMessage) {
function retrieveImageDataForURL(aURL, aCallback) {
let width = 100, height = 100;
let thumb = PageThumbs.getThumbnailURL(aURL, width, height);
// create a tab with a chrome:// URL so it can host the thumbnail image.
// Note that we tried creating the element directly in the top-level chrome
// document, but this caused a strange problem:
// * call this with the url of an image.
// * immediately change the image content.
// * call this again with the same url (now holding different content)
// The original image data would be used. Maybe the img hadn't been
// collected yet and the platform noticed the same URL, so reused the
// content? Not sure - but this solves the problem.
addTab("chrome://global/content/mozilla.xhtml", () => {
let doc = gBrowser.selectedBrowser.contentDocument;
let htmlns = "http://www.w3.org/1999/xhtml";
let img = doc.createElementNS(htmlns, "img");
img.setAttribute("src", thumb);
whenLoaded(img, function () {
let canvas = document.createElementNS(htmlns, "canvas");
canvas.setAttribute("width", width);
canvas.setAttribute("height", height);
let htmlns = "http://www.w3.org/1999/xhtml";
let img = document.createElementNS(htmlns, "img");
img.setAttribute("src", thumb);
// Draw the image to a canvas and compare the pixel color values.
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
let result = ctx.getImageData(0, 0, 100, 100).data;
gBrowser.removeTab(gBrowser.selectedTab);
aCallback(result);
});
whenLoaded(img, function () {
let canvas = document.createElementNS(htmlns, "canvas");
canvas.setAttribute("width", width);
canvas.setAttribute("height", height);
// Draw the image to a canvas and compare the pixel color values.
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
let result = ctx.getImageData(0, 0, 100, 100).data;
aCallback(result);
});
}

View File

@ -174,7 +174,7 @@
</setter>
</property>
<field name="selectedItems">[]</field>
<field name="selectedItems">new ChromeNodeList()</field>
<method name="addItemToSelection">
<parameter name="aItem"/>
@ -186,7 +186,7 @@
if (aItem.selected)
return;
this.selectedItems.push(aItem);
this.selectedItems.append(aItem);
aItem.selected = true;
this._fireOnSelect();
@ -201,14 +201,8 @@
if (!aItem.selected)
return;
for (var i = 0; i < this.selectedItems.length; ++i) {
if (this.selectedItems[i] == aItem) {
this.selectedItems.splice(i, 1);
aItem.selected = false;
break;
}
}
this.selectedItems.remove(aItem);
aItem.selected = false;
this._fireOnSelect();
]]>
</body>
@ -355,10 +349,11 @@
<body>
<![CDATA[
if (this.selectedItems) {
for (var i = this.selectedItems.length - 1; i >= 0; --i)
this.selectedItems[i].selected = false;
this.selectedItems.length = 0;
while (this.selectedItems.length > 0) {
let item = this.selectedItems[0];
item.selected = false;
this.selectedItems.remove(item);
}
}
this._selectionStart = null;
@ -700,7 +695,7 @@
for (var index = 0; index < count; index++) {
var item = this.getItemAtIndex(index);
if (item.getAttribute("selected") == "true")
this.selectedItems.push(item);
this.selectedItems.append(item);
}
]]>
</constructor>

View File

@ -61,7 +61,7 @@
var state = this.currentItem ? this.currentItem.id : "";
if (this.selType == "multiple" && this.selectedCount) {
let getId = function getId(aItem) { return aItem.id; }
state += " " + this.selectedItems.filter(getId).map(getId).join(" ");
state += " " + [... this.selectedItems].filter(getId).map(getId).join(" ");
}
if (state)
this.setAttribute("last-selected", state);
@ -393,13 +393,17 @@
// try to restore the selected items according to their IDs
// (applies after a template rebuild, if last-selected was not set)
if (this.selectedItems) {
let itemIds = [];
for (i = this.selectedCount - 1; i >= 0; i--) {
if (this.selectedItems[i] && this.selectedItems[i].id)
this.selectedItems[i] = document.getElementById(this.selectedItems[i].id);
else
this.selectedItems[i] = null;
if (!this.selectedItems[i])
this.selectedItems.splice(i, 1);
let selectedItem = this.selectedItems[i];
itemIds.push(selectedItem.id);
this.selectedItems.remove(selectedItem);
}
for (i = 0; i < itemIds.length; i++) {
let selectedItem = document.getElementById(itemIds[i]);
if (selectedItem) {
this.selectedItems.append(selectedItem);
}
}
}
if (this.currentItem && this.currentItem.id)
@ -417,7 +421,7 @@
var children = this.children;
for (var i = 0; i < children.length; ++i) {
if (children[i].getAttribute("selected") == "true")
this.selectedItems.push(children[i]);
this.selectedItems.append(children[i]);
}
}

View File

@ -871,10 +871,86 @@ function releaseSDCardMountLock() {
* @return true if the service should be used for updates.
*/
function shouldUseService() {
if (AppConstants.MOZ_MAINTENANCE_SERVICE && isServiceInstalled()) {
return getPref("getBoolPref",
PREF_APP_UPDATE_SERVICE_ENABLED, false);
// This function will return true if the mantenance service should be used if
// all of the following conditions are met:
// 1) This build was done with the maintenance service enabled
// 2) The maintenance service is installed
// 3) The pref for using the service is enabled
// 4) The Windows version is XP Service Pack 3 or above (for SHA-2 support)
// The maintenance service requires SHA-2 support because we sign our binaries
// with a SHA-2 certificate and the certificate is verified before the binary
// is launched.
if (!AppConstants.MOZ_MAINTENANCE_SERVICE || !isServiceInstalled() ||
!getPref("getBoolPref", PREF_APP_UPDATE_SERVICE_ENABLED, false) ||
!AppConstants.isPlatformAndVersionAtLeast("win", "5.1") /* WinXP */) {
return false;
}
// If it's newer than XP, then the service pack doesn't matter.
if (Services.sysinfo.getProperty("version") != "5.1") {
return true;
}
// If the Windows version is XP, we also need to check the service pack.
// We'll return false if only < SP3 is installed, or if we can't tell.
// Check the service pack level by calling GetVersionEx via ctypes.
const BYTE = ctypes.uint8_t;
const WORD = ctypes.uint16_t;
const DWORD = ctypes.uint32_t;
const WCHAR = ctypes.char16_t;
const BOOL = ctypes.int;
// This structure is described at:
// http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
const SZCSDVERSIONLENGTH = 128;
const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
[
{dwOSVersionInfoSize: DWORD},
{dwMajorVersion: DWORD},
{dwMinorVersion: DWORD},
{dwBuildNumber: DWORD},
{dwPlatformId: DWORD},
{szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
{wServicePackMajor: WORD},
{wServicePackMinor: WORD},
{wSuiteMask: WORD},
{wProductType: BYTE},
{wReserved: BYTE}
]);
let kernel32 = false;
try {
kernel32 = ctypes.open("Kernel32");
} catch (e) {
Cu.reportError("Unable to open kernel32! " + e);
return false;
}
if (kernel32) {
try {
try {
let GetVersionEx = kernel32.declare("GetVersionExW",
ctypes.default_abi,
BOOL,
OSVERSIONINFOEXW.ptr);
let winVer = OSVERSIONINFOEXW();
winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
if (0 !== GetVersionEx(winVer.address())) {
return winVer.wServicePackMajor >= 3;
} else {
Cu.reportError("Unknown failure in GetVersionEX (returned 0)");
return false;
}
} catch (e) {
Cu.reportError("Error getting service pack information. Exception: " + e);
return false;
}
} finally {
kernel32.close();
}
}
// If the service pack check couldn't be done, assume we can't use the service.
return false;
}