Merge fx-team to central, a=merge

This commit is contained in:
Wes Kocher 2015-09-09 13:43:01 -07:00
commit 3e0ec8b89f
41 changed files with 811 additions and 149 deletions

View File

@ -128,10 +128,6 @@
<description class="identity-popup-warning-yellow"
when-ciphers="weak">&identity.description.weakCipher2;</description>
<!-- More Security Information -->
<button label="&identity.moreInfoLinkText2;"
oncommand="gIdentityHandler.handleMoreInfoClick(event);"/>
<!-- Active Mixed Content Blocked -->
<description class="identity-popup-warning-gray"
when-mixedcontent="active-blocked">&identity.description.activeBlocked; <label observes="identity-popup-mcb-learn-more"/></description>
@ -159,6 +155,10 @@
label="&identity.enableMixedContentBlocking.label;"
accesskey="&identity.enableMixedContentBlocking.accesskey;"
oncommand="gIdentityHandler.enableMixedContentProtection()"/>
<!-- More Security Information -->
<button label="&identity.moreInfoLinkText2;"
oncommand="gIdentityHandler.handleMoreInfoClick(event);"/>
</vbox>
</panelview>

View File

@ -980,7 +980,7 @@ AnimationsTimeline.prototype = {
createNode({
parent: iterations,
attributes: {
"class": "delay",
"class": "delay" + (delay < 0 ? " negative" : ""),
"style": `left:-${x}px;
width:${w}px;`
}

View File

@ -5,11 +5,14 @@ support-files =
doc_body_animation.html
doc_frame_script.js
doc_modify_playbackRate.html
doc_negative_animation.html
doc_simple_animation.html
head.js
[browser_animation_controller_exposes_document_currentTime.js]
[browser_animation_empty_on_invalid_nodes.js]
[browser_animation_iterationCount_hidden_by_default.js]
[browser_animation_mutations_with_same_names.js]
[browser_animation_panel_exists.js]
[browser_animation_participate_in_inspector_update.js]
[browser_animation_play_pause_button.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 the controller provides the document.timeline currentTime (at least
// the last known version since new animations were added).
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {panel, controller} = yield openAnimationInspectorNewUI();
ok(controller.documentCurrentTime, "The documentCurrentTime getter exists");
checkDocumentTimeIsCorrect(controller);
let time1 = controller.documentCurrentTime;
yield startNewAnimation(controller, panel);
checkDocumentTimeIsCorrect(controller);
let time2 = controller.documentCurrentTime;
ok(time2 > time1, "The new documentCurrentTime is higher than the old one");
});
function checkDocumentTimeIsCorrect(controller) {
let time = 0;
for (let {state} of controller.animationPlayers) {
time = Math.max(time, state.documentCurrentTime);
}
is(controller.documentCurrentTime, time,
"The documentCurrentTime is correct");
}
function* startNewAnimation(controller, panel) {
info("Add a new animation to the page and check the time again");
let onPlayerAdded = controller.once(controller.PLAYERS_UPDATED_EVENT);
yield executeInContent("devtools:test:setAttribute", {
selector: ".still",
attributeName: "class",
attributeValue: "ball still short"
});
yield onPlayerAdded;
yield waitForAllAnimationTargets(panel);
}

View File

@ -0,0 +1,31 @@
/* 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";
// Check that when animations are added later (through animation mutations) and
// if these animations have the same names, then all of them are still being
// displayed (which should be true as long as these animations apply to
// different nodes).
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_negative_animation.html");
let {controller, panel} = yield openAnimationInspectorNewUI();
info("Wait until all animations have been added " +
"(they're added with setTimeout)");
while (controller.animationPlayers.length < 3) {
yield controller.once(controller.PLAYERS_UPDATED_EVENT);
}
yield waitForAllAnimationTargets(panel);
is(panel.animationsTimelineComponent.animations.length, 3,
"The timeline shows 3 animations too");
// Reduce the known nodeFronts to a set to make them unique.
let nodeFronts = new Set(panel.animationsTimelineComponent
.targetNodes.map(n => n.nodeFront));
is(nodeFronts.size, 3,
"The animations are applied to 3 different node fronts");
});

View File

@ -20,10 +20,11 @@ add_task(function*() {
ok(!timeline.hasAttribute("disabled"), "The timeline input[range] is enabled");
ok(widget.setCurrentTime, "The widget has the setCurrentTime method");
ok(widget.player.setCurrentTime, "The associated player front has the setCurrentTime method");
ok(widget.player.setCurrentTime,
"The associated player front has the setCurrentTime method");
info("Faking an older server version by setting " +
"AnimationsController.traits.hasSetCurrentTime to false");
"AnimationsController.traits.hasSetCurrentTime to false");
yield selectNode("body", inspector);
controller.traits.hasSetCurrentTime = false;
@ -35,7 +36,4 @@ add_task(function*() {
timeline = widget.currentTimeEl;
ok(timeline.hasAttribute("disabled"), "The timeline input[range] is disabled");
yield selectNode("body", inspector);
controller.traits.hasSetCurrentTime = true;
});

View File

@ -47,6 +47,7 @@ function* testRefreshOnRemove(inspector, panel) {
attributeValue: "ball short test-node"
});
yield onPanelUpdated;
yield waitForAllAnimationTargets(panel);
assertAnimationsDisplayed(panel, 1);
}

View File

@ -9,7 +9,6 @@
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspectorNewUI();
yield waitForAllAnimationTargets(panel);
let timeline = panel.animationsTimelineComponent;
let scrubberEl = timeline.scrubberEl;

View File

@ -11,7 +11,6 @@ add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspectorNewUI();
yield waitForAllAnimationTargets(panel);
let timeline = panel.animationsTimelineComponent;
let win = timeline.win;
@ -20,17 +19,17 @@ add_task(function*() {
info("Mousedown in the header to move the scrubber");
EventUtils.synthesizeMouse(timeHeaderEl, 50, 1, {type: "mousedown"}, win);
let newPos = parseInt(scrubberEl.style.left);
let newPos = parseInt(scrubberEl.style.left, 10);
is(newPos, 50, "The scrubber moved on mousedown");
info("Continue moving the mouse and verify that the scrubber tracks it");
EventUtils.synthesizeMouse(timeHeaderEl, 100, 1, {type: "mousemove"}, win);
newPos = parseInt(scrubberEl.style.left);
newPos = parseInt(scrubberEl.style.left, 10);
is(newPos, 100, "The scrubber followed the mouse");
info("Release the mouse and move again and verify that the scrubber stays");
EventUtils.synthesizeMouse(timeHeaderEl, 100, 1, {type: "mouseup"}, win);
EventUtils.synthesizeMouse(timeHeaderEl, 200, 1, {type: "mousemove"}, win);
newPos = parseInt(scrubberEl.style.left);
newPos = parseInt(scrubberEl.style.left, 10);
is(newPos, 100, "The scrubber stopped following the mouse");
});

View File

@ -12,15 +12,10 @@
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspectorNewUI();
yield waitForAllAnimationTargets(panel);
let timeline = panel.animationsTimelineComponent;
let win = timeline.win;
let timeHeaderEl = timeline.timeHeaderEl;
let scrubberEl = timeline.scrubberEl;
let startPos = scrubberEl.getBoundingClientRect().left;
info("Wait for some time to check that the scrubber moves");

View File

@ -6,6 +6,8 @@
// Check that animation delay is visualized in the timeline-based UI when the
// animation is delayed.
// Also check that negative delays do not overflow the UI, and are shown like
// positive delays.
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
@ -13,18 +15,38 @@ add_task(function*() {
info("Selecting a delayed animated node");
yield selectNode(".delayed", inspector);
info("Getting the animation and delay elements from the panel");
let timelineEl = panel.animationsTimelineComponent.rootWrapperEl;
let delay = timelineEl.querySelector(".delay");
ok(delay, "The animation timeline contains the delay element");
checkDelayAndName(timelineEl, true);
info("Selecting a no-delay animated node");
yield selectNode(".animated", inspector);
checkDelayAndName(timelineEl, false);
info("Getting the animation and delay elements from the panel again");
delay = timelineEl.querySelector(".delay");
ok(!delay, "The animation timeline contains no delay element");
info("Selecting a negative-delay animated node");
yield selectNode(".negative-delay", inspector);
checkDelayAndName(timelineEl, true);
});
function checkDelayAndName(timelineEl, hasDelay) {
let delay = timelineEl.querySelector(".delay");
is(!!delay, hasDelay, "The timeline " +
(hasDelay ? "contains" : "does not contain") +
" a delay element, as expected");
if (hasDelay) {
let name = timelineEl.querySelector(".name");
let targetNode = timelineEl.querySelector(".target");
// Check that the delay element does not cause the timeline to overflow.
let delayRect = delay.getBoundingClientRect();
let sidebarWidth = targetNode.getBoundingClientRect().width;
ok(delayRect.x >= sidebarWidth,
"The delay element isn't displayed over the sidebar");
// Check that the delay is not displayed on top of the name.
let nameLeft = name.getBoundingClientRect().left;
ok(delayRect.right <= nameLeft,
"The delay element does not span over the name element");
}
}

View File

@ -10,7 +10,6 @@
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspectorNewUI();
yield waitForAllAnimationTargets(panel);
info("Getting the animation element from the panel");
let timelineEl = panel.animationsTimelineComponent.rootWrapperEl;
@ -22,8 +21,8 @@ add_task(function*() {
ok(el.hasAttribute("title"), "The tooltip is defined");
let title = el.getAttribute("title");
ok(title.match(/Delay: [\d.]+s/), "The tooltip shows the delay");
ok(title.match(/Duration: [\d.]+s/), "The tooltip shows the delay");
ok(title.match(/Delay: [\d.-]+s/), "The tooltip shows the delay");
ok(title.match(/Duration: [\d.]+s/), "The tooltip shows the duration");
ok(title.match(/Repeats: /), "The tooltip shows the iterations");
}
});

View File

@ -15,7 +15,6 @@ add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_modify_playbackRate.html");
let {panel} = yield openAnimationInspectorNewUI();
yield waitForAllAnimationTargets(panel);
let timelineEl = panel.animationsTimelineComponent.rootWrapperEl;

View File

@ -23,9 +23,9 @@ function* testDataUpdates({panel, controller, inspector}, isNewUI=false) {
yield selectNode(".animated", inspector);
let animation = controller.animationPlayers[0];
yield setStyle(animation, "animationDuration", "5.5s", isNewUI);
yield setStyle(animation, "animationIterationCount", "300", isNewUI);
yield setStyle(animation, "animationDelay", "45s", isNewUI);
yield setStyle(animation, panel, "animationDuration", "5.5s", isNewUI);
yield setStyle(animation, panel, "animationIterationCount", "300", isNewUI);
yield setStyle(animation, panel, "animationDelay", "45s", isNewUI);
if (isNewUI) {
let animationsEl = panel.animationsTimelineComponent.animationsEl;
@ -52,7 +52,7 @@ function* testDataUpdates({panel, controller, inspector}, isNewUI=false) {
}
}
function* setStyle(animation, name, value, isNewUI=false) {
function* setStyle(animation, panel, name, value, isNewUI=false) {
info("Change the animation style via the content DOM. Setting " +
name + " to " + value);
@ -64,6 +64,10 @@ function* setStyle(animation, name, value, isNewUI=false) {
});
yield onAnimationChanged;
// Also wait for the target node previews to be loaded if the panel got
// refreshed as a result of this animation mutation.
yield waitForAllAnimationTargets(panel);
// If this is the playerWidget-based UI, wait for the auto-refresh event too
// to make sure the UI has updated.
if (!isNewUI) {

View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
html, body {
margin: 0;
height: 100%;
overflow: hidden;
}
div {
position: absolute;
top: 0;
left: -500px;
height: 20px;
width: 500px;
color: red;
background: linear-gradient(to left, currentColor, currentColor 2px, transparent);
}
.zero {
color: blue;
top: 20px;
}
.positive {
color: green;
top: 40px;
}
.negative.move { animation: 5s -1s move linear forwards; }
.zero.move { animation: 5s 0s move linear forwards; }
.positive.move { animation: 5s 1s move linear forwards; }
@keyframes move {
to {
transform: translateX(500px);
}
}
</style>
</head>
<body>
<div class="negative"></div>
<div class="zero"></div>
<div class="positive"></div>
<script>
var negative = document.querySelector(".negative");
var zero = document.querySelector(".zero");
var positive = document.querySelector(".positive");
// The non-delayed animation starts now.
zero.classList.add("move");
// The negative-delayed animation starts in 1 second.
setTimeout(function() {
negative.classList.add("move");
}, 1000);
// The positive-delayed animation starts in 200 ms.
setTimeout(function() {
positive.classList.add("move");
}, 200);
</script>
</body>
</html>

View File

@ -65,6 +65,15 @@
animation: simple-animation 120s;
}
.negative-delay {
top: 700px;
left: 10px;
background: gray;
animation: simple-animation 15s -10s;
animation-fill-mode: forwards;
}
@keyframes simple-animation {
100% {
transform: translateX(300px);
@ -87,5 +96,6 @@
<div class="ball multi-finite"></div>
<div class="ball short"></div>
<div class="ball long"></div>
<div class="ball negative-delay"></div>
</body>
</html>

View File

@ -21,6 +21,7 @@ const ROOT_TEST_DIR = getRootDirectory(gTestPath);
const FRAME_SCRIPT_URL = ROOT_TEST_DIR + "doc_frame_script.js";
const COMMON_FRAME_SCRIPT_URL = "chrome://browser/content/devtools/frame-script-utils.js";
const NEW_UI_PREF = "devtools.inspector.animationInspectorV3";
const TAB_NAME = "animationinspector";
// Auto clean-up when a test ends
registerCleanupFunction(function*() {
@ -129,6 +130,13 @@ let selectNode = Task.async(function*(data, inspector, reason="test") {
let updated = inspector.once("inspector-updated");
inspector.selection.setNodeFront(nodeFront, reason);
yield updated;
// 99% of the times, selectNode is called to select an animated node, and we
// want to make sure the rest of the test waits for the animations to be
// properly displayed (wait for all target DOM nodes to be previewed).
// Even if there are no animations, this is safe to do.
let {AnimationsPanel} = inspector.sidebar.getWindowForTab(TAB_NAME);
yield waitForAllAnimationTargets(AnimationsPanel);
});
/**
@ -159,7 +167,7 @@ function assertAnimationsDisplayed(panel, nbAnimations, msg="") {
* @return {Promise}
*/
let waitForAnimationInspectorReady = Task.async(function*(inspector) {
let win = inspector.sidebar.getWindowForTab("animationinspector");
let win = inspector.sidebar.getWindowForTab(TAB_NAME);
let updated = inspector.once("inspector-updated");
// In e10s, if we wait for underlying toolbox actors to
@ -192,12 +200,12 @@ let openAnimationInspector = Task.async(function*() {
info("Waiting for toolbox focus");
yield waitForToolboxFrameFocus(toolbox);
inspector.sidebar.select("animationinspector");
inspector.sidebar.select(TAB_NAME);
info("Waiting for the inspector and sidebar to be ready");
yield panelReady;
let win = inspector.sidebar.getWindowForTab("animationinspector");
let win = inspector.sidebar.getWindowForTab(TAB_NAME);
let {AnimationsController, AnimationsPanel} = win;
info("Waiting for the animation controller and panel to be ready");
@ -207,6 +215,11 @@ let openAnimationInspector = Task.async(function*() {
yield AnimationsPanel.once(AnimationsPanel.PANEL_INITIALIZED);
}
// Make sure we wait for all animations to be loaded (especially their target
// nodes to be lazily displayed). This is safe to do even if there are no
// animations displayed.
yield waitForAllAnimationTargets(AnimationsPanel);
return {
toolbox: toolbox,
inspector: inspector,
@ -250,11 +263,9 @@ let closeAnimationInspectorAndRestartWithNewUI = Task.async(function*(reload) {
if (reload) {
yield reloadTab();
}
enableNewUI();
return yield openAnimationInspector();
return yield openAnimationInspectorNewUI();
});
/**
* Wait for the toolbox frame to receive focus after it loads
* @param {Toolbox} toolbox

View File

@ -10,26 +10,51 @@ const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const {TimeScale} = require("devtools/animationinspector/components");
const TEST_ANIMATIONS = [{
startTime: 500,
delay: 0,
duration: 1000,
iterationCount: 1,
playbackRate: 1
desc: "Testing a few standard animations",
animations: [{
startTime: 500,
delay: 0,
duration: 1000,
iterationCount: 1,
playbackRate: 1
}, {
startTime: 400,
delay: 100,
duration: 10,
iterationCount: 100,
playbackRate: 1
}, {
startTime: 50,
delay: 1000,
duration: 100,
iterationCount: 20,
playbackRate: 1
}],
expectedMinStart: 50,
expectedMaxEnd: 3050
}, {
startTime: 400,
delay: 100,
duration: 10,
iterationCount: 100,
playbackRate: 1
desc: "Testing a single negative-delay animation",
animations: [{
startTime: 100,
delay: -100,
duration: 100,
iterationCount: 1,
playbackRate: 1
}],
expectedMinStart: 0,
expectedMaxEnd: 100
}, {
startTime: 50,
delay: 1000,
duration: 100,
iterationCount: 20,
playbackRate: 1
desc: "Testing a single negative-delay animation with a different rate",
animations: [{
startTime: 3500,
delay: -1000,
duration: 10000,
iterationCount: 2,
playbackRate: 2
}],
expectedMinStart: 3000,
expectedMaxEnd: 13000
}];
const EXPECTED_MIN_START = 50;
const EXPECTED_MAX_END = 3050;
const TEST_STARTTIME_TO_DISTANCE = [{
time: 50,
@ -126,24 +151,27 @@ function run_test() {
equal(TimeScale.minStartTime, Infinity);
equal(TimeScale.maxEndTime, 0);
do_print("Test adding a few animations");
for (let state of TEST_ANIMATIONS) {
for (let {desc, animations, expectedMinStart, expectedMaxEnd} of
TEST_ANIMATIONS) {
do_print("Test adding a few animations: " + desc);
for (let state of animations) {
TimeScale.addAnimation(state);
}
do_print("Checking the time scale range");
equal(TimeScale.minStartTime, expectedMinStart);
equal(TimeScale.maxEndTime, expectedMaxEnd);
do_print("Test reseting the animations");
TimeScale.reset();
equal(TimeScale.minStartTime, Infinity);
equal(TimeScale.maxEndTime, 0);
}
do_print("Add a set of animations again");
for (let state of TEST_ANIMATIONS[0].animations) {
TimeScale.addAnimation(state);
}
equal(TimeScale.minStartTime, EXPECTED_MIN_START);
equal(TimeScale.maxEndTime, EXPECTED_MAX_END);
do_print("Test reseting the animations");
TimeScale.reset();
equal(TimeScale.minStartTime, Infinity);
equal(TimeScale.maxEndTime, 0);
do_print("Test adding the animations again");
for (let state of TEST_ANIMATIONS) {
TimeScale.addAnimation(state);
}
equal(TimeScale.minStartTime, EXPECTED_MIN_START);
equal(TimeScale.maxEndTime, EXPECTED_MAX_END);
do_print("Test converting start times to distances");
for (let {time, width, expectedDistance} of TEST_STARTTIME_TO_DISTANCE) {

View File

@ -120,8 +120,8 @@
}
#PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
background-color: transparent;
background-image: url(chrome://browser/skin/warning.svg);
background: transparent url(chrome://browser/skin/warning.svg) no-repeat center;
height: 13px;
box-shadow: none;
filter: drop-shadow(0 1px 0 hsla(206, 50%, 10%, .15));
}

View File

@ -13,7 +13,12 @@
}
:root {
/* How high should toolbars be */
--toolbar-height: 20px;
/* How wide should the sidebar be */
--timeline-sidebar-width: 150px;
/* How high should animations displayed in the timeline be */
--timeline-animation-height: 20px;
}
html {
@ -38,7 +43,7 @@ body {
flex-direction: row;
align-items: center;
justify-content: flex-end;
height: 20px;
height: var(--toolbar-height);
}
#toolbar .label {
@ -47,13 +52,13 @@ body {
#toggle-all {
border-width: 0 0 0 1px;
min-height: 20px;
min-height: var(--toolbar-height);
}
/* The main animations container */
#players {
height: calc(100% - 20px);
height: calc(100% - var(--toolbar-height));
overflow: auto;
}
@ -159,7 +164,7 @@ body {
.animation-timeline .time-header {
margin-left: var(--timeline-sidebar-width);
height: 20px;
min-height: var(--toolbar-height);
overflow: hidden;
position: relative;
/* This is the same color as the time graduations in
@ -187,7 +192,7 @@ body {
.animation-timeline .animation {
margin: 4px 0;
height: 20px;
height: var(--timeline-animation-height);
position: relative;
}
@ -289,14 +294,25 @@ body {
top: 0;
/* Make sure the delay covers up the animation border */
transform: translate(-1px, -1px);
height: 100%;
box-sizing: border-box;
height: calc(100% + 2px);
border: 1px solid var(--timelime-border-color);
border-width: 1px 0 1px 1px;
background-image: repeating-linear-gradient(45deg,
transparent,
transparent 1px,
var(--theme-selection-color) 1px,
var(--theme-selection-color) 4px);
background-color: var(--timelime-border-color);
border: 1px solid var(--timelime-border-color);
}
.animation-timeline .animation .delay.negative {
/* Negative delays are displayed on top of the animation, so they need a
right border. Whereas normal delays are displayed just before the
animation, so there's already the animation's left border that serves as
a separation. */
border-width: 1px;
}
/* Animation target node gutter, contains a preview of the dom node */
@ -361,7 +377,7 @@ body {
/* Timeline wiget */
.timeline {
height: 20px;
height: var(--timeline-animation-height);
width: 100%;
display: flex;
flex-direction: row;

View File

@ -99,7 +99,7 @@
<!-- In a 56x60dp space, centering 24dp image will leave 16x18dp. -->
<org.mozilla.gecko.toolbar.TabCounter android:id="@+id/tabs_counter"
style="@style/UrlBar.ImageButton.TabCount"
style="@style/UrlBar.ImageButton"
android:layout_alignLeft="@id/tabs"
android:layout_alignRight="@id/tabs"
android:layout_alignTop="@id/tabs"
@ -107,7 +107,8 @@
android:layout_marginTop="18dp"
android:layout_marginBottom="18dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"/>
android:layout_marginRight="16dp"
android:background="@drawable/tabs_count"/>
<org.mozilla.gecko.widget.themed.ThemedFrameLayout
android:id="@+id/menu"
@ -133,7 +134,10 @@
which is thus drawn on top, may be pressed. -->
<org.mozilla.gecko.widget.themed.ThemedImageView
android:id="@+id/edit_cancel"
style="@style/UrlBar.ImageButton.Icon"
style="@style/UrlBar.ImageButton"
android:layout_width="@dimen/browser_toolbar_icon_width"
android:layout_height="@dimen/browser_toolbar_height"
android:layout_weight="0.0"
android:layout_alignParentRight="true"
android:src="@drawable/close_edit_mode_selector"
android:contentDescription="@string/edit_mode_cancel"

View File

@ -70,12 +70,16 @@
The margins will be 40dp on left, 8dp on right, instead of ideal 30dp
and 12dp. -->
<org.mozilla.gecko.toolbar.TabCounter android:id="@+id/tabs_counter"
style="@style/UrlBar.ImageButton.TabCount"
style="@style/UrlBar.ImageButton"
android:layout_width="24dip"
android:layout_height="24dip"
android:layout_centerVertical="true"
android:layout_marginRight="8dip"
android:layout_alignRight="@id/tabs"/>
android:layout_alignRight="@id/tabs"
android:background="@drawable/tabs_count"
android:gravity="center_horizontal"
android:clipChildren="false"
android:clipToPadding="false"/>
<!-- Note that the edit components are invisible so that the views
depending on their location can properly layout. -->

View File

@ -15,10 +15,6 @@
<item name="drawableTintList">@color/action_bar_menu_item_colors</item>
</style>
<style name="UrlBar.ImageButton.TabCount">
<item name="android:background">@drawable/tabs_count</item>
</style>
<style name="UrlBar.Button.Container">
<item name="android:layout_marginTop">6dp</item>
<item name="android:layout_marginBottom">6dp</item>

View File

@ -457,13 +457,6 @@
<item name="android:orientation">horizontal</item>
</style>
<style name="UrlBar.ImageButton.TabCount">
<item name="android:background">@drawable/tabs_count</item>
<item name="android:gravity">center_horizontal</item>
<item name="android:clipChildren">false</item>
<item name="android:clipToPadding">false</item>
</style>
<!-- URL bar -->
<style name="UrlBar">
<item name="android:layout_width">match_parent</item>
@ -503,13 +496,6 @@
<item name="android:layout_width">@dimen/browser_toolbar_height</item>
</style>
<!-- URL bar - Image Button - Icon -->
<style name="UrlBar.ImageButton.Icon">
<item name="android:layout_width">@dimen/browser_toolbar_icon_width</item>
<item name="android:layout_height">@dimen/browser_toolbar_height</item>
<item name="android:layout_weight">0.0</item>
</style>
<!-- TabsLayout -->
<style name="TabsLayoutBase">
<item name="android:background">@android:color/transparent</item>

View File

@ -162,7 +162,7 @@ public class PageActionLayout extends LinearLayout implements NativeEventListene
ThreadUtils.assertOnUiThread();
final int width = mContext.getResources().getDimensionPixelSize(R.dimen.page_action_button_width);
ImageButton imageButton = new ImageButton(mContext, null, R.style.UrlBar_ImageButton_Icon);
ImageButton imageButton = new ImageButton(mContext, null, R.style.UrlBar_ImageButton);
imageButton.setLayoutParams(new LayoutParams(width, LayoutParams.MATCH_PARENT));
imageButton.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageButton.setOnClickListener(this);

View File

@ -1077,10 +1077,14 @@ EnvironmentCache.prototype = {
_getCpuData: function () {
let cpuData = {
count: getSysinfoProperty("cpucount", null),
vendor: null, // TODO: bug 1128472
family: null, // TODO: bug 1128472
model: null, // TODO: bug 1128472
stepping: null, // TODO: bug 1128472
cores: getSysinfoProperty("cpucores", null),
vendor: getSysinfoProperty("cpuvendor", null),
family: getSysinfoProperty("cpufamily", null),
model: getSysinfoProperty("cpumodel", null),
stepping: getSysinfoProperty("cpustepping", null),
l2cacheKB: getSysinfoProperty("cpucachel2", null),
l3cacheKB: getSysinfoProperty("cpucachel3", null),
speedMHz: getSysinfoProperty("cpuspeed", null),
};
const CPU_EXTENSIONS = ["hasMMX", "hasSSE", "hasSSE2", "hasSSE3", "hasSSSE3",
@ -1223,8 +1227,16 @@ EnvironmentCache.prototype = {
memoryMB = Math.round(memoryMB / 1024 / 1024);
}
let virtualMB = getSysinfoProperty("virtualmemsize", null);
if (virtualMB) {
// Send the total virtual memory size in megabytes. Rounding because
// sysinfo doesn't always provide RAM in multiples of 1024.
virtualMB = Math.round(virtualMB / 1024 / 1024);
}
return {
memoryMB: memoryMB,
virtualMaxMB: virtualMB,
#ifdef XP_WIN
isWow64: getSysinfoProperty("isWow64", null),
#endif

View File

@ -75,13 +75,18 @@ Structure::
},
system: {
memoryMB: <number>,
virtualMaxMB: <number>, // windows-only
isWow64: <bool>, // windows-only
cpu: {
count: <number>, // e.g. 8, or null on failure
vendor: <string>, // e.g. "GenuineIntel", or null on failure
family: <string>, // null on failure
model: <string>, // null on failure
stepping: <string>, // null on failure
count: <number>, // desktop only, e.g. 8, or null on failure - logical cpus
cores: <number>, // desktop only, e.g., 4, or null on failure - physical cores
vendor: <string>, // desktop only, e.g. "GenuineIntel", or null on failure
family: <string>, // desktop only, null on failure
model: <string>, // desktop only, null on failure
stepping: <string>, // desktop only, null on failure
l2cacheKB: <number>, // L2 cache size in KB, only on windows & mac
l3cacheKB: <number>, // desktop only, L3 cache size in KB
speedMHz: <number>, // desktop only, cpu clock speed in MHz
extensions: [
<string>,
...

View File

@ -8,11 +8,13 @@ Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://gre/modules/PromiseUtils.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
Cu.import("resource://testing-common/httpd.js", this);
Cu.import("resource://gre/modules/AppConstants.jsm");
const gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
const gIsMac = ("@mozilla.org/xpcom/mac-utils;1" in Cc);
const gIsAndroid = ("@mozilla.org/android/bridge;1" in Cc);
const gIsGonk = ("@mozilla.org/cellbroadcast/gonkservice;1" in Cc);
const gIsWindows = AppConstants.platform == "win";
const gIsMac = AppConstants.platform == "macosx";
const gIsAndroid = AppConstants.platform == "android";
const gIsGonk = AppConstants.platform == "gonk";
const gIsLinux = AppConstants.platform == "linux";
const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);

View File

@ -365,9 +365,40 @@ function checkSystemSection(data) {
}
Assert.ok(Number.isFinite(data.system.memoryMB), "MemoryMB must be a number.");
if (gIsWindows) {
Assert.equal(typeof data.system.isWow64, "boolean",
"isWow64 must be available on Windows and have the correct type.");
if (gIsWindows || gIsMac || gIsLinux) {
let EXTRA_CPU_FIELDS = ["cores", "model", "family", "stepping",
"l2cacheKB", "l3cacheKB", "speedMHz", "vendor"];
for (let f of EXTRA_CPU_FIELDS) {
// Note this is testing TelemetryEnvironment.js only, not that the
// values are valid - null is the fallback.
Assert.ok(f in data.system.cpu, f + " must be available under cpu.");
}
if (gIsWindows) {
Assert.equal(typeof data.system.isWow64, "boolean",
"isWow64 must be available on Windows and have the correct type.");
Assert.ok("virtualMaxMB" in data.system, "virtualMaxMB must be available.");
Assert.ok(Number.isFinite(data.system.virtualMaxMB),
"virtualMaxMB must be a number.");
}
// We insist these are available
for (let f of ["cores"]) {
Assert.ok(!(f in data.system.cpu) ||
Number.isFinite(data.system.cpu[f]),
f + " must be a number if non null.");
}
// These should be numbers if they are not null
for (let f of ["model", "family", "stepping", "l2cacheKB",
"l3cacheKB", "speedMHz"]) {
Assert.ok(!(f in data.system.cpu) ||
data.system.cpu[f] === null ||
Number.isFinite(data.system.cpu[f]),
f + " must be a number if non null.");
}
}
let cpuData = data.system.cpu;

View File

@ -41,6 +41,7 @@ function* playerHasAnInitialState(walker, front) {
ok("iterationCount" in player.initialState, "Player's state has iterationCount");
ok("isRunningOnCompositor" in player.initialState, "Player's state has isRunningOnCompositor");
ok("type" in player.initialState, "Player's state has type");
ok("documentCurrentTime" in player.initialState, "Player's state has documentCurrentTime");
}
function* playerStateIsCorrect(walker, front) {

View File

@ -2998,6 +2998,8 @@ this.AddonManager = {
SIGNEDSTATE_PRELIMINARY: 1,
// Add-on is fully reviewed.
SIGNEDSTATE_SIGNED: 2,
// Add-on is system add-on.
SIGNEDSTATE_SYSTEM: 3,
// Constants for the Addon.userDisabled property
// Indicates that the userDisabled state of this add-on is currently

View File

@ -617,6 +617,12 @@ function isUsableAddon(aAddon) {
return false;
if (aAddon.foreignInstall && aAddon.signedState < AddonManager.SIGNEDSTATE_SIGNED)
return false;
if (aAddon._installLocation.name == KEY_APP_SYSTEM_ADDONS ||
aAddon._installLocation.name == KEY_APP_SYSTEM_DEFAULTS) {
if (aAddon.signedState != AddonManager.SIGNEDSTATE_SYSTEM)
return false;
}
}
if (aAddon.blocklistState == Blocklist.STATE_BLOCKED)
@ -1155,7 +1161,7 @@ function defineSyncGUID(aAddon) {
* @return an AddonInternal object
* @throws if the directory does not contain a valid install manifest
*/
let loadManifestFromDir = Task.async(function* loadManifestFromDir(aDir) {
let loadManifestFromDir = Task.async(function* loadManifestFromDir(aDir, aInstallLocation) {
function getFileSize(aFile) {
if (aFile.isSymlink())
return 0;
@ -1202,6 +1208,7 @@ let loadManifestFromDir = Task.async(function* loadManifestFromDir(aDir) {
loadFromRDF(file, bis);
addon._sourceBundle = aDir.clone();
addon._installLocation = aInstallLocation;
addon.size = getFileSize(aDir);
addon.signedState = yield verifyDirSignedState(aDir, addon);
addon.appDisabled = !isUsableAddon(addon);
@ -1224,7 +1231,7 @@ let loadManifestFromDir = Task.async(function* loadManifestFromDir(aDir) {
* @return an AddonInternal object
* @throws if the XPI file does not contain a valid install manifest
*/
let loadManifestFromZipReader = Task.async(function* loadManifestFromZipReader(aZipReader) {
let loadManifestFromZipReader = Task.async(function* loadManifestFromZipReader(aZipReader, aInstallLocation) {
function loadFromRDF(aStream) {
let uri = buildJarURI(aZipReader.file, FILE_RDF_MANIFEST);
let addon = loadManifestFromRDF(uri, aStream);
@ -1259,6 +1266,7 @@ let loadManifestFromZipReader = Task.async(function* loadManifestFromZipReader(a
loadFromRDF(bis);
addon._sourceBundle = aZipReader.file;
addon._installLocation = aInstallLocation;
addon.size = 0;
let entries = aZipReader.findEntries(null);
@ -1286,7 +1294,7 @@ let loadManifestFromZipReader = Task.async(function* loadManifestFromZipReader(a
* @return an AddonInternal object
* @throws if the XPI file does not contain a valid install manifest
*/
let loadManifestFromZipFile = Task.async(function* loadManifestFromZipFile(aXPIFile) {
let loadManifestFromZipFile = Task.async(function* loadManifestFromZipFile(aXPIFile, aInstallLocation) {
let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
createInstance(Ci.nsIZipReader);
try {
@ -1295,7 +1303,7 @@ let loadManifestFromZipFile = Task.async(function* loadManifestFromZipFile(aXPIF
// Can't return this promise because that will make us close the zip reader
// before it has finished loading the manifest. Wait for the result and then
// return.
let manifest = yield loadManifestFromZipReader(zipReader);
let manifest = yield loadManifestFromZipReader(zipReader, aInstallLocation);
return manifest;
}
finally {
@ -1303,22 +1311,22 @@ let loadManifestFromZipFile = Task.async(function* loadManifestFromZipFile(aXPIF
}
});
function loadManifestFromFile(aFile) {
function loadManifestFromFile(aFile, aInstallLocation) {
if (aFile.isFile())
return loadManifestFromZipFile(aFile);
return loadManifestFromZipFile(aFile, aInstallLocation);
else
return loadManifestFromDir(aFile);
return loadManifestFromDir(aFile, aInstallLocation);
}
/**
* A synchronous method for loading an add-on's manifest. This should only ever
* be used during startup or a sync load of the add-ons DB
*/
function syncLoadManifestFromFile(aFile) {
function syncLoadManifestFromFile(aFile, aInstallLocation) {
let success = undefined;
let result = null;
loadManifestFromFile(aFile).then(val => {
loadManifestFromFile(aFile, aInstallLocation).then(val => {
success = true;
result = val;
}, val => {
@ -1466,6 +1474,9 @@ function getSignedStatus(aRv, aCert, aExpectedID) {
}
}
if (aCert.organizationalUnit == "Mozilla Components")
return AddonManager.SIGNEDSTATE_SYSTEM;
return /preliminary/i.test(aCert.organizationalUnit)
? AddonManager.SIGNEDSTATE_PRELIMINARY
: AddonManager.SIGNEDSTATE_SIGNED;
@ -2922,7 +2933,7 @@ this.XPIProvider = {
let addon;
try {
addon = syncLoadManifestFromFile(stageDirEntry);
addon = syncLoadManifestFromFile(stageDirEntry, aLocation);
}
catch (e) {
logger.error("Unable to read add-on manifest from " + stageDirEntry.path, e);
@ -3096,7 +3107,7 @@ this.XPIProvider = {
let addon;
try {
addon = syncLoadManifestFromFile(entry);
addon = syncLoadManifestFromFile(entry, profileLocation);
}
catch (e) {
logger.warn("File entry " + entry.path + " contains an invalid add-on", e);
@ -3119,7 +3130,7 @@ this.XPIProvider = {
if (existingEntry) {
let existingAddon;
try {
existingAddon = syncLoadManifestFromFile(existingEntry);
existingAddon = syncLoadManifestFromFile(existingEntry, profileLocation);
if (Services.vc.compare(addon.version, existingAddon.version) <= 0)
continue;
@ -4958,7 +4969,7 @@ AddonInstall.prototype = {
try {
// loadManifestFromZipReader performs the certificate verification for us
this.addon = yield loadManifestFromZipReader(zipreader);
this.addon = yield loadManifestFromZipReader(zipreader, this.installLocation);
}
catch (e) {
zipreader.close();
@ -5506,7 +5517,6 @@ AddonInstall.prototype = {
// Update the metadata in the database
this.addon._sourceBundle = file;
this.addon._installLocation = this.installLocation;
this.addon.visible = true;
if (isUpgrade) {

View File

@ -1604,7 +1604,7 @@ this.XPIDatabaseReconcile = {
// Load the manifest from the add-on.
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.persistentDescriptor = aAddonState.descriptor;
aNewAddon = syncLoadManifestFromFile(file);
aNewAddon = syncLoadManifestFromFile(file, aInstallLocation);
}
// The add-on in the manifest should match the add-on ID.
if (aNewAddon.id != aId) {
@ -1625,7 +1625,6 @@ this.XPIDatabaseReconcile = {
}
// Update the AddonInternal properties.
aNewAddon._installLocation = aInstallLocation;
aNewAddon.installDate = aAddonState.mtime;
aNewAddon.updateDate = aAddonState.mtime;
@ -1728,7 +1727,7 @@ this.XPIDatabaseReconcile = {
if (!aNewAddon) {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.persistentDescriptor = aAddonState.descriptor;
aNewAddon = syncLoadManifestFromFile(file);
aNewAddon = syncLoadManifestFromFile(file, aInstallLocation);
applyBlocklistChanges(aOldAddon, aNewAddon);
// Carry over any pendingUninstall state to add-ons modified directly
@ -1756,7 +1755,6 @@ this.XPIDatabaseReconcile = {
}
// Set the additional properties on the new AddonInternal
aNewAddon._installLocation = aInstallLocation;
aNewAddon.updateDate = aAddonState.mtime;
// Update the database
@ -1814,7 +1812,7 @@ this.XPIDatabaseReconcile = {
SIGNED_TYPES.has(aOldAddon.type)) {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.persistentDescriptor = aAddonState.descriptor;
let manifest = syncLoadManifestFromFile(file);
let manifest = syncLoadManifestFromFile(file, aInstallLocation);
aOldAddon.signedState = manifest.signedState;
}
// This updates the addon's JSON cached data in place

View File

@ -2,6 +2,9 @@
// application versions
const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet";
// Enable signature checks for these tests
Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
const featureDir = gProfD.clone();
featureDir.append("features");
@ -47,13 +50,21 @@ function* check_installed(inProfile, ...versions) {
do_check_true(uri instanceof AM_Ci.nsIFileURL);
do_check_eq(uri.file.path, file.path);
do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_SYSTEM);
// Verify the add-on actually started
let installed = Services.prefs.getCharPref("bootstraptest." + id + ".active_version");
do_check_eq(installed, versions[i]);
}
else {
// Add-on should not be installed
do_check_eq(addon, null);
if (inProfile) {
// Add-on should not be installed
do_check_eq(addon, null);
}
else {
// Either add-on should not be installed or it shouldn't be active
do_check_true(!addon || !addon.isActive);
}
try {
Services.prefs.getCharPref("bootstraptest." + id + ".active_version");
@ -131,7 +142,7 @@ add_task(function* test_updated() {
// Inject it into the system set
let addonSet = {
schema: 1,
directory: dirname,
directory: featureDir.leafName,
addons: {
"system2@tests.mozilla.org": {
version: "1.0"
@ -198,3 +209,50 @@ add_task(function* test_corrupt_pref() {
yield promiseShutdownManager();
});
// An add-on with a bad certificate should cause us to use the default set
add_task(function* test_bad_profile_cert() {
let file = do_get_file("data/system_addons/app3/features/system1@tests.mozilla.org.xpi");
file.copyTo(featureDir, file.leafName);
// Inject it into the system set
let addonSet = {
schema: 1,
directory: featureDir.leafName,
addons: {
"system1@tests.mozilla.org": {
version: "2.0"
},
"system2@tests.mozilla.org": {
version: "1.0"
},
"system3@tests.mozilla.org": {
version: "1.0"
},
}
};
Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify(addonSet));
startupManager(false);
yield check_installed(false, "1.0", "1.0", null);
yield promiseShutdownManager();
});
// Switching to app defaults that contain a bad certificate should ignore the
// bad add-on
add_task(function* test_bad_app_cert() {
gAppInfo.version = "3";
distroDir.leafName = "app3";
startupManager();
// Add-on will still be present just not active
let addon = yield promiseAddonByID("system1@tests.mozilla.org");
do_check_neq(addon, null);
do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_SIGNED);
yield check_installed(false, null, null, "1.0");
yield promiseShutdownManager();
});

View File

@ -31,6 +31,13 @@
#ifdef MOZ_WIDGET_GTK
#include <gtk/gtk.h>
#include <unistd.h>
#include <fstream>
#include "mozilla/Tokenizer.h"
#include "nsCharSeparatedTokenizer.h"
#include <map>
#include <string>
#endif
#ifdef MOZ_WIDGET_ANDROID
@ -49,6 +56,10 @@ NS_EXPORT int android_sdk_version;
}
#endif
#ifdef XP_MACOSX
#include <sys/sysctl.h>
#endif
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
#include "mozilla/SandboxInfo.h"
#endif
@ -60,6 +71,30 @@ NS_EXPORT int android_sdk_version;
// only happens well after that point.
uint32_t nsSystemInfo::gUserUmask = 0;
#if defined (MOZ_WIDGET_GTK)
static void
SimpleParseKeyValuePairs(const std::string& aFilename,
std::map<nsCString, nsCString>& aKeyValuePairs)
{
std::ifstream input(aFilename.c_str());
for (std::string line; std::getline(input, line); ) {
nsAutoCString key, value;
nsCCharSeparatedTokenizer tokens(nsDependentCString(line.c_str()), ':');
if (tokens.hasMoreTokens()) {
key = tokens.nextToken();
if (tokens.hasMoreTokens()) {
value = tokens.nextToken();
}
// We want the value even if there was just one token, to cover the
// case where we had the key, and the value was blank (seems to be
// a valid scenario some files.)
aKeyValuePairs[key] = value;
}
}
}
#endif
#if defined(XP_WIN)
namespace {
nsresult
@ -235,6 +270,66 @@ static const struct PropItems
{ "hasNEON", mozilla::supports_neon }
};
#ifdef XP_WIN
// Lifted from media/webrtc/trunk/webrtc/base/systeminfo.cc,
// so keeping the _ instead of switching to camel case for now.
typedef BOOL (WINAPI *LPFN_GLPI)(
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,
PDWORD);
static void
GetProcessorInformation(int* physical_cpus, int* cache_size_L2, int* cache_size_L3)
{
MOZ_ASSERT(physical_cpus && cache_size_L2 && cache_size_L3);
*physical_cpus = 0;
*cache_size_L2 = 0; // This will be in kbytes
*cache_size_L3 = 0; // This will be in kbytes
// GetLogicalProcessorInformation() is available on Windows XP SP3 and beyond.
LPFN_GLPI glpi = reinterpret_cast<LPFN_GLPI>(GetProcAddress(
GetModuleHandle(L"kernel32"),
"GetLogicalProcessorInformation"));
if (nullptr == glpi) {
return;
}
// Determine buffer size, allocate and get processor information.
// Size can change between calls (unlikely), so a loop is done.
SYSTEM_LOGICAL_PROCESSOR_INFORMATION info_buffer[32];
SYSTEM_LOGICAL_PROCESSOR_INFORMATION* infos = &info_buffer[0];
DWORD return_length = sizeof(info_buffer);
while (!glpi(infos, &return_length)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && infos == &info_buffer[0]) {
infos = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)];
} else {
return;
}
}
for (size_t i = 0;
i < return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i) {
if (infos[i].Relationship == RelationProcessorCore) {
++*physical_cpus;
} else if (infos[i].Relationship == RelationCache) {
// Only care about L2 and L3 cache
switch (infos[i].Cache.Level) {
case 2:
*cache_size_L2 = static_cast<int>(infos[i].Cache.Size/1024);
break;
case 3:
*cache_size_L3 = static_cast<int>(infos[i].Cache.Size/1024);
break;
default:
break;
}
}
}
if (infos != &info_buffer[0]) {
delete [] infos;
}
return;
}
#endif
nsresult
nsSystemInfo::Init()
{
@ -272,10 +367,246 @@ nsSystemInfo::Init()
SetInt32Property(NS_LITERAL_STRING("pagesize"), PR_GetPageSize());
SetInt32Property(NS_LITERAL_STRING("pageshift"), PR_GetPageShift());
SetInt32Property(NS_LITERAL_STRING("memmapalign"), PR_GetMemMapAlignment());
SetInt32Property(NS_LITERAL_STRING("cpucount"), PR_GetNumberOfProcessors());
SetUint64Property(NS_LITERAL_STRING("memsize"), PR_GetPhysicalMemorySize());
SetUint32Property(NS_LITERAL_STRING("umask"), nsSystemInfo::gUserUmask);
uint64_t virtualMem = 0;
nsAutoCString cpuVendor;
int cpuSpeed = -1;
int cpuFamily = -1;
int cpuModel = -1;
int cpuStepping = -1;
int logicalCPUs = -1;
int physicalCPUs = -1;
int cacheSizeL2 = -1;
int cacheSizeL3 = -1;
#if defined (XP_WIN)
// Virtual memory:
MEMORYSTATUSEX memStat;
memStat.dwLength = sizeof(memStat);
if (GlobalMemoryStatusEx(&memStat)) {
virtualMem = memStat.ullTotalVirtual;
}
// CPU speed
HKEY key;
static const WCHAR keyName[] =
L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName , 0, KEY_QUERY_VALUE, &key)
== ERROR_SUCCESS) {
DWORD data, len, vtype;
len = sizeof(data);
if (RegQueryValueEx(key, L"~Mhz", 0, 0, reinterpret_cast<LPBYTE>(&data),
&len) == ERROR_SUCCESS) {
cpuSpeed = static_cast<int>(data);
}
// Limit to 64 double byte characters, should be plenty, but create
// a buffer one larger as the result may not be null terminated. If
// it is more than 64, we will not get the value.
wchar_t cpuVendorStr[64+1];
len = sizeof(cpuVendorStr)-2;
if (RegQueryValueExW(key, L"VendorIdentifier",
0, &vtype,
reinterpret_cast<LPBYTE>(cpuVendorStr),
&len) == ERROR_SUCCESS &&
vtype == REG_SZ && len % 2 == 0 && len > 1) {
cpuVendorStr[len/2] = 0; // In case it isn't null terminated
CopyUTF16toUTF8(nsDependentString(cpuVendorStr), cpuVendor);
}
RegCloseKey(key);
}
// Other CPU attributes:
SYSTEM_INFO si;
GetNativeSystemInfo(&si);
logicalCPUs = si.dwNumberOfProcessors;
GetProcessorInformation(&physicalCPUs, &cacheSizeL2, &cacheSizeL3);
if (physicalCPUs <= 0) {
physicalCPUs = logicalCPUs;
}
cpuFamily = si.wProcessorLevel;
cpuModel = si.wProcessorRevision >> 8;
cpuStepping = si.wProcessorRevision & 0xFF;
#elif defined (XP_MACOSX)
// CPU speed
uint64_t sysctlValue64 = 0;
uint32_t sysctlValue32 = 0;
size_t len = 0;
len = sizeof(sysctlValue64);
if (!sysctlbyname("hw.cpufrequency_max", &sysctlValue64, &len, NULL, 0)) {
cpuSpeed = static_cast<int>(sysctlValue64/1000000);
}
MOZ_ASSERT(sizeof(sysctlValue64) == len);
len = sizeof(sysctlValue32);
if (!sysctlbyname("hw.physicalcpu_max", &sysctlValue32, &len, NULL, 0)) {
physicalCPUs = static_cast<int>(sysctlValue32);
}
MOZ_ASSERT(sizeof(sysctlValue32) == len);
len = sizeof(sysctlValue32);
if (!sysctlbyname("hw.logicalcpu_max", &sysctlValue32, &len, NULL, 0)) {
logicalCPUs = static_cast<int>(sysctlValue32);
}
MOZ_ASSERT(sizeof(sysctlValue32) == len);
len = sizeof(sysctlValue64);
if (!sysctlbyname("hw.l2cachesize", &sysctlValue64, &len, NULL, 0)) {
cacheSizeL2 = static_cast<int>(sysctlValue64/1024);
}
MOZ_ASSERT(sizeof(sysctlValue64) == len);
len = sizeof(sysctlValue64);
if (!sysctlbyname("hw.l3cachesize", &sysctlValue64, &len, NULL, 0)) {
cacheSizeL3 = static_cast<int>(sysctlValue64/1024);
}
MOZ_ASSERT(sizeof(sysctlValue64) == len);
if (!sysctlbyname("machdep.cpu.vendor", NULL, &len, NULL, 0)) {
char* cpuVendorStr = new char[len];
if (!sysctlbyname("machdep.cpu.vendor", cpuVendorStr, &len, NULL, 0)) {
cpuVendor = cpuVendorStr;
}
delete [] cpuVendorStr;
}
len = sizeof(sysctlValue32);
if (!sysctlbyname("machdep.cpu.family", &sysctlValue32, &len, NULL, 0)) {
cpuFamily = static_cast<int>(sysctlValue32);
}
MOZ_ASSERT(sizeof(sysctlValue32) == len);
len = sizeof(sysctlValue32);
if (!sysctlbyname("machdep.cpu.model", &sysctlValue32, &len, NULL, 0)) {
cpuModel = static_cast<int>(sysctlValue32);
}
MOZ_ASSERT(sizeof(sysctlValue32) == len);
len = sizeof(sysctlValue32);
if (!sysctlbyname("machdep.cpu.stepping", &sysctlValue32, &len, NULL, 0)) {
cpuStepping = static_cast<int>(sysctlValue32);
}
MOZ_ASSERT(sizeof(sysctlValue32) == len);
#elif defined (MOZ_WIDGET_GTK)
// Get vendor, family, model, stepping, physical cores, L3 cache size
// from /proc/cpuinfo file
{
std::map<nsCString, nsCString> keyValuePairs;
SimpleParseKeyValuePairs("/proc/cpuinfo", keyValuePairs);
// cpuVendor from "vendor_id"
cpuVendor.Assign(keyValuePairs[NS_LITERAL_CSTRING("vendor_id")]);
{
// cpuFamily from "cpu family"
Tokenizer::Token t;
Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("cpu family")]);
if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
t.AsInteger() <= INT32_MAX) {
cpuFamily = static_cast<int>(t.AsInteger());
}
}
{
// cpuModel from "model"
Tokenizer::Token t;
Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("model")]);
if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
t.AsInteger() <= INT32_MAX) {
cpuModel = static_cast<int>(t.AsInteger());
}
}
{
// cpuStepping from "stepping"
Tokenizer::Token t;
Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("stepping")]);
if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
t.AsInteger() <= INT32_MAX) {
cpuStepping = static_cast<int>(t.AsInteger());
}
}
{
// physicalCPUs from "cpu cores"
Tokenizer::Token t;
Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("cpu cores")]);
if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
t.AsInteger() <= INT32_MAX) {
physicalCPUs = static_cast<int>(t.AsInteger());
}
}
{
// cacheSizeL3 from "cache size"
Tokenizer::Token t;
Tokenizer p(keyValuePairs[NS_LITERAL_CSTRING("cache size")]);
if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
t.AsInteger() <= INT32_MAX) {
cacheSizeL3 = static_cast<int>(t.AsInteger());
if (p.Next(t) && t.Type() == Tokenizer::TOKEN_WORD &&
t.AsString() != NS_LITERAL_CSTRING("KB")) {
// If we get here, there was some text after the cache size value
// and that text was not KB. For now, just don't report the
// L3 cache.
cacheSizeL3 = -1;
}
}
}
}
{
// Get cpuSpeed from another file.
std::ifstream input("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
std::string line;
if (getline(input, line)) {
Tokenizer::Token t;
Tokenizer p(line.c_str());
if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
t.AsInteger() <= INT32_MAX) {
cpuSpeed = static_cast<int>(t.AsInteger()/1000);
}
}
}
{
// Get cacheSizeL2 from yet another file
std::ifstream input("/sys/devices/system/cpu/cpu0/cache/index2/size");
std::string line;
if (getline(input, line)) {
Tokenizer::Token t;
Tokenizer p(line.c_str(), nullptr, "K");
if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
t.AsInteger() <= INT32_MAX) {
cacheSizeL2 = static_cast<int>(t.AsInteger());
}
}
}
SetInt32Property(NS_LITERAL_STRING("cpucount"), PR_GetNumberOfProcessors());
#else
SetInt32Property(NS_LITERAL_STRING("cpucount"), PR_GetNumberOfProcessors());
#endif
if (virtualMem) SetUint64Property(NS_LITERAL_STRING("virtualmemsize"), virtualMem);
if (cpuSpeed >= 0) SetInt32Property(NS_LITERAL_STRING("cpuspeed"), cpuSpeed);
if (!cpuVendor.IsEmpty()) SetPropertyAsACString(NS_LITERAL_STRING("cpuvendor"), cpuVendor);
if (cpuFamily >= 0) SetInt32Property(NS_LITERAL_STRING("cpufamily"), cpuFamily);
if (cpuModel >= 0) SetInt32Property(NS_LITERAL_STRING("cpumodel"), cpuModel);
if (cpuStepping >= 0) SetInt32Property(NS_LITERAL_STRING("cpustepping"), cpuStepping);
if (logicalCPUs >= 0) SetInt32Property(NS_LITERAL_STRING("cpucount"), logicalCPUs);
if (physicalCPUs >= 0) SetInt32Property(NS_LITERAL_STRING("cpucores"), physicalCPUs);
if (cacheSizeL2 >= 0) SetInt32Property(NS_LITERAL_STRING("cpucachel2"), cacheSizeL2);
if (cacheSizeL3 >= 0) SetInt32Property(NS_LITERAL_STRING("cpucachel3"), cacheSizeL3);
for (uint32_t i = 0; i < ArrayLength(cpuPropItems); i++) {
rv = SetPropertyAsBool(NS_ConvertASCIItoUTF16(cpuPropItems[i].name),
cpuPropItems[i].propfun());