mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-13 05:15:45 +00:00
Merge fx-team to mozilla-central a=merge
This commit is contained in:
commit
0474fa769c
@ -862,7 +862,7 @@ pref("browser.preferences.animateFadeIn", false);
|
||||
#endif
|
||||
|
||||
// Toggles between the two Preferences implementations, pop-up window and in-content
|
||||
#ifndef RELEASE_BUILD
|
||||
#ifdef EARLY_BETA_OR_EARLIER
|
||||
pref("browser.preferences.inContent", true);
|
||||
pref("browser.preferences.instantApply", true);
|
||||
#else
|
||||
@ -1644,6 +1644,7 @@ pref("image.mem.max_decoded_image_kb", 256000);
|
||||
pref("loop.enabled", true);
|
||||
pref("loop.server", "https://loop.services.mozilla.com/v0");
|
||||
pref("loop.seenToS", "unseen");
|
||||
pref("loop.showPartnerLogo", true);
|
||||
pref("loop.gettingStarted.seen", false);
|
||||
pref("loop.gettingStarted.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/hello/start/");
|
||||
pref("loop.gettingStarted.resumeOnFirstJoin", false);
|
||||
|
@ -266,11 +266,6 @@ let gFxAccounts = {
|
||||
Weave.Notifications.removeAll(this.SYNC_MIGRATION_NOTIFICATION_TITLE);
|
||||
return;
|
||||
}
|
||||
if (gBrowser.currentURI.spec.split("?")[0] == "about:accounts") {
|
||||
// If the current tab is about:accounts, assume the user just completed a
|
||||
// migration step and don't bother them with a redundant notification.
|
||||
return;
|
||||
}
|
||||
let note = null;
|
||||
switch (this._migrationInfo.state) {
|
||||
case this.fxaMigrator.STATE_USER_FXA: {
|
||||
|
@ -201,13 +201,29 @@ loop.panel = (function(_, mozL10n) {
|
||||
|
||||
return {
|
||||
seenToS: getPref("seenToS"),
|
||||
gettingStartedSeen: getPref("gettingStarted.seen")
|
||||
gettingStartedSeen: getPref("gettingStarted.seen"),
|
||||
showPartnerLogo: getPref("showPartnerLogo")
|
||||
};
|
||||
},
|
||||
|
||||
renderPartnerLogo: function() {
|
||||
if (!this.state.showPartnerLogo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var locale = mozL10n.getLanguage();
|
||||
navigator.mozLoop.setLoopPref('showPartnerLogo', false);
|
||||
return (
|
||||
React.createElement("p", {id: "powered-by", className: "powered-by"},
|
||||
mozL10n.get("powered_by_beforeLogo"),
|
||||
React.createElement("img", {id: "powered-by-logo", className: locale}),
|
||||
mozL10n.get("powered_by_afterLogo")
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.state.gettingStartedSeen || this.state.seenToS == "unseen") {
|
||||
var locale = mozL10n.getLanguage();
|
||||
var terms_of_use_url = navigator.mozLoop.getLoopPref('legal.ToS_url');
|
||||
var privacy_notice_url = navigator.mozLoop.getLoopPref('legal.privacy_url');
|
||||
var tosHTML = mozL10n.get("legal_text_and_links3", {
|
||||
@ -224,11 +240,7 @@ loop.panel = (function(_, mozL10n) {
|
||||
),
|
||||
});
|
||||
return React.createElement("div", {id: "powered-by-wrapper"},
|
||||
React.createElement("p", {id: "powered-by"},
|
||||
mozL10n.get("powered_by_beforeLogo"),
|
||||
React.createElement("img", {id: "powered-by-logo", className: locale}),
|
||||
mozL10n.get("powered_by_afterLogo")
|
||||
),
|
||||
this.renderPartnerLogo(),
|
||||
React.createElement("p", {className: "terms-service",
|
||||
dangerouslySetInnerHTML: {__html: tosHTML}})
|
||||
);
|
||||
|
@ -201,13 +201,29 @@ loop.panel = (function(_, mozL10n) {
|
||||
|
||||
return {
|
||||
seenToS: getPref("seenToS"),
|
||||
gettingStartedSeen: getPref("gettingStarted.seen")
|
||||
gettingStartedSeen: getPref("gettingStarted.seen"),
|
||||
showPartnerLogo: getPref("showPartnerLogo")
|
||||
};
|
||||
},
|
||||
|
||||
renderPartnerLogo: function() {
|
||||
if (!this.state.showPartnerLogo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var locale = mozL10n.getLanguage();
|
||||
navigator.mozLoop.setLoopPref('showPartnerLogo', false);
|
||||
return (
|
||||
<p id="powered-by" className="powered-by">
|
||||
{mozL10n.get("powered_by_beforeLogo")}
|
||||
<img id="powered-by-logo" className={locale} />
|
||||
{mozL10n.get("powered_by_afterLogo")}
|
||||
</p>
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.state.gettingStartedSeen || this.state.seenToS == "unseen") {
|
||||
var locale = mozL10n.getLanguage();
|
||||
var terms_of_use_url = navigator.mozLoop.getLoopPref('legal.ToS_url');
|
||||
var privacy_notice_url = navigator.mozLoop.getLoopPref('legal.privacy_url');
|
||||
var tosHTML = mozL10n.get("legal_text_and_links3", {
|
||||
@ -224,11 +240,7 @@ loop.panel = (function(_, mozL10n) {
|
||||
),
|
||||
});
|
||||
return <div id="powered-by-wrapper">
|
||||
<p id="powered-by">
|
||||
{mozL10n.get("powered_by_beforeLogo")}
|
||||
<img id="powered-by-logo" className={locale} />
|
||||
{mozL10n.get("powered_by_afterLogo")}
|
||||
</p>
|
||||
{this.renderPartnerLogo()}
|
||||
<p className="terms-service"
|
||||
dangerouslySetInnerHTML={{__html: tosHTML}}></p>
|
||||
</div>;
|
||||
|
@ -203,6 +203,11 @@ loop.store.ActiveRoomStore = (function() {
|
||||
* @param {sharedActions.SetupRoomInfo} actionData
|
||||
*/
|
||||
setupRoomInfo: function(actionData) {
|
||||
if (this._onUpdateListener) {
|
||||
console.error("Room info already set up!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.setStoreState({
|
||||
roomName: actionData.roomName,
|
||||
roomOwner: actionData.roomOwner,
|
||||
@ -211,10 +216,11 @@ loop.store.ActiveRoomStore = (function() {
|
||||
roomUrl: actionData.roomUrl
|
||||
});
|
||||
|
||||
this._mozLoop.rooms.on("update:" + actionData.roomToken,
|
||||
this._handleRoomUpdate.bind(this));
|
||||
this._mozLoop.rooms.on("delete:" + actionData.roomToken,
|
||||
this._handleRoomDelete.bind(this));
|
||||
this._onUpdateListener = this._handleRoomUpdate.bind(this);
|
||||
this._onDeleteListener = this._handleRoomDelete.bind(this);
|
||||
|
||||
this._mozLoop.rooms.on("update:" + actionData.roomToken, this._onUpdateListener);
|
||||
this._mozLoop.rooms.on("delete:" + actionData.roomToken, this._onDeleteListener);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -390,10 +396,16 @@ loop.store.ActiveRoomStore = (function() {
|
||||
windowUnload: function() {
|
||||
this._leaveRoom(ROOM_STATES.CLOSING);
|
||||
|
||||
if (!this._onUpdateListener) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're closing the window, we can stop listening to updates.
|
||||
var roomToken = this.getStoreState().roomToken;
|
||||
this._mozLoop.rooms.off("update:" + roomToken);
|
||||
this._mozLoop.rooms.off("delete:" + roomToken);
|
||||
this._mozLoop.rooms.off("update:" + roomToken, this._onUpdateListener);
|
||||
this._mozLoop.rooms.off("delete:" + roomToken, this._onDeleteListener);
|
||||
delete this._onUpdateListener;
|
||||
delete this._onDeleteListener;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1052,5 +1052,21 @@ describe("loop.panel", function() {
|
||||
TestUtils.findRenderedDOMComponentWithClass(view, "terms-service");
|
||||
});
|
||||
|
||||
it("should render the telefonica logo after the first time use",
|
||||
function() {
|
||||
navigator.mozLoop.getLoopPref = function(key) {
|
||||
return {
|
||||
"gettingStarted.seen": false,
|
||||
"seenToS": "unseen",
|
||||
"showPartnerLogo": false
|
||||
}[key];
|
||||
};
|
||||
|
||||
var view = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.panel.ToSView));
|
||||
|
||||
expect(view.getDOMNode().querySelector(".powered-by")).eql(null);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -555,7 +555,8 @@ var gCookiesWindow = {
|
||||
|
||||
let buttonLabel = this._bundle.getString("removeSelectedCookies");
|
||||
let removeSelectedCookies = document.getElementById("removeSelectedCookies");
|
||||
removeSelectedCookies.label = PluralForm.get(selectedCookieCount, buttonLabel);
|
||||
removeSelectedCookies.label = PluralForm.get(selectedCookieCount, buttonLabel)
|
||||
.replace("#1", selectedCookieCount);
|
||||
|
||||
removeSelectedCookies.disabled = !(seln.count > 0);
|
||||
},
|
||||
|
@ -35,8 +35,9 @@ function prettyPrintSource() {
|
||||
}
|
||||
|
||||
function testPrettyPrinted({ error, source }) {
|
||||
ok(!error);
|
||||
ok(source.contains("\n "));
|
||||
ok(!error, "Should not get an error while pretty-printing");
|
||||
ok(source.contains("\n "),
|
||||
"Source should be pretty-printed");
|
||||
disablePrettyPrint();
|
||||
}
|
||||
|
||||
@ -45,8 +46,9 @@ function disablePrettyPrint() {
|
||||
}
|
||||
|
||||
function testUgly({ error, source }) {
|
||||
ok(!error);
|
||||
ok(!source.contains("\n "));
|
||||
ok(!error, "Should not get an error while disabling pretty-printing");
|
||||
ok(!source.contains("\n "),
|
||||
"Source should not be pretty after disabling pretty-printing");
|
||||
closeDebuggerAndFinish(gPanel);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
function stopMe(){throw Error("boom");}try{stopMe();var a=1;a=a*2;}catch(e){};
|
||||
|
||||
//# sourceMappingURL=bogus.map
|
||||
|
@ -15,28 +15,38 @@ function test()
|
||||
openScratchpad(runTests);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html;charset=utf8,test Scratchpad pretty print error goto line.";
|
||||
content.location = "data:text/html;charset=utf8,"
|
||||
+ "test Scratchpad pretty print error goto line.";
|
||||
}
|
||||
|
||||
function testJumpToPrettyPrintError(sp, error, remark) {
|
||||
info("will test jumpToLine after prettyPrint error" + remark);
|
||||
// CodeMirror lines and columns are 0-based, Scratchpad UI and error
|
||||
// stack are 1-based.
|
||||
is(/Invalid regexp flag \(3:10\)/.test(error), true, "prettyPrint expects error in editor text:\n" + error);
|
||||
const errorLine = 3, errorColumn = 10;
|
||||
const editorDoc = sp.editor.container.contentDocument;
|
||||
sp.editor.jumpToLine();
|
||||
const lineInput = editorDoc.querySelector("input");
|
||||
const errorLocation = lineInput.value;
|
||||
const [ inputLine, inputColumn ] = errorLocation.split(":");
|
||||
is(inputLine, errorLine, "jumpToLine input field is set from editor selection (line)");
|
||||
is(inputColumn, errorColumn, "jumpToLine input field is set from editor selection (column)");
|
||||
EventUtils.synthesizeKey("VK_RETURN", { }, editorDoc.defaultView);
|
||||
// CodeMirror lines and columns are 0-based, Scratchpad UI and error
|
||||
// stack are 1-based.
|
||||
const cursor = sp.editor.getCursor();
|
||||
is(inputLine, cursor.line + 1, "jumpToLine goto error location (line)");
|
||||
is(inputColumn, cursor.ch + 1, "jumpToLine goto error location (column)");
|
||||
|
||||
// CodeMirror lines and columns are 0-based, Scratchpad UI and error
|
||||
// stack are 1-based.
|
||||
is(/Invalid regular expression flag \(3:10\)/.test(error), true,
|
||||
"prettyPrint expects error in editor text:\n" + error);
|
||||
|
||||
sp.editor.jumpToLine();
|
||||
|
||||
const editorDoc = sp.editor.container.contentDocument;
|
||||
const lineInput = editorDoc.querySelector("input");
|
||||
const errorLocation = lineInput.value;
|
||||
const [ inputLine, inputColumn ] = errorLocation.split(":");
|
||||
const errorLine = 3, errorColumn = 10;
|
||||
|
||||
is(inputLine, errorLine,
|
||||
"jumpToLine input field is set from editor selection (line)");
|
||||
is(inputColumn, errorColumn,
|
||||
"jumpToLine input field is set from editor selection (column)");
|
||||
|
||||
EventUtils.synthesizeKey("VK_RETURN", { }, editorDoc.defaultView);
|
||||
|
||||
// CodeMirror lines and columns are 0-based, Scratchpad UI and error
|
||||
// stack are 1-based.
|
||||
const cursor = sp.editor.getCursor();
|
||||
is(inputLine, cursor.line + 1, "jumpToLine goto error location (line)");
|
||||
is(inputColumn, cursor.ch + 1, "jumpToLine goto error location (column)");
|
||||
}
|
||||
|
||||
function runTests(sw, sp)
|
||||
@ -49,12 +59,14 @@ function runTests(sw, sp)
|
||||
"// line 5",
|
||||
""
|
||||
].join("\n"));
|
||||
|
||||
sp.prettyPrint().then(aFulfill => {
|
||||
ok(false, "Expecting Invalid regexp flag (3:10)");
|
||||
finish();
|
||||
}, error => {
|
||||
testJumpToPrettyPrintError(sp, error, " (Bug 1005471, first time)");
|
||||
});
|
||||
|
||||
sp.prettyPrint().then(aFulfill => {
|
||||
ok(false, "Expecting Invalid regexp flag (3:10)");
|
||||
finish();
|
||||
|
@ -13,26 +13,30 @@ add_task(function*() {
|
||||
get3(front, "create-node")
|
||||
]);
|
||||
|
||||
let t0 = 0, t1 = 0.1, t2 = 0.2, t3 = 0.3, t4 = 0.4, t5 = 0.6, t6 = 0.7, t7 = 1;
|
||||
yield oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.2, t0]);
|
||||
yield oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.3, t1]);
|
||||
yield oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.4, t2]);
|
||||
yield oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [1, t3]);
|
||||
yield oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [0.15, t4]);
|
||||
yield oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [0.75, t5]);
|
||||
yield oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [0.05, t6]);
|
||||
yield oscNode.addAutomationEvent("frequency", "setValueAtTime", [300, 0.1]);
|
||||
yield oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [500, 0.4]);
|
||||
yield oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [200, 0.6]);
|
||||
// End with a setTargetAtTime event, as the target approaches infinity, which will
|
||||
// give us more points to render than the default 2000
|
||||
yield oscNode.addAutomationEvent("frequency", "setTargetAtTime", [1, t7, 0.5]);
|
||||
yield oscNode.addAutomationEvent("frequency", "setTargetAtTime", [1000, 2, 0.5]);
|
||||
|
||||
let { events, values } = yield oscNode.getAutomationData("frequency");
|
||||
var { events, values } = yield oscNode.getAutomationData("frequency");
|
||||
|
||||
is(events.length, 8, "8 recorded events returned.");
|
||||
is(values.length, 4000, "6000 value points returned when ending with exponentiall approaching automator.");
|
||||
is(events.length, 4, "4 recorded events returned.");
|
||||
is(values.length, 4000, "4000 value points returned when ending with exponentiall approaching automator.");
|
||||
|
||||
checkAutomationValue(values, 1, 0.05);
|
||||
checkAutomationValue(values, 2, 0.87);
|
||||
checkAutomationValue(values, 3, 0.98);
|
||||
checkAutomationValue(values, 2.01, 215.055)
|
||||
checkAutomationValue(values, 2.1, 345.930);
|
||||
checkAutomationValue(values, 3, 891.601);
|
||||
checkAutomationValue(values, 5, 998.01);
|
||||
|
||||
// Refetch the automation data to ensure it recalculates correctly (bug 1118071)
|
||||
var { events, values } = yield oscNode.getAutomationData("frequency");
|
||||
|
||||
checkAutomationValue(values, 2.01, 215.055)
|
||||
checkAutomationValue(values, 2.1, 345.930);
|
||||
checkAutomationValue(values, 3, 891.601);
|
||||
checkAutomationValue(values, 5, 998.01);
|
||||
|
||||
yield removeTab(target.tab);
|
||||
});
|
||||
|
@ -7,6 +7,10 @@
|
||||
<!ENTITY cookiesonsystem.label "The following cookies are stored on your computer:">
|
||||
<!ENTITY cookiename.label "Cookie Name">
|
||||
<!ENTITY cookiedomain.label "Site">
|
||||
<!-- LOCALIZATION NOTE (button.removeSelectedCookies.accesskey):
|
||||
The label associated with this accesskey can be found in
|
||||
preferences.properties as removeSelectedCookies.
|
||||
-->
|
||||
<!ENTITY button.removeSelectedCookies.accesskey "R">
|
||||
<!ENTITY button.removeAllCookies.label "Remove All">
|
||||
<!ENTITY button.removeAllCookies.accesskey "A">
|
||||
|
@ -95,6 +95,10 @@ cookiesFiltered=The following cookies match your search:
|
||||
# LOCALIZATION NOTE (removeSelectedCookies):
|
||||
# Semicolon-separated list of plural forms. See:
|
||||
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
# If you need to display the number of selected elements in your language,
|
||||
# you can use #1 in your localization as a placeholder for the number.
|
||||
# For example this is the English string with numbers:
|
||||
# removeSelectedCookied=Remove #1 Selected;Remove #1 Selected
|
||||
removeSelectedCookies=Remove Selected;Remove Selected
|
||||
|
||||
#### Offline apps
|
||||
|
@ -177,3 +177,7 @@ toolbarpaletteitem:-moz-any([place="palette"], [place="panel"]) > toolbaritem[sd
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
toolbarpaletteitem[place="palette"] > .toolbarbutton-1 > .toolbarbutton-menubutton-button {
|
||||
padding: 3px 1px;
|
||||
}
|
@ -194,6 +194,10 @@ description > html|a {
|
||||
padding-bottom: 0; /* no padding needed in inContent prefs */
|
||||
}
|
||||
|
||||
#tabsElement {
|
||||
-moz-margin-end: 4px; /* add the 4px end-margin of other elements */
|
||||
}
|
||||
|
||||
#encryptionPanel {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountUtils;
|
||||
@ -59,6 +60,7 @@ public class FxAccountStatusFragment
|
||||
// schedule the sync as usual. See also comment below about garbage
|
||||
// collection.
|
||||
private static final long DELAY_IN_MILLISECONDS_BEFORE_REQUESTING_SYNC = 5 * 1000;
|
||||
private static final long LAST_SYNCED_TIME_UPDATE_INTERVAL_IN_MILLISECONDS = 60 * 1000;
|
||||
|
||||
// By default, the auth/account server preference is only shown when the
|
||||
// account is configured to use a custom server. In debug mode, this is set.
|
||||
@ -104,6 +106,9 @@ public class FxAccountStatusFragment
|
||||
// single account. (That is, it does not capture a single account instance.)
|
||||
protected Runnable requestSyncRunnable;
|
||||
|
||||
// Runnable to update last synced time.
|
||||
protected Runnable lastSyncedTimeUpdateRunnable;
|
||||
|
||||
protected final InnerSyncStatusDelegate syncStatusDelegate = new InnerSyncStatusDelegate();
|
||||
|
||||
protected Preference ensureFindPreference(String key) {
|
||||
@ -333,6 +338,9 @@ public class FxAccountStatusFragment
|
||||
|
||||
protected void showNeedsMasterSyncAutomaticallyEnabled() {
|
||||
syncCategory.setTitle(R.string.fxaccount_status_sync);
|
||||
needsMasterSyncAutomaticallyEnabledPreference.setTitle(AppConstants.Versions.preLollipop ?
|
||||
R.string.fxaccount_status_needs_master_sync_automatically_enabled :
|
||||
R.string.fxaccount_status_needs_master_sync_automatically_enabled_v21);
|
||||
showOnlyOneErrorPreference(needsMasterSyncAutomaticallyEnabledPreference);
|
||||
setCheckboxesEnabled(false);
|
||||
}
|
||||
@ -421,6 +429,7 @@ public class FxAccountStatusFragment
|
||||
// a reference to this fragment alive, but we expect posted runnables to be
|
||||
// serviced very quickly, so this is not an issue.
|
||||
requestSyncRunnable = new RequestSyncRunnable();
|
||||
lastSyncedTimeUpdateRunnable = new LastSyncTimeUpdateRunnable();
|
||||
|
||||
// We would very much like register these status observers in bookended
|
||||
// onResume/onPause calls, but because the Fragment gets onResume during the
|
||||
@ -437,6 +446,11 @@ public class FxAccountStatusFragment
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
FxAccountSyncStatusHelper.getInstance().stopObserving(syncStatusDelegate);
|
||||
|
||||
// Focus lost, remove scheduled update if any.
|
||||
if (lastSyncedTimeUpdateRunnable != null) {
|
||||
handler.removeCallbacks(lastSyncedTimeUpdateRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
protected void hardRefresh() {
|
||||
@ -531,8 +545,13 @@ public class FxAccountStatusFragment
|
||||
} else {
|
||||
syncNowPreference.setTitle(R.string.fxaccount_status_sync_now);
|
||||
}
|
||||
scheduleAndUpdateLastSyncedTime();
|
||||
}
|
||||
|
||||
private void scheduleAndUpdateLastSyncedTime() {
|
||||
final String lastSynced = getLastSyncedString(fxAccount.getLastSyncedTimestamp());
|
||||
syncNowPreference.setSummary(lastSynced);
|
||||
handler.postDelayed(lastSyncedTimeUpdateRunnable, LAST_SYNCED_TIME_UPDATE_INTERVAL_IN_MILLISECONDS);
|
||||
}
|
||||
|
||||
protected void updateAuthServerPreference() {
|
||||
@ -694,6 +713,16 @@ public class FxAccountStatusFragment
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Runnable that schedules a future update and updates the last synced time.
|
||||
*/
|
||||
protected class LastSyncTimeUpdateRunnable implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
scheduleAndUpdateLastSyncedTime();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A separate listener to separate debug logic from main code paths.
|
||||
*/
|
||||
|
@ -206,6 +206,7 @@
|
||||
<!ENTITY fxaccount_status_needs_credentials 'Cannot connect. Tap to sign in.'>
|
||||
<!ENTITY fxaccount_status_needs_upgrade 'You need to upgrade &brandShortName; to sign in.'>
|
||||
<!ENTITY fxaccount_status_needs_master_sync_automatically_enabled '&syncBrand.shortName.label; is set up, but not syncing automatically. Toggle “Auto-sync data” in Android Settings > Data Usage.'>
|
||||
<!ENTITY fxaccount_status_needs_master_sync_automatically_enabled_v21 '&syncBrand.shortName.label; is set up, but not syncing automatically. Toggle “Auto-sync data” in the menu of Android Settings > Accounts.'>
|
||||
<!ENTITY fxaccount_status_needs_account_enabled '&syncBrand.shortName.label; is set up, but not syncing automatically. Tap to start syncing.'>
|
||||
<!ENTITY fxaccount_status_needs_finish_migrating 'Tap to sign in to your new Firefox Account.'>
|
||||
<!ENTITY fxaccount_status_bookmarks 'Bookmarks'>
|
||||
|
147
mobile/android/chrome/content/about.js
Normal file
147
mobile/android/chrome/content/about.js
Normal file
@ -0,0 +1,147 @@
|
||||
/* 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/. */
|
||||
|
||||
let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils, Cr = Components.results;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function init() {
|
||||
// Include the build date and a warning about Telemetry
|
||||
// if this is an "a#" (nightly or aurora) build
|
||||
#expand const version = "__MOZ_APP_VERSION__";
|
||||
if (/a\d+$/.test(version)) {
|
||||
let buildID = Services.appinfo.appBuildID;
|
||||
let buildDate = buildID.slice(0, 4) + "-" + buildID.slice(4, 6) + "-" + buildID.slice(6, 8);
|
||||
let br = document.createElement("br");
|
||||
let versionPara = document.getElementById("version");
|
||||
versionPara.appendChild(br);
|
||||
let date = document.createTextNode("(" + buildDate + ")");
|
||||
versionPara.appendChild(date);
|
||||
document.getElementById("telemetry").hidden = false;
|
||||
}
|
||||
|
||||
// Include the Distribution information if available
|
||||
try {
|
||||
let distroId = Services.prefs.getCharPref("distribution.id");
|
||||
if (distroId) {
|
||||
let distroVersion = Services.prefs.getCharPref("distribution.version");
|
||||
let distroIdField = document.getElementById("distributionID");
|
||||
distroIdField.textContent = distroId + " - " + distroVersion;
|
||||
distroIdField.hidden = false;
|
||||
|
||||
let distroAbout = Services.prefs.getComplexValue("distribution.about", Ci.nsISupportsString);
|
||||
let distroField = document.getElementById("distributionAbout");
|
||||
distroField.textContent = distroAbout;
|
||||
distroField.hidden = false;
|
||||
}
|
||||
} catch (e) {
|
||||
// Pref is unset
|
||||
}
|
||||
|
||||
// get URLs from prefs
|
||||
try {
|
||||
let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
|
||||
|
||||
let links = [
|
||||
{id: "releaseNotesURL", pref: "app.releaseNotesURL"},
|
||||
{id: "supportURL", pref: "app.support.baseURL"},
|
||||
{id: "faqURL", pref: "app.faqURL"},
|
||||
{id: "privacyURL", pref: "app.privacyURL"},
|
||||
{id: "creditsURL", pref: "app.creditsURL"},
|
||||
];
|
||||
|
||||
links.forEach(function(link) {
|
||||
let url = formatter.formatURLPref(link.pref);
|
||||
let element = document.getElementById(link.id);
|
||||
element.setAttribute("href", url);
|
||||
});
|
||||
} catch (ex) {}
|
||||
|
||||
#ifdef MOZ_UPDATER
|
||||
let Updater = {
|
||||
update: null,
|
||||
|
||||
init: function() {
|
||||
Services.obs.addObserver(this, "Update:CheckResult", false);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "Update:CheckResult") {
|
||||
showUpdateMessage(aData);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Updater.init();
|
||||
|
||||
function checkForUpdates() {
|
||||
showCheckingMessage();
|
||||
|
||||
Services.androidBridge.handleGeckoMessage({ type: "Update:Check" });
|
||||
}
|
||||
|
||||
function downloadUpdate() {
|
||||
Services.androidBridge.handleGeckoMessage({ type: "Update:Download" });
|
||||
}
|
||||
|
||||
function installUpdate() {
|
||||
showCheckAction();
|
||||
|
||||
Services.androidBridge.handleGeckoMessage({ type: "Update:Install" });
|
||||
}
|
||||
|
||||
let updateLink = document.getElementById("updateLink");
|
||||
let checkingSpan = document.getElementById("update-message-checking");
|
||||
let noneSpan = document.getElementById("update-message-none");
|
||||
let foundSpan = document.getElementById("update-message-found");
|
||||
let downloadingSpan = document.getElementById("update-message-downloading");
|
||||
let downloadedSpan = document.getElementById("update-message-downloaded");
|
||||
|
||||
function showCheckAction() {
|
||||
checkingSpan.style.display = "none";
|
||||
noneSpan.style.display = "none";
|
||||
foundSpan.style.display = "none";
|
||||
downloadingSpan.style.display = "none";
|
||||
downloadedSpan.style.display = "none";
|
||||
updateLink.style.display = "block";
|
||||
}
|
||||
|
||||
function showCheckingMessage() {
|
||||
updateLink.style.display = "none";
|
||||
noneSpan.style.display = "none";
|
||||
foundSpan.style.display = "none";
|
||||
downloadingSpan.style.display = "none";
|
||||
downloadedSpan.style.display = "none";
|
||||
checkingSpan.style.display = "block";
|
||||
}
|
||||
|
||||
function showUpdateMessage(aResult) {
|
||||
updateLink.style.display = "none";
|
||||
checkingSpan.style.display = "none";
|
||||
noneSpan.style.display = "none";
|
||||
foundSpan.style.display = "none";
|
||||
downloadingSpan.style.display = "none";
|
||||
downloadedSpan.style.display = "none";
|
||||
|
||||
// the aResult values come from mobile/android/base/UpdateServiceHelper.java
|
||||
switch (aResult) {
|
||||
case "NOT_AVAILABLE":
|
||||
noneSpan.style.display = "block";
|
||||
setTimeout(showCheckAction, 2000);
|
||||
break;
|
||||
case "AVAILABLE":
|
||||
foundSpan.style.display = "block";
|
||||
break;
|
||||
case "DOWNLOADING":
|
||||
downloadingSpan.style.display = "block";
|
||||
break;
|
||||
case "DOWNLOADED":
|
||||
downloadedSpan.style.display = "block";
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", init, false);
|
@ -71,146 +71,7 @@
|
||||
</div>
|
||||
#endif
|
||||
|
||||
<script type="application/javascript;version=1.8"><![CDATA[
|
||||
let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils, Cr = Components.results;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
<script type="application/javascript;version=1.8" src="chrome://browser/content/about.js" />
|
||||
|
||||
// Include the build date and a warning about Telemetry
|
||||
// if this is an "a#" (nightly or aurora) build
|
||||
#expand const version = "__MOZ_APP_VERSION__";
|
||||
if (/a\d+$/.test(version)) {
|
||||
let buildID = Services.appinfo.appBuildID;
|
||||
let buildDate = buildID.slice(0,4) + "-" + buildID.slice(4,6) + "-" + buildID.slice(6,8);
|
||||
let br = document.createElement("br");
|
||||
let versionPara = document.getElementById("version");
|
||||
versionPara.appendChild(br);
|
||||
let date = document.createTextNode("(" + buildDate + ")");
|
||||
versionPara.appendChild(date);
|
||||
document.getElementById("telemetry").hidden = false;
|
||||
}
|
||||
|
||||
// Include the Distribution information if available
|
||||
try {
|
||||
let distroId = Services.prefs.getCharPref("distribution.id");
|
||||
if (distroId) {
|
||||
let distroVersion = Services.prefs.getCharPref("distribution.version");
|
||||
let distroIdField = document.getElementById("distributionID");
|
||||
distroIdField.textContent = distroId + " - " + distroVersion;
|
||||
distroIdField.hidden = false;
|
||||
|
||||
let distroAbout = Services.prefs.getComplexValue("distribution.about", Ci.nsISupportsString);
|
||||
let distroField = document.getElementById("distributionAbout");
|
||||
distroField.textContent = distroAbout;
|
||||
distroField.hidden = false;
|
||||
}
|
||||
} catch (e) {
|
||||
// Pref is unset
|
||||
}
|
||||
|
||||
// get URLs from prefs
|
||||
try {
|
||||
let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
|
||||
|
||||
let links = [
|
||||
{id: "releaseNotesURL", pref: "app.releaseNotesURL"},
|
||||
{id: "supportURL", pref: "app.support.baseURL"},
|
||||
{id: "faqURL", pref: "app.faqURL"},
|
||||
{id: "privacyURL", pref: "app.privacyURL"},
|
||||
{id: "creditsURL", pref: "app.creditsURL"},
|
||||
];
|
||||
|
||||
links.forEach(function (link) {
|
||||
let url = formatter.formatURLPref(link.pref);
|
||||
let element = document.getElementById(link.id);
|
||||
element.setAttribute("href", url);
|
||||
});
|
||||
} catch (ex) {}
|
||||
|
||||
#ifdef MOZ_UPDATER
|
||||
let Updater = {
|
||||
update: null,
|
||||
|
||||
init: function() {
|
||||
Services.obs.addObserver(this, "Update:CheckResult", false);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "Update:CheckResult") {
|
||||
showUpdateMessage(aData);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Updater.init();
|
||||
|
||||
function checkForUpdates() {
|
||||
showCheckingMessage();
|
||||
|
||||
Services.androidBridge.handleGeckoMessage({ type: "Update:Check" });
|
||||
}
|
||||
|
||||
function downloadUpdate() {
|
||||
Services.androidBridge.handleGeckoMessage({ type: "Update:Download" });
|
||||
}
|
||||
|
||||
function installUpdate() {
|
||||
showCheckAction();
|
||||
|
||||
Services.androidBridge.handleGeckoMessage({ type: "Update:Install" });
|
||||
}
|
||||
|
||||
let updateLink = document.getElementById("updateLink");
|
||||
let checkingSpan = document.getElementById("update-message-checking");
|
||||
let noneSpan = document.getElementById("update-message-none");
|
||||
let foundSpan = document.getElementById("update-message-found");
|
||||
let downloadingSpan = document.getElementById("update-message-downloading");
|
||||
let downloadedSpan = document.getElementById("update-message-downloaded");
|
||||
|
||||
function showCheckAction() {
|
||||
checkingSpan.style.display = "none";
|
||||
noneSpan.style.display = "none";
|
||||
foundSpan.style.display = "none";
|
||||
downloadingSpan.style.display = "none";
|
||||
downloadedSpan.style.display = "none";
|
||||
updateLink.style.display = "block";
|
||||
}
|
||||
|
||||
function showCheckingMessage() {
|
||||
updateLink.style.display = "none";
|
||||
noneSpan.style.display = "none";
|
||||
foundSpan.style.display = "none";
|
||||
downloadingSpan.style.display = "none";
|
||||
downloadedSpan.style.display = "none";
|
||||
checkingSpan.style.display = "block";
|
||||
}
|
||||
|
||||
function showUpdateMessage(aResult) {
|
||||
updateLink.style.display = "none";
|
||||
checkingSpan.style.display = "none";
|
||||
noneSpan.style.display = "none";
|
||||
foundSpan.style.display = "none";
|
||||
downloadingSpan.style.display = "none";
|
||||
downloadedSpan.style.display = "none";
|
||||
|
||||
// the aResult values come from mobile/android/base/UpdateServiceHelper.java
|
||||
switch (aResult) {
|
||||
case "NOT_AVAILABLE":
|
||||
noneSpan.style.display = "block";
|
||||
setTimeout(showCheckAction, 2000);
|
||||
break;
|
||||
case "AVAILABLE":
|
||||
foundSpan.style.display = "block";
|
||||
break;
|
||||
case "DOWNLOADING":
|
||||
downloadingSpan.style.display = "block";
|
||||
break;
|
||||
case "DOWNLOADED":
|
||||
downloadedSpan.style.display = "block";
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
]]></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -369,13 +369,16 @@ var BrowserApp = {
|
||||
|
||||
// Queue up some other performance-impacting initializations
|
||||
Services.tm.mainThread.dispatch(function() {
|
||||
// Init LoginManager
|
||||
Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
|
||||
CastingApps.init();
|
||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
|
||||
BrowserApp.gmpInstallManager = new GMPInstallManager();
|
||||
BrowserApp.gmpInstallManager.simpleCheckAndInstall().then(null, () => {});
|
||||
CastingApps.init();
|
||||
|
||||
// Delay this a minute because there's no rush
|
||||
setTimeout(() => {
|
||||
BrowserApp.gmpInstallManager = new GMPInstallManager();
|
||||
BrowserApp.gmpInstallManager.simpleCheckAndInstall().then(null, () => {});
|
||||
}, 1000 * 60);
|
||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
Services.tm.mainThread.dispatch(function() {
|
||||
|
@ -8,6 +8,7 @@ chrome.jar:
|
||||
% content browser %content/ contentaccessible=yes
|
||||
|
||||
* content/about.xhtml (content/about.xhtml)
|
||||
* content/about.js (content/about.js)
|
||||
content/config.xhtml (content/config.xhtml)
|
||||
content/config.js (content/config.js)
|
||||
content/content.js (content/content.js)
|
||||
|
@ -196,6 +196,7 @@
|
||||
<string name="fxaccount_status_needs_credentials">&fxaccount_status_needs_credentials;</string>
|
||||
<string name="fxaccount_status_needs_upgrade">&fxaccount_status_needs_upgrade;</string>
|
||||
<string name="fxaccount_status_needs_master_sync_automatically_enabled">&fxaccount_status_needs_master_sync_automatically_enabled;</string>
|
||||
<string name="fxaccount_status_needs_master_sync_automatically_enabled_v21">&fxaccount_status_needs_master_sync_automatically_enabled_v21;</string>
|
||||
<string name="fxaccount_status_needs_account_enabled">&fxaccount_status_needs_account_enabled;</string>
|
||||
<string name="fxaccount_status_needs_finish_migrating">&fxaccount_status_needs_finish_migrating;</string>
|
||||
<string name="fxaccount_status_bookmarks">&fxaccount_status_bookmarks;</string>
|
||||
|
@ -67,5 +67,16 @@ public class AppGlobals {
|
||||
|
||||
public static final String ACTION_TEST_SETTING_ENABLED = "stumbler-test-setting-enabled";
|
||||
public static final String ACTION_TEST_SETTING_DISABLED = "stumbler-test-setting-disabled";
|
||||
|
||||
// Histogram values
|
||||
public static final String TELEMETRY_TIME_BETWEEN_UPLOADS_SEC = "STUMBLER_TIME_BETWEEN_UPLOADS_SEC";
|
||||
public static final String TELEMETRY_BYTES_UPLOADED_PER_SEC = "STUMBLER_VOLUME_BYTES_UPLOADED_PER_SEC";
|
||||
public static final String TELEMETRY_TIME_BETWEEN_STARTS_SEC = "STUMBLER_TIME_BETWEEN_START_SEC";
|
||||
public static final String TELEMETRY_BYTES_PER_UPLOAD = "STUMBLER_UPLOAD_BYTES";
|
||||
public static final String TELEMETRY_OBSERVATIONS_PER_UPLOAD = "STUMBLER_UPLOAD_OBSERVATION_COUNT";
|
||||
public static final String TELEMETRY_CELLS_PER_UPLOAD = "STUMBLER_UPLOAD_CELL_COUNT";
|
||||
public static final String TELEMETRY_WIFIS_PER_UPLOAD = "STUMBLER_UPLOAD_WIFI_AP_COUNT";
|
||||
public static final String TELEMETRY_OBSERVATIONS_PER_DAY = "STUMBLER_OBSERVATIONS_PER_DAY";
|
||||
public static final String TELEMETRY_TIME_BETWEEN_RECEIVED_LOCATIONS_SEC = "STUMBLER_TIME_BETWEEN_RECEIVED_LOCATIONS_SEC";
|
||||
}
|
||||
|
||||
|
@ -21,12 +21,13 @@ public final class DataStorageContract {
|
||||
|
||||
public static class Stats {
|
||||
public static final String KEY_VERSION = "version_code";
|
||||
public static final int VERSION_CODE = 1;
|
||||
public static final int VERSION_CODE = 2;
|
||||
public static final String KEY_BYTES_SENT = "bytes_sent";
|
||||
public static final String KEY_LAST_UPLOAD_TIME = "last_upload_time";
|
||||
public static final String KEY_OBSERVATIONS_SENT = "observations_sent";
|
||||
public static final String KEY_WIFIS_SENT = "wifis_sent";
|
||||
public static final String KEY_CELLS_SENT = "cells_sent";
|
||||
public static final String KEY_OBSERVATIONS_PER_DAY = "obs_per_day";
|
||||
}
|
||||
|
||||
private DataStorageContract() {
|
||||
|
@ -69,6 +69,7 @@ public class DataStorageManager {
|
||||
private ReportBatchIterator mReportBatchIterator;
|
||||
private final ReportFileList mFileList;
|
||||
private Timer mFlushMemoryBuffersToDiskTimer;
|
||||
private final PersistedStats mPersistedOnDiskUploadStats;
|
||||
|
||||
static final String SEP_REPORT_COUNT = "-r";
|
||||
static final String SEP_WIFI_COUNT = "-w";
|
||||
@ -242,6 +243,7 @@ public class DataStorageManager {
|
||||
}
|
||||
mFileList = new ReportFileList();
|
||||
mFileList.update(mReportsDir);
|
||||
mPersistedOnDiskUploadStats = new PersistedStats(baseDir);
|
||||
}
|
||||
|
||||
public synchronized int getMaxWeeksStored() {
|
||||
@ -448,51 +450,6 @@ public class DataStorageManager {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized Properties readSyncStats() throws IOException {
|
||||
if (!mStatsFile.exists()) {
|
||||
return new Properties();
|
||||
}
|
||||
|
||||
final FileInputStream input = new FileInputStream(mStatsFile);
|
||||
try {
|
||||
final Properties props = new Properties();
|
||||
props.load(input);
|
||||
return props;
|
||||
} finally {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void incrementSyncStats(long bytesSent, long reports, long cells, long wifis) throws IOException {
|
||||
if (reports + cells + wifis < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Properties properties = readSyncStats();
|
||||
final long time = System.currentTimeMillis();
|
||||
writeSyncStats(time,
|
||||
Long.parseLong(properties.getProperty(DataStorageContract.Stats.KEY_BYTES_SENT, "0")) + bytesSent,
|
||||
Long.parseLong(properties.getProperty(DataStorageContract.Stats.KEY_OBSERVATIONS_SENT, "0")) + reports,
|
||||
Long.parseLong(properties.getProperty(DataStorageContract.Stats.KEY_CELLS_SENT, "0")) + cells,
|
||||
Long.parseLong(properties.getProperty(DataStorageContract.Stats.KEY_WIFIS_SENT, "0")) + wifis);
|
||||
}
|
||||
|
||||
public void writeSyncStats(long time, long bytesSent, long totalObs, long totalCells, long totalWifis) throws IOException {
|
||||
final FileOutputStream out = new FileOutputStream(mStatsFile);
|
||||
try {
|
||||
final Properties props = new Properties();
|
||||
props.setProperty(DataStorageContract.Stats.KEY_LAST_UPLOAD_TIME, String.valueOf(time));
|
||||
props.setProperty(DataStorageContract.Stats.KEY_BYTES_SENT, String.valueOf(bytesSent));
|
||||
props.setProperty(DataStorageContract.Stats.KEY_OBSERVATIONS_SENT, String.valueOf(totalObs));
|
||||
props.setProperty(DataStorageContract.Stats.KEY_CELLS_SENT, String.valueOf(totalCells));
|
||||
props.setProperty(DataStorageContract.Stats.KEY_WIFIS_SENT, String.valueOf(totalWifis));
|
||||
props.setProperty(DataStorageContract.Stats.KEY_VERSION, String.valueOf(DataStorageContract.Stats.VERSION_CODE));
|
||||
props.store(out, null);
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void deleteAll() {
|
||||
if (mFileList.mFiles == null) {
|
||||
return;
|
||||
@ -509,4 +466,8 @@ public class DataStorageManager {
|
||||
mTracker.notifyStorageStateEmpty(isEmpty);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void incrementSyncStats(long bytesSent, long reports, long cells, long wifis) throws IOException {
|
||||
mPersistedOnDiskUploadStats.incrementSyncStats(bytesSent, reports, cells, wifis);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,99 @@
|
||||
/* 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.mozstumbler.service.stumblerthread.datahandling;
|
||||
|
||||
import org.mozilla.mozstumbler.service.AppGlobals;
|
||||
import org.mozilla.mozstumbler.service.utils.TelemetryWrapper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
class PersistedStats {
|
||||
private final File mStatsFile;
|
||||
|
||||
public PersistedStats(String baseDir) {
|
||||
mStatsFile = new File(baseDir, "upload_stats.ini");
|
||||
}
|
||||
|
||||
public synchronized Properties readSyncStats() throws IOException {
|
||||
if (!mStatsFile.exists()) {
|
||||
return new Properties();
|
||||
}
|
||||
|
||||
final FileInputStream input = new FileInputStream(mStatsFile);
|
||||
try {
|
||||
final Properties props = new Properties();
|
||||
props.load(input);
|
||||
return props;
|
||||
} finally {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void incrementSyncStats(long bytesSent, long reports, long cells, long wifis) throws IOException {
|
||||
if (reports + cells + wifis < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Properties properties = readSyncStats();
|
||||
final long time = System.currentTimeMillis();
|
||||
final long lastUploadTime = Long.parseLong(properties.getProperty(DataStorageContract.Stats.KEY_LAST_UPLOAD_TIME, "0"));
|
||||
final long storedObsPerDay = Long.parseLong(properties.getProperty(DataStorageContract.Stats.KEY_OBSERVATIONS_PER_DAY, "0"));
|
||||
long observationsToday = reports;
|
||||
if (lastUploadTime > 0) {
|
||||
long dayLastUploaded = TimeUnit.MILLISECONDS.toDays(lastUploadTime);
|
||||
long dayDiff = TimeUnit.MILLISECONDS.toDays(time) - dayLastUploaded;
|
||||
if (dayDiff > 0) {
|
||||
// send value of store obs per day
|
||||
TelemetryWrapper.addToHistogram(AppGlobals.TELEMETRY_OBSERVATIONS_PER_DAY,
|
||||
Long.valueOf(storedObsPerDay / dayDiff).intValue());
|
||||
} else {
|
||||
observationsToday += storedObsPerDay;
|
||||
}
|
||||
}
|
||||
|
||||
writeSyncStats(time,
|
||||
Long.parseLong(properties.getProperty(DataStorageContract.Stats.KEY_BYTES_SENT, "0")) + bytesSent,
|
||||
Long.parseLong(properties.getProperty(DataStorageContract.Stats.KEY_OBSERVATIONS_SENT, "0")) + reports,
|
||||
Long.parseLong(properties.getProperty(DataStorageContract.Stats.KEY_CELLS_SENT, "0")) + cells,
|
||||
Long.parseLong(properties.getProperty(DataStorageContract.Stats.KEY_WIFIS_SENT, "0")) + wifis,
|
||||
observationsToday);
|
||||
|
||||
|
||||
final long lastUploadMs = Long.parseLong(properties.getProperty(DataStorageContract.Stats.KEY_LAST_UPLOAD_TIME, "0"));
|
||||
final int timeDiffSec = Long.valueOf((time - lastUploadMs) / 1000).intValue();
|
||||
if (lastUploadMs > 0 && timeDiffSec > 0) {
|
||||
TelemetryWrapper.addToHistogram(AppGlobals.TELEMETRY_TIME_BETWEEN_UPLOADS_SEC, timeDiffSec);
|
||||
TelemetryWrapper.addToHistogram(AppGlobals.TELEMETRY_BYTES_UPLOADED_PER_SEC, Long.valueOf(bytesSent).intValue() / timeDiffSec);
|
||||
}
|
||||
TelemetryWrapper.addToHistogram(AppGlobals.TELEMETRY_BYTES_PER_UPLOAD, Long.valueOf(bytesSent).intValue());
|
||||
TelemetryWrapper.addToHistogram(AppGlobals.TELEMETRY_OBSERVATIONS_PER_UPLOAD, Long.valueOf(reports).intValue());
|
||||
TelemetryWrapper.addToHistogram(AppGlobals.TELEMETRY_WIFIS_PER_UPLOAD, Long.valueOf(wifis).intValue());
|
||||
TelemetryWrapper.addToHistogram(AppGlobals.TELEMETRY_CELLS_PER_UPLOAD, Long.valueOf(cells).intValue());
|
||||
}
|
||||
|
||||
public synchronized void writeSyncStats(long time, long bytesSent, long totalObs,
|
||||
long totalCells, long totalWifis, long obsPerDay) throws IOException {
|
||||
final FileOutputStream out = new FileOutputStream(mStatsFile);
|
||||
try {
|
||||
final Properties props = new Properties();
|
||||
props.setProperty(DataStorageContract.Stats.KEY_LAST_UPLOAD_TIME, String.valueOf(time));
|
||||
props.setProperty(DataStorageContract.Stats.KEY_BYTES_SENT, String.valueOf(bytesSent));
|
||||
props.setProperty(DataStorageContract.Stats.KEY_OBSERVATIONS_SENT, String.valueOf(totalObs));
|
||||
props.setProperty(DataStorageContract.Stats.KEY_CELLS_SENT, String.valueOf(totalCells));
|
||||
props.setProperty(DataStorageContract.Stats.KEY_WIFIS_SENT, String.valueOf(totalWifis));
|
||||
props.setProperty(DataStorageContract.Stats.KEY_VERSION, String.valueOf(DataStorageContract.Stats.VERSION_CODE));
|
||||
props.setProperty(DataStorageContract.Stats.KEY_OBSERVATIONS_PER_DAY, String.valueOf(obsPerDay));
|
||||
props.store(out, null);
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -15,10 +15,10 @@ import android.location.LocationProvider;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.util.Log;
|
||||
|
||||
import org.mozilla.mozstumbler.service.AppGlobals;
|
||||
import org.mozilla.mozstumbler.service.AppGlobals.ActiveOrPassiveStumbling;
|
||||
import org.mozilla.mozstumbler.service.Prefs;
|
||||
import org.mozilla.mozstumbler.service.utils.TelemetryWrapper;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
@ -48,7 +48,7 @@ public class GPSScanner implements LocationListener {
|
||||
private Location mLocation = new Location("internal");
|
||||
private boolean mAutoGeofencing;
|
||||
private boolean mIsPassiveMode;
|
||||
|
||||
private long mTelemetry_lastStartedMs;
|
||||
private final ScanManager mScanManager;
|
||||
|
||||
public GPSScanner(Context context, ScanManager scanManager) {
|
||||
@ -82,9 +82,13 @@ public class GPSScanner implements LocationListener {
|
||||
return;
|
||||
}
|
||||
|
||||
locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
|
||||
0,
|
||||
0, this);
|
||||
locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, this);
|
||||
|
||||
final int timeDiffSec = Long.valueOf((System.currentTimeMillis() - mTelemetry_lastStartedMs) / 1000).intValue();
|
||||
if (mTelemetry_lastStartedMs > 0 && timeDiffSec > 0) {
|
||||
TelemetryWrapper.addToHistogram(AppGlobals.TELEMETRY_TIME_BETWEEN_STARTS_SEC, timeDiffSec);
|
||||
}
|
||||
mTelemetry_lastStartedMs = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private void startActiveMode() {
|
||||
@ -195,13 +199,14 @@ public class GPSScanner implements LocationListener {
|
||||
return;
|
||||
}
|
||||
|
||||
final long timeDeltaMs = location.getTime() - mLocation.getTime();
|
||||
|
||||
// Seem to get greater likelihood of non-fused location with higher update freq.
|
||||
// Check dist and time threshold here, not set on the listener.
|
||||
if (mIsPassiveMode) {
|
||||
final long timeDelta = location.getTime() - mLocation.getTime();
|
||||
final boolean hasMoved = location.distanceTo(mLocation) > PASSIVE_GPS_MOVEMENT_MIN_DELTA_M;
|
||||
|
||||
if (timeDelta < PASSIVE_GPS_MIN_UPDATE_FREQ_MS || !hasMoved) {
|
||||
if (timeDeltaMs < PASSIVE_GPS_MIN_UPDATE_FREQ_MS || !hasMoved) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -228,6 +233,11 @@ public class GPSScanner implements LocationListener {
|
||||
if (mIsPassiveMode) {
|
||||
mScanManager.newPassiveGpsLocation();
|
||||
}
|
||||
|
||||
if (timeDeltaMs > 0) {
|
||||
TelemetryWrapper.addToHistogram(AppGlobals.TELEMETRY_TIME_BETWEEN_RECEIVED_LOCATIONS_SEC,
|
||||
Long.valueOf(timeDeltaMs).intValue() / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,35 @@
|
||||
package org.mozilla.mozstumbler.service.utils;
|
||||
|
||||
import android.util.Log;
|
||||
import org.mozilla.mozstumbler.service.AppGlobals;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class TelemetryWrapper {
|
||||
private static final String LOG_TAG = AppGlobals.makeLogTag(TelemetryWrapper.class.getSimpleName());
|
||||
private static Method mAddToHistogram;
|
||||
|
||||
public static void addToHistogram(String key, int value) {
|
||||
if (mAddToHistogram == null) {
|
||||
try {
|
||||
Class<?> telemetry = Class.forName("org.mozilla.gecko.Telemetry");
|
||||
mAddToHistogram = telemetry.getMethod("addToHistogram", String.class, int.class);
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.d(LOG_TAG, "Class not found!");
|
||||
return;
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.d(LOG_TAG, "Method not found!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mAddToHistogram != null) {
|
||||
try {
|
||||
mAddToHistogram.invoke(null, key, value);
|
||||
}
|
||||
catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
|
||||
Log.d(LOG_TAG, "Got exception invoking.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ stumbler_sources = [
|
||||
'java/org/mozilla/mozstumbler/service/stumblerthread/blocklist/WifiBlockListInterface.java',
|
||||
'java/org/mozilla/mozstumbler/service/stumblerthread/datahandling/DataStorageContract.java',
|
||||
'java/org/mozilla/mozstumbler/service/stumblerthread/datahandling/DataStorageManager.java',
|
||||
'java/org/mozilla/mozstumbler/service/stumblerthread/datahandling/PersistedStats.java',
|
||||
'java/org/mozilla/mozstumbler/service/stumblerthread/datahandling/StumblerBundle.java',
|
||||
'java/org/mozilla/mozstumbler/service/stumblerthread/Reporter.java',
|
||||
'java/org/mozilla/mozstumbler/service/stumblerthread/scanners/cellscanner/CellInfo.java',
|
||||
@ -28,5 +29,6 @@ stumbler_sources = [
|
||||
'java/org/mozilla/mozstumbler/service/utils/AbstractCommunicator.java',
|
||||
'java/org/mozilla/mozstumbler/service/utils/NetworkUtils.java',
|
||||
'java/org/mozilla/mozstumbler/service/utils/PersistentIntentService.java',
|
||||
'java/org/mozilla/mozstumbler/service/utils/TelemetryWrapper.java',
|
||||
'java/org/mozilla/mozstumbler/service/utils/Zipper.java',
|
||||
]
|
||||
|
@ -1,6 +1,6 @@
|
||||
Metadata-Version: 1.1
|
||||
Name: virtualenv
|
||||
Version: 12.0.2
|
||||
Version: 12.0.5
|
||||
Summary: Virtual Python Environment builder
|
||||
Home-page: https://virtualenv.pypa.io/
|
||||
Author: Jannis Leidel, Carl Meyer and Brian Rosner
|
||||
@ -47,6 +47,30 @@ Description: Virtualenv
|
||||
Release History
|
||||
===============
|
||||
|
||||
12.0.5 (2015-01-03)
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Upgrade pip to 6.0.6
|
||||
|
||||
* Upgrade setuptools to 11.0
|
||||
|
||||
|
||||
12.0.4 (2014-22-23)
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Revert the fix to ``-p`` on Debian based pythons as it was broken in other
|
||||
situations.
|
||||
|
||||
* Revert several sys.path changes new in 12.0 which were breaking virtualenv.
|
||||
|
||||
12.0.3 (2014-22-23)
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Fix an issue where Debian based Pythons would fail when using -p with the
|
||||
host Python.
|
||||
|
||||
* Upgrade pip to 6.0.3
|
||||
|
||||
12.0.2 (2014-12-23)
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -1,6 +1,30 @@
|
||||
Release History
|
||||
===============
|
||||
|
||||
12.0.5 (2015-01-03)
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Upgrade pip to 6.0.6
|
||||
|
||||
* Upgrade setuptools to 11.0
|
||||
|
||||
|
||||
12.0.4 (2014-22-23)
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Revert the fix to ``-p`` on Debian based pythons as it was broken in other
|
||||
situations.
|
||||
|
||||
* Revert several sys.path changes new in 12.0 which were breaking virtualenv.
|
||||
|
||||
12.0.3 (2014-22-23)
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Fix an issue where Debian based Pythons would fail when using -p with the
|
||||
host Python.
|
||||
|
||||
* Upgrade pip to 6.0.3
|
||||
|
||||
12.0.2 (2014-12-23)
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
[egg_info]
|
||||
tag_svn_revision = 0
|
||||
tag_build =
|
||||
tag_svn_revision = 0
|
||||
tag_date = 0
|
||||
|
||||
|
@ -2,35 +2,12 @@
|
||||
"""Create a "virtual" Python installation
|
||||
"""
|
||||
|
||||
__version__ = "12.0.2"
|
||||
__version__ = "12.0.5"
|
||||
virtualenv_version = __version__ # legacy
|
||||
|
||||
# NB: avoid placing additional imports here, before sys.path is fixed!
|
||||
|
||||
import base64
|
||||
import sys
|
||||
import os
|
||||
|
||||
#
|
||||
# RATIONALE:
|
||||
# This script is both it's own "host" and "guest". If it's running in "guest
|
||||
# mode" (inside the virtualenv interpreter), it's essentially invoked via:
|
||||
# /path/to/python /path/to/this/script.py
|
||||
#
|
||||
# Which, by the nature of Python, will put `/path/to/this` on the system path
|
||||
# as the first argument. Now this can cause many subtle bugs, because the
|
||||
# rest of the script is now looking to import from the "host" Python version
|
||||
# first. This has been especially troublesome when trying to create a Python
|
||||
# 3 "guest" env using a Python 2 "host", but even with minor Python
|
||||
# differences, there may been some bleeding between environments that doesn't
|
||||
# stand out as obviously.
|
||||
#
|
||||
# This removes the first argument off the system path, to avoid any accidental
|
||||
# usage of the "host" library directories.
|
||||
#
|
||||
if os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'):
|
||||
del sys.path[0]
|
||||
|
||||
import base64
|
||||
import codecs
|
||||
import optparse
|
||||
import re
|
||||
@ -1108,33 +1085,45 @@ def change_prefix(filename, dst_prefix):
|
||||
|
||||
def copy_required_modules(dst_prefix, symlink):
|
||||
import imp
|
||||
for modname in REQUIRED_MODULES:
|
||||
if modname in sys.builtin_module_names:
|
||||
logger.info("Ignoring built-in bootstrap module: %s" % modname)
|
||||
continue
|
||||
try:
|
||||
f, filename, _ = imp.find_module(modname)
|
||||
except ImportError:
|
||||
logger.info("Cannot import bootstrap module: %s" % modname)
|
||||
else:
|
||||
if f is not None:
|
||||
f.close()
|
||||
# special-case custom readline.so on OS X, but not for pypy:
|
||||
if modname == 'readline' and sys.platform == 'darwin' and not (
|
||||
is_pypy or filename.endswith(join('lib-dynload', 'readline.so'))):
|
||||
dst_filename = join(dst_prefix, 'lib', 'python%s' % sys.version[:3], 'readline.so')
|
||||
elif modname == 'readline' and sys.platform == 'win32':
|
||||
# special-case for Windows, where readline is not a
|
||||
# standard module, though it may have been installed in
|
||||
# site-packages by a third-party package
|
||||
pass
|
||||
# If we are running under -p, we need to remove the current
|
||||
# directory from sys.path temporarily here, so that we
|
||||
# definitely get the modules from the site directory of
|
||||
# the interpreter we are running under, not the one
|
||||
# virtualenv.py is installed under (which might lead to py2/py3
|
||||
# incompatibility issues)
|
||||
_prev_sys_path = sys.path
|
||||
if os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'):
|
||||
sys.path = sys.path[1:]
|
||||
try:
|
||||
for modname in REQUIRED_MODULES:
|
||||
if modname in sys.builtin_module_names:
|
||||
logger.info("Ignoring built-in bootstrap module: %s" % modname)
|
||||
continue
|
||||
try:
|
||||
f, filename, _ = imp.find_module(modname)
|
||||
except ImportError:
|
||||
logger.info("Cannot import bootstrap module: %s" % modname)
|
||||
else:
|
||||
dst_filename = change_prefix(filename, dst_prefix)
|
||||
copyfile(filename, dst_filename, symlink)
|
||||
if filename.endswith('.pyc'):
|
||||
pyfile = filename[:-1]
|
||||
if os.path.exists(pyfile):
|
||||
copyfile(pyfile, dst_filename[:-1], symlink)
|
||||
if f is not None:
|
||||
f.close()
|
||||
# special-case custom readline.so on OS X, but not for pypy:
|
||||
if modname == 'readline' and sys.platform == 'darwin' and not (
|
||||
is_pypy or filename.endswith(join('lib-dynload', 'readline.so'))):
|
||||
dst_filename = join(dst_prefix, 'lib', 'python%s' % sys.version[:3], 'readline.so')
|
||||
elif modname == 'readline' and sys.platform == 'win32':
|
||||
# special-case for Windows, where readline is not a
|
||||
# standard module, though it may have been installed in
|
||||
# site-packages by a third-party package
|
||||
pass
|
||||
else:
|
||||
dst_filename = change_prefix(filename, dst_prefix)
|
||||
copyfile(filename, dst_filename, symlink)
|
||||
if filename.endswith('.pyc'):
|
||||
pyfile = filename[:-1]
|
||||
if os.path.exists(pyfile):
|
||||
copyfile(pyfile, dst_filename[:-1], symlink)
|
||||
finally:
|
||||
sys.path = _prev_sys_path
|
||||
|
||||
|
||||
def subst_path(prefix_path, prefix, home_dir):
|
||||
|
Binary file not shown.
Binary file not shown.
@ -7069,5 +7069,68 @@
|
||||
"kind": "count",
|
||||
"keyed": true,
|
||||
"description": "Counts of plugin and content process crashes which are reported with a crash dump."
|
||||
},
|
||||
"STUMBLER_TIME_BETWEEN_UPLOADS_SEC": {
|
||||
"expires_in_version": "45",
|
||||
"kind": "exponential",
|
||||
"n_buckets": 50,
|
||||
"high": 259200,
|
||||
"description": "Stumbler: The time in seconds between uploads."
|
||||
},
|
||||
"STUMBLER_VOLUME_BYTES_UPLOADED_PER_SEC": {
|
||||
"expires_in_version": "45",
|
||||
"kind": "exponential",
|
||||
"n_buckets": 50,
|
||||
"high": 1000000,
|
||||
"description": "Stumbler: Volume measurement of bytes uploaded, normalized to per-second."
|
||||
},
|
||||
"STUMBLER_TIME_BETWEEN_START_SEC": {
|
||||
"expires_in_version": "45",
|
||||
"kind": "exponential",
|
||||
"n_buckets": 50,
|
||||
"high": 259200,
|
||||
"description": "Stumbler: The time between the service starts."
|
||||
},
|
||||
"STUMBLER_UPLOAD_BYTES": {
|
||||
"expires_in_version": "45",
|
||||
"kind": "exponential",
|
||||
"n_buckets": 50,
|
||||
"high": 1000000,
|
||||
"description": "Stumbler: The bytes per upload."
|
||||
},
|
||||
"STUMBLER_UPLOAD_OBSERVATION_COUNT": {
|
||||
"expires_in_version": "45",
|
||||
"kind": "exponential",
|
||||
"n_buckets": 50,
|
||||
"high": 10000,
|
||||
"description": "Stumbler: The observations per upload."
|
||||
},
|
||||
"STUMBLER_UPLOAD_CELL_COUNT": {
|
||||
"expires_in_version": "45",
|
||||
"kind": "exponential",
|
||||
"n_buckets": 50,
|
||||
"high": 10000,
|
||||
"description": "Stumbler: The cells per upload."
|
||||
},
|
||||
"STUMBLER_UPLOAD_WIFI_AP_COUNT": {
|
||||
"expires_in_version": "45",
|
||||
"kind": "exponential",
|
||||
"n_buckets": 50,
|
||||
"high": 10000,
|
||||
"description": "Stumbler: The Wi-Fi APs per upload."
|
||||
},
|
||||
"STUMBLER_OBSERVATIONS_PER_DAY": {
|
||||
"expires_in_version": "45",
|
||||
"kind": "exponential",
|
||||
"n_buckets": 50,
|
||||
"high": 10000,
|
||||
"description": "Stumbler: The number of observations between upload events, normalized to per day."
|
||||
},
|
||||
"STUMBLER_TIME_BETWEEN_RECEIVED_LOCATIONS_SEC": {
|
||||
"expires_in_version": "45",
|
||||
"kind": "exponential",
|
||||
"n_buckets": 50,
|
||||
"high": 86400,
|
||||
"description": "Stumbler: The time between receiving passive locations."
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
var { Ci, Cu, Cc, components } = require("chrome");
|
||||
var Services = require("Services");
|
||||
var promise = require("promise");
|
||||
var { setTimeout } = require("Timer");
|
||||
|
||||
/**
|
||||
* Turn the error |aError| into a string, without fail.
|
||||
@ -121,7 +120,7 @@ exports.zip = function zip(a, b) {
|
||||
*/
|
||||
exports.executeSoon = function executeSoon(aFn) {
|
||||
if (isWorker) {
|
||||
setTimeout(aFn, 0);
|
||||
require("Timer").setTimeout(aFn, 0);
|
||||
} else {
|
||||
Services.tm.mainThread.dispatch({
|
||||
run: exports.makeInfallible(aFn)
|
||||
@ -151,7 +150,7 @@ exports.waitForTick = function waitForTick() {
|
||||
*/
|
||||
exports.waitForTime = function waitForTime(aDelay) {
|
||||
let deferred = promise.defer();
|
||||
setTimeout(deferred.resolve, aDelay);
|
||||
require("Timer").setTimeout(deferred.resolve, aDelay);
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
|
@ -10,25 +10,12 @@
|
||||
|
||||
let { Constructor: CC, classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
// addDebuggerToGlobal only allows adding the Debugger object to a global. The
|
||||
// this object is not guaranteed to be a global (in particular on B2G, due to
|
||||
// compartment sharing), so add the Debugger object to a sandbox instead.
|
||||
let sandbox = Cu.Sandbox(CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')());
|
||||
Cu.evalInSandbox(
|
||||
"Components.utils.import('resource://gre/modules/jsdebugger.jsm');" +
|
||||
"addDebuggerToGlobal(this);",
|
||||
sandbox
|
||||
);
|
||||
let Debugger = sandbox.Debugger;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "console", "resource://gre/modules/devtools/Console.jsm");
|
||||
|
||||
let xpcInspector = Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
|
||||
|
||||
let loader = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {}).Loader;
|
||||
let promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
|
||||
@ -40,23 +27,43 @@ this.EXPORTED_SYMBOLS = ["DevToolsLoader", "devtools", "BuiltinProvider",
|
||||
* Providers are different strategies for loading the devtools.
|
||||
*/
|
||||
|
||||
let Timer = Cu.import("resource://gre/modules/Timer.jsm", {});
|
||||
|
||||
let loaderModules = {
|
||||
"Debugger": Debugger,
|
||||
"Services": Object.create(Services),
|
||||
"Timer": Object.create(Timer),
|
||||
"toolkit/loader": loader,
|
||||
"xpcInspector": xpcInspector,
|
||||
"promise": promise,
|
||||
"PromiseDebugging": PromiseDebugging
|
||||
};
|
||||
try {
|
||||
let { indexedDB } = Cu.Sandbox(this, {wantGlobalProperties:["indexedDB"]});
|
||||
loaderModules.indexedDB = indexedDB;
|
||||
} catch(e) {
|
||||
XPCOMUtils.defineLazyGetter(loaderModules, "Debugger", () => {
|
||||
// addDebuggerToGlobal only allows adding the Debugger object to a global. The
|
||||
// this object is not guaranteed to be a global (in particular on B2G, due to
|
||||
// compartment sharing), so add the Debugger object to a sandbox instead.
|
||||
let sandbox = Cu.Sandbox(CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')());
|
||||
Cu.evalInSandbox(
|
||||
"Components.utils.import('resource://gre/modules/jsdebugger.jsm');" +
|
||||
"addDebuggerToGlobal(this);",
|
||||
sandbox
|
||||
);
|
||||
return sandbox.Debugger;
|
||||
});
|
||||
XPCOMUtils.defineLazyGetter(loaderModules, "Timer", () => {
|
||||
let {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
|
||||
// Do not return Cu.import result, as SDK loader would freeze Timer.jsm globals...
|
||||
return {
|
||||
setTimeout,
|
||||
clearTimeout
|
||||
};
|
||||
});
|
||||
XPCOMUtils.defineLazyGetter(loaderModules, "xpcInspector", () => {
|
||||
return Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector);
|
||||
});
|
||||
XPCOMUtils.defineLazyGetter(loaderModules, "indexedDB", () => {
|
||||
// On xpcshell, we can't instantiate indexedDB without crashing
|
||||
}
|
||||
try {
|
||||
return Cu.Sandbox(this, {wantGlobalProperties:["indexedDB"]}).indexedDB;
|
||||
} catch(e) {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
|
||||
let sharedGlobalBlacklist = ["sdk/indexed-db"];
|
||||
|
||||
@ -356,7 +363,6 @@ DevToolsLoader.prototype = {
|
||||
isWorker: false,
|
||||
reportError: Cu.reportError,
|
||||
btoa: btoa,
|
||||
console: console,
|
||||
_Iterator: Iterator,
|
||||
loader: {
|
||||
lazyGetter: this.lazyGetter,
|
||||
@ -365,6 +371,10 @@ DevToolsLoader.prototype = {
|
||||
lazyRequireGetter: this.lazyRequireGetter
|
||||
},
|
||||
};
|
||||
// Lazy define console in order to load Console.jsm only when it is used
|
||||
XPCOMUtils.defineLazyGetter(this._provider.globals, "console", () => {
|
||||
return Cu.import("resource://gre/modules/devtools/Console.jsm", {}).console;
|
||||
});
|
||||
|
||||
this._provider.load();
|
||||
this.require = loader.Require(this._provider.loader, { id: "devtools" });
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -32,6 +32,23 @@
|
||||
c(node, state);
|
||||
};
|
||||
|
||||
// An ancestor walk builds up an array of ancestor nodes (including
|
||||
// the current node) and passes them to the callback as the state parameter.
|
||||
exports.ancestor = function(node, visitors, base, state) {
|
||||
if (!base) base = exports.base;
|
||||
if (!state) state = [];
|
||||
function c(node, st, override) {
|
||||
var type = override || node.type, found = visitors[type];
|
||||
if (node != st[st.length - 1]) {
|
||||
st = st.slice();
|
||||
st.push(node);
|
||||
}
|
||||
base[type](node, st, c);
|
||||
if (found) found(node, st);
|
||||
}
|
||||
c(node, state);
|
||||
};
|
||||
|
||||
// A recursive walk is one where your functions override the default
|
||||
// walkers. They can modify and replace the state parameter that's
|
||||
// threaded through the walk, and can opt how and whether to walk
|
||||
@ -180,10 +197,10 @@
|
||||
c(cs.consequent[j], st, "Statement");
|
||||
}
|
||||
};
|
||||
base.ReturnStatement = function(node, st, c) {
|
||||
base.ReturnStatement = base.YieldExpression = function(node, st, c) {
|
||||
if (node.argument) c(node.argument, st, "Expression");
|
||||
};
|
||||
base.ThrowStatement = function(node, st, c) {
|
||||
base.ThrowStatement = base.SpreadElement = function(node, st, c) {
|
||||
c(node.argument, st, "Expression");
|
||||
};
|
||||
base.TryStatement = function(node, st, c) {
|
||||
@ -202,7 +219,7 @@
|
||||
if (node.update) c(node.update, st, "Expression");
|
||||
c(node.body, st, "Statement");
|
||||
};
|
||||
base.ForInStatement = function(node, st, c) {
|
||||
base.ForInStatement = base.ForOfStatement = function(node, st, c) {
|
||||
c(node.left, st, "ForInit");
|
||||
c(node.right, st, "Expression");
|
||||
c(node.body, st, "Statement");
|
||||
@ -240,10 +257,10 @@
|
||||
};
|
||||
base.ObjectExpression = function(node, st, c) {
|
||||
for (var i = 0; i < node.properties.length; ++i)
|
||||
c(node.properties[i].value, st, "Expression");
|
||||
c(node.properties[i], st);
|
||||
};
|
||||
base.FunctionExpression = base.FunctionDeclaration;
|
||||
base.SequenceExpression = function(node, st, c) {
|
||||
base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
|
||||
base.SequenceExpression = base.TemplateLiteral = function(node, st, c) {
|
||||
for (var i = 0; i < node.expressions.length; ++i)
|
||||
c(node.expressions[i], st, "Expression");
|
||||
};
|
||||
@ -268,7 +285,28 @@
|
||||
c(node.object, st, "Expression");
|
||||
if (node.computed) c(node.property, st, "Expression");
|
||||
};
|
||||
base.Identifier = base.Literal = ignore;
|
||||
base.Identifier = base.Literal = base.ExportDeclaration = base.ImportDeclaration = ignore;
|
||||
|
||||
base.TaggedTemplateExpression = function(node, st, c) {
|
||||
c(node.tag, st, "Expression");
|
||||
c(node.quasi, st);
|
||||
};
|
||||
base.ClassDeclaration = base.ClassExpression = function(node, st, c) {
|
||||
if (node.superClass) c(node.superClass, st, "Expression");
|
||||
for (var i = 0; i < node.body.body.length; i++)
|
||||
c(node.body.body[i], st);
|
||||
};
|
||||
base.MethodDefinition = base.Property = function(node, st, c) {
|
||||
if (node.computed) c(node.key, st, "Expression");
|
||||
c(node.value, st, "Expression");
|
||||
};
|
||||
base.ComprehensionExpression = function(node, st, c) {
|
||||
for (var i = 0; i < node.blocks.length; i++)
|
||||
c(node.blocks[i].right, st, "Expression");
|
||||
c(node.body, st, "Expression");
|
||||
};
|
||||
|
||||
// NOTE: the stuff below is deprecated, and will be removed when 1.0 is released
|
||||
|
||||
// A custom walker that keeps track of the scope chain and the
|
||||
// variables defined in it.
|
||||
|
@ -5,9 +5,11 @@
|
||||
* http://opensource.org/licenses/BSD-2-Clause
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
"use strict";
|
||||
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define(factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
} else if (typeof exports === "object") {
|
||||
module.exports = factory();
|
||||
} else {
|
||||
root.prettyFast = factory();
|
||||
@ -92,7 +94,9 @@
|
||||
if (lastToken.type.isAssign) {
|
||||
return true;
|
||||
}
|
||||
return !!PRE_ARRAY_LITERAL_TOKENS[lastToken.type.keyword || lastToken.type.type];
|
||||
return !!PRE_ARRAY_LITERAL_TOKENS[
|
||||
lastToken.type.keyword || lastToken.type.type
|
||||
];
|
||||
}
|
||||
|
||||
// If any of these tokens are followed by a token on a new line, we know that
|
||||
@ -209,7 +213,9 @@
|
||||
if (token.startLoc.line === lastToken.startLoc.line) {
|
||||
return false;
|
||||
}
|
||||
if (PREVENT_ASI_AFTER_TOKENS[lastToken.type.type || lastToken.type.keyword]) {
|
||||
if (PREVENT_ASI_AFTER_TOKENS[
|
||||
lastToken.type.type || lastToken.type.keyword
|
||||
]) {
|
||||
return false;
|
||||
}
|
||||
if (PREVENT_ASI_BEFORE_TOKENS[token.type.type || token.type.keyword]) {
|
||||
@ -490,8 +496,8 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that we output the escaped character combination inside string literals
|
||||
* instead of various problematic characters.
|
||||
* Make sure that we output the escaped character combination inside string
|
||||
* literals instead of various problematic characters.
|
||||
*/
|
||||
var sanitize = (function () {
|
||||
var escapeCharacters = {
|
||||
@ -520,11 +526,11 @@
|
||||
+ ")";
|
||||
var escapeCharactersRegExp = new RegExp(regExpString, "g");
|
||||
|
||||
return function(str) {
|
||||
return function (str) {
|
||||
return str.replace(escapeCharactersRegExp, function (_, c) {
|
||||
return escapeCharacters[c];
|
||||
});
|
||||
}
|
||||
};
|
||||
}());
|
||||
/**
|
||||
* Add the given token to the pretty printed results.
|
||||
@ -533,14 +539,16 @@
|
||||
* The token to add.
|
||||
* @param Function write
|
||||
* The function to write pretty printed code to the result SourceNode.
|
||||
* @param Object options
|
||||
* The options object.
|
||||
*/
|
||||
function addToken(token, write, options) {
|
||||
function addToken(token, write) {
|
||||
if (token.type.type == "string") {
|
||||
write("'" + sanitize(token.value) + "'",
|
||||
token.startLoc.line,
|
||||
token.startLoc.column);
|
||||
} else if (token.type.type == "regexp") {
|
||||
write(String(token.value.value),
|
||||
token.startLoc.line,
|
||||
token.startLoc.column);
|
||||
} else {
|
||||
write(String(token.value != null ? token.value : token.type.type),
|
||||
token.startLoc.line,
|
||||
@ -584,7 +592,7 @@
|
||||
*/
|
||||
function decrementsIndent(tokenType, stack) {
|
||||
return tokenType == "}"
|
||||
|| (tokenType == "]" && stack[stack.length - 1] == "[\n")
|
||||
|| (tokenType == "]" && stack[stack.length - 1] == "[\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -690,12 +698,13 @@
|
||||
for (var i = 0, len = buffer.length; i < len; i++) {
|
||||
lineStr += buffer[i];
|
||||
}
|
||||
result.add(new SourceNode(bufferLine, bufferColumn, options.url, lineStr));
|
||||
result.add(new SourceNode(bufferLine, bufferColumn, options.url,
|
||||
lineStr));
|
||||
buffer.splice(0, buffer.length);
|
||||
bufferLine = -1;
|
||||
bufferColumn = -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
||||
// Whether or not we added a newline on after we added the last token.
|
||||
@ -773,7 +782,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
while (true) {
|
||||
for (;;) {
|
||||
token = getToken();
|
||||
|
||||
ttk = token.type.keyword;
|
||||
@ -807,8 +816,8 @@
|
||||
|
||||
prependWhiteSpace(token, lastToken, addedNewline, write, options,
|
||||
indentLevel, stack);
|
||||
addToken(token, write, options);
|
||||
if (commentQueue.length == 0 || !commentQueue[0].trailing) {
|
||||
addToken(token, write);
|
||||
if (commentQueue.length === 0 || !commentQueue[0].trailing) {
|
||||
addedNewline = appendNewline(token, write, stack);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
|
||||
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -10,15 +10,11 @@ const { Cc, Ci, Cu } = require("chrome");
|
||||
const Services = require("Services");
|
||||
const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/server/actors/common");
|
||||
const { DebuggerServer } = require("devtools/server/main");
|
||||
const { dumpProtocolSpec } = require("devtools/server/protocol");
|
||||
const makeDebugger = require("./utils/make-debugger");
|
||||
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
|
||||
DevToolsUtils.defineLazyGetter(this, "StyleSheetActor", () => {
|
||||
return require("devtools/server/actors/stylesheets").StyleSheetActor;
|
||||
});
|
||||
loader.lazyRequireGetter(this, "StyleSheetActor", "devtools/server/actors/stylesheets", true);
|
||||
|
||||
DevToolsUtils.defineLazyGetter(this, "ppmm", () => {
|
||||
loader.lazyGetter(this, "ppmm", () => {
|
||||
return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIMessageBroadcaster);
|
||||
});
|
||||
|
||||
@ -402,7 +398,9 @@ RootActor.prototype = {
|
||||
return Cu.cloneInto(aRequest, {});
|
||||
},
|
||||
|
||||
onProtocolDescription: dumpProtocolSpec,
|
||||
onProtocolDescription: function () {
|
||||
return require("devtools/server/protocol").dumpProtocolSpec();
|
||||
},
|
||||
|
||||
/* Support for DebuggerServer.addGlobalActor. */
|
||||
_createExtraActors: createExtraActors,
|
||||
|
@ -13,16 +13,22 @@ const { DebuggerServer } = require("devtools/server/main");
|
||||
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
const { dbg_assert, dumpn, update, fetch } = DevToolsUtils;
|
||||
const { dirname, joinURI } = require("devtools/toolkit/path");
|
||||
const { SourceMapConsumer, SourceMapGenerator } = require("source-map");
|
||||
const promise = require("promise");
|
||||
const PromiseDebugging = require("PromiseDebugging");
|
||||
const Debugger = require("Debugger");
|
||||
const xpcInspector = require("xpcInspector");
|
||||
const mapURIToAddonID = require("./utils/map-uri-to-addon-id");
|
||||
const ScriptStore = require("./utils/ScriptStore");
|
||||
|
||||
const { defer, resolve, reject, all } = require("devtools/toolkit/deprecated-sync-thenables");
|
||||
const { CssLogic } = require("devtools/styleinspector/css-logic");
|
||||
|
||||
loader.lazyGetter(this, "Debugger", () => {
|
||||
let Debugger = require("Debugger");
|
||||
hackDebugger(Debugger);
|
||||
return Debugger;
|
||||
});
|
||||
loader.lazyRequireGetter(this, "SourceMapConsumer", "source-map", true);
|
||||
loader.lazyRequireGetter(this, "SourceMapGenerator", "source-map", true);
|
||||
loader.lazyRequireGetter(this, "CssLogic", "devtools/styleinspector/css-logic", true);
|
||||
|
||||
let TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
|
||||
"Uint32Array", "Int8Array", "Int16Array", "Int32Array", "Float32Array",
|
||||
@ -44,18 +50,18 @@ let OBJECT_PREVIEW_MAX_ITEMS = 10;
|
||||
* - { state: "fulfilled", value }
|
||||
* - { state: "rejected", reason }
|
||||
*/
|
||||
Debugger.Object.prototype.getPromiseState = function () {
|
||||
if (this.class != "Promise") {
|
||||
function getPromiseState(obj) {
|
||||
if (obj.class != "Promise") {
|
||||
throw new Error(
|
||||
"Can't call `getPromiseState` on `Debugger.Object`s that don't " +
|
||||
"refer to Promise objects.");
|
||||
}
|
||||
|
||||
const state = PromiseDebugging.getState(this.unsafeDereference());
|
||||
const state = PromiseDebugging.getState(obj.unsafeDereference());
|
||||
return {
|
||||
state: state.state,
|
||||
value: this.makeDebuggeeValue(state.value),
|
||||
reason: this.makeDebuggeeValue(state.reason)
|
||||
value: obj.makeDebuggeeValue(state.value),
|
||||
reason: obj.makeDebuggeeValue(state.reason)
|
||||
};
|
||||
};
|
||||
|
||||
@ -1191,7 +1197,7 @@ ThreadActor.prototype = {
|
||||
*/
|
||||
_breakOnEnter: function(script) {
|
||||
let offsets = script.getAllOffsets();
|
||||
let sourceActor = this.sources.source({ source: script.source });
|
||||
let sourceActor = this.sources.createNonSourceMappedActor(script.source);
|
||||
|
||||
for (let line = 0, n = offsets.length; line < n; line++) {
|
||||
if (offsets[line]) {
|
||||
@ -1342,7 +1348,7 @@ ThreadActor.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
return all([this.sources.sourcesForScript(script)
|
||||
return all([this.sources.createSourceActors(script.source)
|
||||
for (script of sourcesToScripts.values())]);
|
||||
},
|
||||
|
||||
@ -1957,8 +1963,6 @@ ThreadActor.prototype = {
|
||||
* A Debugger.Object instance whose referent is the global object.
|
||||
*/
|
||||
onNewScript: function (aScript, aGlobal) {
|
||||
this.sources.sourcesForScript(aScript);
|
||||
|
||||
// XXX: The scripts must be added to the ScriptStore before restoring
|
||||
// breakpoints in _addScript. If we try to add them to the ScriptStore
|
||||
// inside _addScript, we can accidentally set a breakpoint in a top level
|
||||
@ -2023,9 +2027,8 @@ ThreadActor.prototype = {
|
||||
}
|
||||
|
||||
// Set any stored breakpoints.
|
||||
|
||||
let endLine = aScript.startLine + aScript.lineCount - 1;
|
||||
let source = this.sources.source({ source: aScript.source });
|
||||
let source = this.sources.createNonSourceMappedActor(aScript.source);
|
||||
for (let bpActor of this.breakpointActorMap.findActors({ source: source.form() })) {
|
||||
// Limit the search to the line numbers contained in the new script.
|
||||
if (bpActor.location.line >= aScript.startLine
|
||||
@ -2034,6 +2037,11 @@ ThreadActor.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
// Go ahead and establish the source actors for this script, which
|
||||
// fetches sourcemaps if available and sends onNewSource
|
||||
// notifications
|
||||
this.sources.createSourceActors(aScript.source);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -3245,7 +3253,7 @@ let stringifiers = {
|
||||
'nsresult: "0x' + result + ' (' + name + ')"]';
|
||||
},
|
||||
Promise: obj => {
|
||||
const { state, value, reason } = obj.getPromiseState();
|
||||
const { state, value, reason } = getPromiseState(obj);
|
||||
let statePreview = state;
|
||||
if (state != "pending") {
|
||||
const settledValue = state === "fulfilled" ? value : reason;
|
||||
@ -3293,7 +3301,7 @@ ObjectActor.prototype = {
|
||||
if (this.obj.class != "DeadObject") {
|
||||
// Expose internal Promise state.
|
||||
if (this.obj.class == "Promise") {
|
||||
const { state, value, reason } = this.obj.getPromiseState();
|
||||
const { state, value, reason } = getPromiseState(this.obj);
|
||||
g.promiseState = { state };
|
||||
if (state == "fulfilled") {
|
||||
g.promiseState.value = this.threadActor.createValueGrip(value);
|
||||
@ -4979,44 +4987,54 @@ EnvironmentActor.prototype.requestTypes = {
|
||||
|
||||
exports.EnvironmentActor = EnvironmentActor;
|
||||
|
||||
/**
|
||||
* Override the toString method in order to get more meaningful script output
|
||||
* for debugging the debugger.
|
||||
*/
|
||||
Debugger.Script.prototype.toString = function() {
|
||||
let output = "";
|
||||
if (this.url) {
|
||||
output += this.url;
|
||||
}
|
||||
if (typeof this.staticLevel != "undefined") {
|
||||
output += ":L" + this.staticLevel;
|
||||
}
|
||||
if (typeof this.startLine != "undefined") {
|
||||
output += ":" + this.startLine;
|
||||
if (this.lineCount && this.lineCount > 1) {
|
||||
output += "-" + (this.startLine + this.lineCount - 1);
|
||||
}
|
||||
}
|
||||
if (this.strictMode) {
|
||||
output += ":strict";
|
||||
}
|
||||
return output;
|
||||
};
|
||||
function hackDebugger(Debugger) {
|
||||
// TODO: Improve native code instead of hacking on top of it
|
||||
|
||||
/**
|
||||
* Helper property for quickly getting to the line number a stack frame is
|
||||
* currently paused at.
|
||||
*/
|
||||
Object.defineProperty(Debugger.Frame.prototype, "line", {
|
||||
configurable: true,
|
||||
get: function() {
|
||||
if (this.script) {
|
||||
return this.script.getOffsetLine(this.offset);
|
||||
} else {
|
||||
return null;
|
||||
/**
|
||||
* Override the toString method in order to get more meaningful script output
|
||||
* for debugging the debugger.
|
||||
*/
|
||||
Debugger.Script.prototype.toString = function() {
|
||||
let output = "";
|
||||
if (this.url) {
|
||||
output += this.url;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (typeof this.staticLevel != "undefined") {
|
||||
output += ":L" + this.staticLevel;
|
||||
}
|
||||
if (typeof this.startLine != "undefined") {
|
||||
output += ":" + this.startLine;
|
||||
if (this.lineCount && this.lineCount > 1) {
|
||||
output += "-" + (this.startLine + this.lineCount - 1);
|
||||
}
|
||||
}
|
||||
if (typeof this.startLine != "undefined") {
|
||||
output += ":" + this.startLine;
|
||||
if (this.lineCount && this.lineCount > 1) {
|
||||
output += "-" + (this.startLine + this.lineCount - 1);
|
||||
}
|
||||
}
|
||||
if (this.strictMode) {
|
||||
output += ":strict";
|
||||
}
|
||||
return output;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper property for quickly getting to the line number a stack frame is
|
||||
* currently paused at.
|
||||
*/
|
||||
Object.defineProperty(Debugger.Frame.prototype, "line", {
|
||||
configurable: true,
|
||||
get: function() {
|
||||
if (this.script) {
|
||||
return this.script.getOffsetLine(this.offset);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -5107,7 +5125,10 @@ function ThreadSources(aThreadActor, aOptions, aAllowPredicate,
|
||||
this._useSourceMaps = aOptions.useSourceMaps;
|
||||
this._autoBlackBox = aOptions.autoBlackBox;
|
||||
this._allow = aAllowPredicate;
|
||||
this._onNewSource = aOnNewSource;
|
||||
this._onNewSource = DevToolsUtils.makeInfallible(
|
||||
aOnNewSource,
|
||||
"ThreadSources.prototype._onNewSource"
|
||||
);
|
||||
this._anonSourceMapId = 1;
|
||||
|
||||
// generated Debugger.Source -> promise of SourceMapConsumer
|
||||
@ -5222,21 +5243,35 @@ ThreadSources.prototype = {
|
||||
this._sourceMappedSourceActors[originalUrl] = actor;
|
||||
}
|
||||
|
||||
// Don't notify a new source if it's a generated one, as it has
|
||||
// sourcemapped sources. The generated one is created to set
|
||||
// breakpoints.
|
||||
if (!source || !this._sourceMaps.has(source)) {
|
||||
try {
|
||||
this._onNewSource(actor);
|
||||
} catch (e) {
|
||||
reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
this._emitNewSource(actor);
|
||||
return actor;
|
||||
},
|
||||
|
||||
getSource: function(source) {
|
||||
_emitNewSource: function(actor) {
|
||||
if(!actor.source) {
|
||||
// Always notify if we don't have a source because that means
|
||||
// it's something that has been sourcemapped, or it represents
|
||||
// the HTML file that contains inline sources.
|
||||
this._onNewSource(actor);
|
||||
}
|
||||
else {
|
||||
// If sourcemapping is enabled and a source has sourcemaps, we
|
||||
// create `SourceActor` instances for both the original and
|
||||
// generated sources. The source actors for the generated
|
||||
// sources are only for internal use, however; breakpoints are
|
||||
// managed by these internal actors. We only want to notify the
|
||||
// user of the original sources though, so if the actor has a
|
||||
// `Debugger.Source` instance and a valid source map (meaning
|
||||
// it's a generated source), don't send the notification.
|
||||
this.fetchSourceMap(actor.source).then(map => {
|
||||
if(!map) {
|
||||
this._onNewSource(actor);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
getSourceActor: function(source) {
|
||||
if (source.url in this._sourceMappedSourceActors) {
|
||||
return this._sourceMappedSourceActors[source.url];
|
||||
}
|
||||
@ -5249,7 +5284,7 @@ ThreadSources.prototype = {
|
||||
(source.url || 'source'));
|
||||
},
|
||||
|
||||
getSourceByURL: function(url) {
|
||||
getSourceActorByURL: function(url) {
|
||||
if (url) {
|
||||
for (let [source, actor] of this._sourceActors) {
|
||||
if (source.url === url) {
|
||||
@ -5286,15 +5321,23 @@ ThreadSources.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Only to be used when we aren't source mapping.
|
||||
* Create a source actor representing this source. This ignores
|
||||
* source mapping and always returns an actor representing this real
|
||||
* source. Use `createSourceActors` if you want to respect source maps.
|
||||
*
|
||||
* @param Debugger.Source aSource
|
||||
* The source instance to create an actor for.
|
||||
* @returns SourceActor
|
||||
*/
|
||||
_sourceForScript: function (aScript) {
|
||||
createNonSourceMappedActor: function (aSource) {
|
||||
// Don't use getSourceURL because we don't want to consider the
|
||||
// displayURL property if it's an eval source
|
||||
let url = isEvalSource(aScript.source) ? null : aScript.source.url;
|
||||
let spec = {
|
||||
source: aScript.source
|
||||
};
|
||||
// displayURL property if it's an eval source. We only want to
|
||||
// consider real URLs, otherwise if there is a URL but it's
|
||||
// invalid the code below will not set the content type, and we
|
||||
// will later try to fetch the contents of the URL to figure out
|
||||
// the content type, but it's a made up URL for eval sources.
|
||||
let url = isEvalSource(aSource) ? null : aSource.url;
|
||||
let spec = { source: aSource };
|
||||
|
||||
// XXX bug 915433: We can't rely on Debugger.Source.prototype.text
|
||||
// if the source is an HTML-embedded <script> tag. Since we don't
|
||||
@ -5324,31 +5367,48 @@ ThreadSources.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a promise of an array of source actors representing all the
|
||||
* sources of |aScript|.
|
||||
* This is an internal function that returns a promise of an array
|
||||
* of source actors representing all the source mapped sources of
|
||||
* `aSource`, or `null` if the source is not sourcemapped or
|
||||
* sourcemapping is disabled. Users should call `createSourceActors`
|
||||
* instead of this.
|
||||
*
|
||||
* If source map handling is enabled and |aScript| has a source map, then
|
||||
* use it to find all of |aScript|'s *original* sources; return a promise
|
||||
* of an array of source actors for those.
|
||||
* @param Debugger.Source aSource
|
||||
* The source instance to create actors for.
|
||||
* @return Promise of an array of source actors
|
||||
*/
|
||||
sourcesForScript: function (aScript) {
|
||||
if (!this._useSourceMaps || !aScript.source.sourceMapURL) {
|
||||
return resolve([this._sourceForScript(aScript)].filter(isNotNull));
|
||||
_createSourceMappedActors: function (aSource) {
|
||||
if (!this._useSourceMaps || !aSource.sourceMapURL) {
|
||||
return resolve(null);
|
||||
}
|
||||
|
||||
return this.fetchSourceMap(aScript.source)
|
||||
return this.fetchSourceMap(aSource)
|
||||
.then(map => {
|
||||
if (map) {
|
||||
return [
|
||||
this.source({ originalUrl: s,
|
||||
generatedSource: aScript.source })
|
||||
this.source({ originalUrl: s, generatedSource: aSource })
|
||||
for (s of map.sources)
|
||||
];
|
||||
].filter(isNotNull);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
},
|
||||
|
||||
return [this._sourceForScript(aScript)];
|
||||
})
|
||||
.then(ss => ss.filter(isNotNull));
|
||||
/**
|
||||
* Creates the source actors representing the appropriate sources
|
||||
* of `aSource`. If sourcemapped, returns actors for all of the original
|
||||
* sources, otherwise returns a 1-element array with the actor for
|
||||
* `aSource`.
|
||||
*
|
||||
* @param Debugger.Source aSource
|
||||
* The source instance to create actors for.
|
||||
* @param Promise of an array of source actors
|
||||
*/
|
||||
createSourceActors: function(aSource) {
|
||||
return this._createSourceMappedActors(aSource).then(actors => {
|
||||
let actor = this.createNonSourceMappedActor(aSource);
|
||||
return (actors || [actor]).filter(isNotNull);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -5356,6 +5416,10 @@ ThreadSources.prototype = {
|
||||
* `aSource`; if we already have such a promise extant, return that.
|
||||
* This will fetch the source map if we don't have a cached object
|
||||
* and source maps are enabled (see `_fetchSourceMap`).
|
||||
*
|
||||
* @param Debugger.Source aSource
|
||||
* The source instance to get sourcemaps for.
|
||||
* @return Promise of a SourceMapConsumer
|
||||
*/
|
||||
fetchSourceMap: function (aSource) {
|
||||
if (this._sourceMaps.has(aSource)) {
|
||||
@ -5369,13 +5433,12 @@ ThreadSources.prototype = {
|
||||
if (aSource.url) {
|
||||
sourceMapURL = this._normalize(sourceMapURL, aSource.url);
|
||||
}
|
||||
let result = this._fetchSourceMap(sourceMapURL, aSource.url);
|
||||
|
||||
let map = this._fetchSourceMap(sourceMapURL, aSource.url);
|
||||
if (map) {
|
||||
this._sourceMaps.set(aSource, map);
|
||||
return map;
|
||||
}
|
||||
return resolve(null);
|
||||
// The promises in `_sourceMaps` must be the exact same instances
|
||||
// as returned by `_fetchSourceMap` for `clearSourceMapCache` to work.
|
||||
this._sourceMaps.set(aSource, result);
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -5413,7 +5476,7 @@ ThreadSources.prototype = {
|
||||
return this._sourceMapCache[aAbsSourceMapURL];
|
||||
}
|
||||
else if (!this._useSourceMaps) {
|
||||
return null;
|
||||
return resolve(null);
|
||||
}
|
||||
|
||||
let fetching = fetch(aAbsSourceMapURL, { loadFromCache: false })
|
||||
@ -5424,7 +5487,7 @@ ThreadSources.prototype = {
|
||||
})
|
||||
.then(null, error => {
|
||||
if (!DevToolsUtils.reportingDisabled) {
|
||||
DevToolsUtils.reportException("ThreadSources.prototype.getOriginalLocation", error);
|
||||
DevToolsUtils.reportException("ThreadSources.prototype._fetchSourceMap", error);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
@ -5517,7 +5580,10 @@ ThreadSources.prototype = {
|
||||
|
||||
/**
|
||||
* Returns a promise of the location in the original source if the source is
|
||||
* source mapped, otherwise a promise of the same location.
|
||||
* source mapped, otherwise a promise of the same location. This can
|
||||
* be called with a source from *any* Debugger instance and we make
|
||||
* sure to that it works properly, reusing source maps if already
|
||||
* fetched. Use this from any actor that needs sourcemapping.
|
||||
*/
|
||||
getOriginalLocation: function ({ source, line, column }) {
|
||||
// In certain scenarios the source map may have not been fetched
|
||||
@ -5538,7 +5604,17 @@ ThreadSources.prototype = {
|
||||
});
|
||||
|
||||
return {
|
||||
sourceActor: sourceUrl && this.source({ originalUrl: sourceUrl }),
|
||||
// Since the `Debugger.Source` instance may come from a
|
||||
// different `Debugger` instance (any actor can call this
|
||||
// method), we can't rely on any of the source discovery
|
||||
// setup (`_discoverSources`, etc) to have been run yet. So
|
||||
// we have to assume that the actor may not already exist,
|
||||
// and we might need to create it, so use `source` and give
|
||||
// it the required parameters for a sourcemapped source.
|
||||
sourceActor: (!sourceUrl) ? null : this.source({
|
||||
originalUrl: sourceUrl,
|
||||
generatedSource: source
|
||||
}),
|
||||
url: sourceUrl,
|
||||
line: sourceLine,
|
||||
column: sourceCol,
|
||||
@ -5548,9 +5624,7 @@ ThreadSources.prototype = {
|
||||
|
||||
// No source map
|
||||
return resolve({
|
||||
// Don't use `getSource` because sources may have not been
|
||||
// created yet
|
||||
sourceActor: this.source({ source }),
|
||||
sourceActor: this.createNonSourceMappedActor(source),
|
||||
url: source.url,
|
||||
line: line,
|
||||
column: column
|
||||
@ -5584,9 +5658,7 @@ ThreadSources.prototype = {
|
||||
});
|
||||
|
||||
return {
|
||||
// Don't use `getSource` because this could intentionally
|
||||
// create a generated source
|
||||
sourceActor: this.source({ source: source }),
|
||||
sourceActor: this.createNonSourceMappedActor(source),
|
||||
line: genLine,
|
||||
column: genColumn
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* web-audio-automation-timeline - 1.0.2
|
||||
* web-audio-automation-timeline - 1.0.3
|
||||
* https://github.com/jsantell/web-audio-automation-timeline
|
||||
* MIT License, copyright (c) 2014 Jordan Santell
|
||||
*/
|
||||
@ -68,6 +68,8 @@ exports.fuzzyEqual = function (lhs, rhs) {
|
||||
return Math.abs(lhs - rhs) < EPSILON;
|
||||
};
|
||||
|
||||
exports.EPSILON = EPSILON;
|
||||
|
||||
},{}],4:[function(require,module,exports){
|
||||
var TimelineEvent = require("./event").TimelineEvent;
|
||||
var F = require("./formulas");
|
||||
@ -78,11 +80,6 @@ function Timeline (defaultValue) {
|
||||
this.events = [];
|
||||
|
||||
this._value = defaultValue || 0;
|
||||
|
||||
// This is the value of this AudioParam we computed at the last call.
|
||||
this._computedValue = defaultValue || 0;
|
||||
// This is the value of this AudioParam at the last tick of the previous event.
|
||||
this._lastComputedValue = defaultValue || 0;
|
||||
}
|
||||
|
||||
Timeline.prototype.getEventCount = function () {
|
||||
@ -95,7 +92,7 @@ Timeline.prototype.value = function () {
|
||||
|
||||
Timeline.prototype.setValue = function (value) {
|
||||
if (this.events.length === 0) {
|
||||
this._computedValue = this._lastComputedValue = this._value = value;
|
||||
this._value = value;
|
||||
}
|
||||
};
|
||||
|
||||
@ -108,21 +105,19 @@ Timeline.prototype.getValue = function () {
|
||||
};
|
||||
|
||||
Timeline.prototype.getValueAtTime = function (time) {
|
||||
this._computedValue = this._getValueAtTimeHelper(time);
|
||||
return this._computedValue;
|
||||
return this._getValueAtTimeHelper(time);
|
||||
};
|
||||
|
||||
Timeline.prototype._getValueAtTimeHelper = function (time) {
|
||||
var bailOut = false;
|
||||
var previous = null;
|
||||
var next = null;
|
||||
var lastComputedValue = null; // Used for `setTargetAtTime` nodes
|
||||
var events = this.events;
|
||||
var e;
|
||||
|
||||
for (var i = 0; !bailOut && i < events.length; i++) {
|
||||
if (F.fuzzyEqual(time, events[i].time)) {
|
||||
this._lastComputedValue = this._computedValue;
|
||||
|
||||
// Find the last event with the same time as `time`
|
||||
do {
|
||||
++i;
|
||||
@ -132,7 +127,8 @@ Timeline.prototype._getValueAtTimeHelper = function (time) {
|
||||
|
||||
// `setTargetAtTime` can be handled no matter what their next event is (if they have one)
|
||||
if (e.type === "setTargetAtTime") {
|
||||
return e.exponentialApproach(this._lastComputedValue, time);
|
||||
lastComputedValue = this._lastComputedValue(e);
|
||||
return e.exponentialApproach(lastComputedValue, time);
|
||||
}
|
||||
|
||||
// `setValueCurveAtTime` events can be handled no matter what their next event node is
|
||||
@ -170,7 +166,8 @@ Timeline.prototype._getValueAtTimeHelper = function (time) {
|
||||
|
||||
// `setTargetAtTime` can be handled no matter what their next event is (if they have one)
|
||||
if (previous.type === "setTargetAtTime") {
|
||||
return previous.exponentialApproach(this._lastComputedValue, time);
|
||||
lastComputedValue = this._lastComputedValue(previous);
|
||||
return previous.exponentialApproach(lastComputedValue, time);
|
||||
}
|
||||
|
||||
// `setValueCurveAtTime` events can be handled no matter what their next event node is
|
||||
@ -316,6 +313,29 @@ Timeline.prototype._getPreviousEvent = function (time) {
|
||||
return previous;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates the previous value of the timeline, used for
|
||||
* `setTargetAtTime` nodes. Takes an event, and returns
|
||||
* the previous computed value for any sample taken during that
|
||||
* exponential approach node.
|
||||
*/
|
||||
Timeline.prototype._lastComputedValue = function (event) {
|
||||
// If equal times, return the value for the previous event, before
|
||||
// the `setTargetAtTime` node.
|
||||
var lastEvent = this._getPreviousEvent(event.time - F.EPSILON);
|
||||
|
||||
// If no event before the setTargetAtTime event, then return the
|
||||
// intrinsic value.
|
||||
if (!lastEvent) {
|
||||
return this._value;
|
||||
}
|
||||
// Otherwise, return the value for the previous event, which should
|
||||
// always be the last computed value (? I think?)
|
||||
else {
|
||||
return lastEvent.value;
|
||||
}
|
||||
};
|
||||
|
||||
Timeline.prototype.setValueAtTime = function (value, startTime) {
|
||||
this._insertEvent(new TimelineEvent("setValueAtTime", value, startTime));
|
||||
};
|
||||
|
@ -10,7 +10,6 @@ let { Ci, Cu } = require("chrome");
|
||||
let Services = require("Services");
|
||||
let { ActorPool, createExtraActors, appendExtraActors } = require("devtools/server/actors/common");
|
||||
let { RootActor } = require("devtools/server/actors/root");
|
||||
let { AddonThreadActor, ThreadActor } = require("devtools/server/actors/script");
|
||||
let { DebuggerServer } = require("devtools/server/main");
|
||||
let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
let { dbg_assert } = DevToolsUtils;
|
||||
@ -20,18 +19,16 @@ let mapURIToAddonID = require("./utils/map-uri-to-addon-id");
|
||||
let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
|
||||
loader.lazyRequireGetter(this, "AddonThreadActor", "devtools/server/actors/script", true);
|
||||
loader.lazyRequireGetter(this, "ThreadActor", "devtools/server/actors/script", true);
|
||||
loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
// Assumptions on events module:
|
||||
// events needs to be dispatched synchronously,
|
||||
// by calling the listeners in the order or registration.
|
||||
XPCOMUtils.defineLazyGetter(this, "events", () => {
|
||||
return require("sdk/event/core");
|
||||
});
|
||||
loader.lazyRequireGetter(this, "events", "sdk/event/core");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "StyleSheetActor", () => {
|
||||
return require("devtools/server/actors/stylesheets").StyleSheetActor;
|
||||
});
|
||||
loader.lazyRequireGetter(this, "StyleSheetActor", "devtools/server/actors/stylesheets", true);
|
||||
|
||||
function getWindowID(window) {
|
||||
return window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
|
@ -33,7 +33,6 @@ this.Cc = Cc;
|
||||
this.CC = CC;
|
||||
this.Cu = Cu;
|
||||
this.Cr = Cr;
|
||||
this.Debugger = Debugger;
|
||||
this.Services = Services;
|
||||
this.ActorPool = ActorPool;
|
||||
this.DevToolsUtils = DevToolsUtils;
|
||||
@ -76,7 +75,8 @@ function loadSubScript(aURL)
|
||||
}
|
||||
}
|
||||
|
||||
let events = require("sdk/event/core");
|
||||
loader.lazyRequireGetter(this, "events", "sdk/event/core");
|
||||
|
||||
let {defer, resolve, reject, all} = require("devtools/toolkit/deprecated-sync-thenables");
|
||||
this.defer = defer;
|
||||
this.resolve = resolve;
|
||||
@ -176,7 +176,7 @@ var DebuggerServer = {
|
||||
this._initialized = true;
|
||||
},
|
||||
|
||||
protocol: require("devtools/server/protocol"),
|
||||
get protocol() require("devtools/server/protocol"),
|
||||
|
||||
/**
|
||||
* Initialize the debugger server's transport variables. This can be
|
||||
|
@ -307,11 +307,32 @@ if (typeof Components === "object") {
|
||||
const xpcInspector = Cc["@mozilla.org/jsinspector;1"].
|
||||
getService(Ci.nsIJSInspector);
|
||||
|
||||
this.worker = new WorkerDebuggerLoader({
|
||||
let worker = this.worker = new WorkerDebuggerLoader({
|
||||
createSandbox: createSandbox,
|
||||
globals: {
|
||||
"isWorker": true,
|
||||
"reportError": Cu.reportError,
|
||||
"loader": {
|
||||
lazyGetter: function (aObject, aName, aLambda) {
|
||||
Object.defineProperty(aObject, aName, {
|
||||
get: function () {
|
||||
delete aObject[aName];
|
||||
return aObject[aName] = aLambda.apply(aObject);
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
},
|
||||
lazyImporter: function () { throw new Error("Can't import JSM from worker debugger server") },
|
||||
lazyServiceGetter: function () { throw new Error("Can't import XPCOM from worker debugger server") },
|
||||
lazyRequireGetter: function (obj, property, module, destructure) {
|
||||
Object.defineProperty(obj, property, {
|
||||
get: () => destructure
|
||||
? worker.require(module)[property]
|
||||
: worker.require(module || property)
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
loadInSandbox: loadInSandbox,
|
||||
modules: {
|
||||
|
@ -31,6 +31,10 @@ xul|*.help-button > xul|*.button-box > xul|*.button-icon {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
xul|*.groupbox-body {
|
||||
-moz-padding-start: 0;
|
||||
}
|
||||
|
||||
xul|menulist {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
@ -20,6 +20,16 @@ xul|menulist {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
xul|button {
|
||||
/* use the same margin of other elements for the alignment */
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
xul|groupbox > xul|*.groupbox-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
xul|menulist:not([editable="true"]) > xul|menupopup > xul|menuitem[checked="true"]::before,
|
||||
xul|menulist:not([editable="true"]) > xul|menupopup > xul|menuitem[selected="true"]::before {
|
||||
display: none;
|
||||
|
@ -40,8 +40,15 @@ xul|caption > xul|label {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
xul|description {
|
||||
-moz-margin-start: 0;
|
||||
}
|
||||
|
||||
*|*.main-content {
|
||||
padding: 40px 48px 48px;
|
||||
padding-top: 40px;
|
||||
-moz-padding-end: 44px; /* compensate the 4px margin of child elements */
|
||||
padding-bottom: 48px;
|
||||
-moz-padding-start: 48px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@ -54,9 +61,7 @@ xul|prefpane > xul|*.content-box {
|
||||
xul|groupbox {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
-moz-margin-end: 0;
|
||||
margin: 15px 0;
|
||||
-moz-padding-start: 0;
|
||||
-moz-padding-end: 0;
|
||||
font-size: 1.25rem;
|
||||
@ -524,6 +529,7 @@ xul|*#categories[keyboard-navigation="true"]:-moz-focusring > xul|*.category[cur
|
||||
|
||||
*|*.header {
|
||||
border-bottom: 1px solid #c8c8c8;
|
||||
-moz-margin-end: 4px; /* add the 4px end-margin of other elements */
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
@ -584,6 +590,7 @@ xul|filefield + xul|button {
|
||||
xul|richlistbox,
|
||||
xul|listbox {
|
||||
-moz-appearance: none;
|
||||
-moz-margin-start: 0;
|
||||
background-color: #fff;
|
||||
border: 1px solid #c1c1c1;
|
||||
color: #333;
|
||||
|
Loading…
Reference in New Issue
Block a user