Merge mozilla-central to autoland. a=merge on a CLOSED TREE

This commit is contained in:
Andreea Pavel 2018-08-11 13:27:33 +03:00
Родитель c82bf65856 308525ebcd
Коммит 2f1ab64d7d
47 изменённых файлов: 797 добавлений и 1047 удалений

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

@ -20,7 +20,7 @@ def min_android_version(target):
if target.cpu in ['aarch64', 'x86_64', 'mips64']:
# 64-bit support was added in API 21.
return '21'
return '9'
return '16'
js_option('--with-android-version',

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

@ -8,11 +8,34 @@ option('--enable-nodejs',
help='Require Node.js to build')
@depends(host)
@imports('os')
@imports(_from='os', _import='environ')
def node_toolchain_search_path(host):
# XXX partly copied from tooltool.py; should be hoisted somewhere central
# Also add in the location to which `mach bootstrap` or
# `mach artifact toolchain` installs clang.
mozbuild_state_dir = environ.get('MOZBUILD_STATE_PATH', os.path.expanduser(
os.path.join('~', '.mozbuild')))
if host.kernel == "WINNT":
mozbuild_node_path = os.path.join(mozbuild_state_dir, 'node')
else:
mozbuild_node_path = os.path.join(mozbuild_state_dir, 'node', 'bin')
# We still fallback to the PATH, since on OSes that don't have toolchain
# artifacts available to download, Node may be coming from $PATH.
path = [environ.get('PATH')]
updated_path = [mozbuild_node_path] + path
return updated_path
# "nodejs" is first in the tuple on the assumption that it's only likely to
# exist on systems (probably linux distros) where there is a program in the path
# called "node" that does something else.
nodejs = check_prog('NODEJS', ('nodejs', 'node',),
allow_missing=True)
allow_missing=True, paths=node_toolchain_search_path)
@depends_if(nodejs)

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

@ -1,9 +1,9 @@
This is the debugger.html project output.
See https://github.com/devtools-html/debugger.html
Version 80
Version 81
Comparison: https://github.com/devtools-html/debugger.html/compare/release-79...release-80
Comparison: https://github.com/devtools-html/debugger.html/compare/release-80...release-81
Packages:
- babel-plugin-transform-es2015-modules-commonjs @6.26.2

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

@ -60,6 +60,10 @@ var _sources = require("./sources/index");
var sources = _interopRequireWildcard(_sources);
var _tabs = require("./tabs");
var tabs = _interopRequireWildcard(_tabs);
var _debuggee = require("./debuggee");
var debuggee = _interopRequireWildcard(_debuggee);
@ -82,6 +86,7 @@ exports.default = { ...navigation,
...expressions,
...eventListeners,
...sources,
...tabs,
...pause,
...ui,
...fileSearch,

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

@ -25,6 +25,7 @@ DevToolsModules(
'quick-open.js',
'replay.js',
'source-tree.js',
'tabs.js',
'toolbox.js',
'ui.js',
)

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

@ -62,16 +62,4 @@ Object.keys(_select).forEach(function (key) {
return _select[key];
}
});
});
var _tabs = require("./tabs");
Object.keys(_tabs).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _tabs[key];
}
});
});

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

@ -14,5 +14,4 @@ DevToolsModules(
'newSources.js',
'prettyPrint.js',
'select.js',
'tabs.js',
)

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

@ -53,8 +53,14 @@ function loadSourceMaps(sources) {
return;
}
const originalSources = await Promise.all(sources.map(source => dispatch(loadSourceMap(source.id))));
await dispatch(newSources((0, _lodash.flatten)(originalSources)));
let originalSources = await Promise.all(sources.map(({
id
}) => dispatch(loadSourceMap(id))));
originalSources = (0, _lodash.flatten)(originalSources).filter(Boolean);
if (originalSources.length > 0) {
await dispatch(newSources(originalSources));
}
};
}
/**
@ -185,25 +191,25 @@ function newSources(sources) {
dispatch,
getState
}) => {
const filteredSources = sources.filter(source => source && !(0, _selectors.getSource)(getState(), source.id));
sources = sources.filter(source => !(0, _selectors.getSource)(getState(), source.id));
if (filteredSources.length == 0) {
if (sources.length == 0) {
return;
}
dispatch({
type: "ADD_SOURCES",
sources: filteredSources
sources: sources
});
for (const source of filteredSources) {
for (const source of sources) {
dispatch(checkSelectedSource(source.id));
dispatch(checkPendingBreakpoints(source.id));
}
await dispatch(loadSourceMaps(filteredSources)); // We would like to restore the blackboxed state
await dispatch(loadSourceMaps(sources)); // We would like to restore the blackboxed state
// after loading all states to make sure the correctness.
await dispatch(restoreBlackBoxedSources(filteredSources));
await dispatch(restoreBlackBoxedSources(sources));
};
}

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

@ -20,7 +20,7 @@ var _ui = require("../ui");
var _prettyPrint = require("./prettyPrint");
var _tabs = require("./tabs");
var _tabs = require("../tabs");
var _loadSourceText = require("./loadSourceText");

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

@ -0,0 +1,91 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.addTab = addTab;
exports.moveTab = moveTab;
exports.closeTab = closeTab;
exports.closeTabs = closeTabs;
var _editor = require("../utils/editor/index");
var _sources = require("./sources/index");
var _selectors = require("../selectors/index");
/* 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/>. */
/**
* Redux actions for the editor tabs
* @module actions/tabs
*/
function addTab(url, tabIndex) {
return {
type: "ADD_TAB",
url,
tabIndex
};
}
function moveTab(url, tabIndex) {
return {
type: "MOVE_TAB",
url,
tabIndex
};
}
/**
* @memberof actions/tabs
* @static
*/
function closeTab(url) {
return ({
dispatch,
getState,
client
}) => {
(0, _editor.removeDocument)(url);
const tabs = (0, _selectors.removeSourceFromTabList)((0, _selectors.getSourceTabs)(getState()), url);
const sourceId = (0, _selectors.getNewSelectedSourceId)(getState(), tabs);
dispatch({
type: "CLOSE_TAB",
url,
tabs
});
dispatch((0, _sources.selectSource)(sourceId));
};
}
/**
* @memberof actions/tabs
* @static
*/
function closeTabs(urls) {
return ({
dispatch,
getState,
client
}) => {
urls.forEach(url => {
const source = (0, _selectors.getSourceByURL)(getState(), url);
if (source) {
(0, _editor.removeDocument)(source.id);
}
});
const tabs = (0, _selectors.removeSourcesFromTabList)((0, _selectors.getSourceTabs)(getState()), urls);
dispatch({
type: "CLOSE_TABS",
urls,
tabs
});
const sourceId = (0, _selectors.getNewSelectedSourceId)(getState(), tabs);
dispatch((0, _sources.selectSource)(sourceId));
};
}

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

@ -165,7 +165,7 @@ class Tab extends _react.PureComponent {
return _react2.default.createElement("div", {
className: className,
key: sourceId,
onMouseUp: handleTabClick,
onClick: handleTabClick,
onContextMenu: e => this.onTabContextMenu(e, sourceId),
title: (0, _source.getFileURL)(source)
}, _react2.default.createElement(_SourceIcon2.default, {

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

@ -182,7 +182,7 @@ class Editor extends _react.PureComponent {
const sourceLine = (0, _editor.toSourceLine)(selectedSource.id, line);
if (ev.altKey) {
if (ev.metaKey) {
return continueToHere(sourceLine);
}
@ -423,7 +423,8 @@ class Editor extends _react.PureComponent {
const isFirstLoad = (!selectedSource || !(0, _source.isLoaded)(selectedSource)) && (0, _source.isLoaded)(nextProps.selectedSource);
const locationChanged = selectedLocation !== nextProps.selectedLocation;
return isFirstLoad || locationChanged;
const symbolsChanged = nextProps.symbols != this.props.symbols;
return isFirstLoad || locationChanged || symbolsChanged;
}
scrollToLocation(nextProps) {

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

@ -162,8 +162,7 @@ class SourcesTree extends _react.Component {
}, _react2.default.createElement(_Svg2.default, {
name: "home"
}), _react2.default.createElement(_Svg2.default, {
name: "breadcrumb",
"class": true
name: "breadcrumb"
}), _react2.default.createElement("span", {
className: "sources-clear-root-label"
}, rootLabel)));

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

@ -125,7 +125,7 @@ class SourceTreeItem extends _react.Component {
return _react2.default.createElement(_Svg2.default, {
name: "angular"
});
} else if (item.path === "moz-extension://") {
} else if (item.path.startsWith("moz-extension://") && depth === 0) {
return _react2.default.createElement("img", {
className: "extension"
});
@ -176,9 +176,6 @@ class SourceTreeItem extends _react.Component {
case "webpack://":
return "Webpack";
case "moz-extension://":
return L10N.getStr("extensionsText");
default:
return name;
}

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

@ -16,6 +16,10 @@ var _sources = require("./sources");
var _sources2 = _interopRequireDefault(_sources);
var _tabs = require("./tabs");
var _tabs2 = _interopRequireDefault(_tabs);
var _breakpoints = require("./breakpoints");
var _breakpoints2 = _interopRequireDefault(_breakpoints);
@ -82,6 +86,7 @@ exports.default = {
expressions: _expressions2.default,
eventListeners: _eventListeners2.default,
sources: _sources2.default,
tabs: _tabs2.default,
breakpoints: _breakpoints2.default,
pendingBreakpoints: _pendingBreakpoints2.default,
asyncRequests: _asyncRequests2.default,

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

@ -24,5 +24,6 @@ DevToolsModules(
'replay.js',
'source-tree.js',
'sources.js',
'tabs.js',
'ui.js',
)

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

@ -3,13 +3,10 @@
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getSelectedSource = exports.getSelectedLocation = exports.getSourcesForTabs = exports.getSourceTabs = exports.getTabs = undefined;
exports.getSelectedSource = exports.getSelectedLocation = exports.getSourceCount = undefined;
exports.initialSourcesState = initialSourcesState;
exports.createSource = createSource;
exports.removeSourceFromTabList = removeSourceFromTabList;
exports.removeSourcesFromTabList = removeSourcesFromTabList;
exports.getBlackBoxList = getBlackBoxList;
exports.getNewSelectedSourceId = getNewSelectedSourceId;
exports.getSource = getSource;
exports.getSourceFromId = getSourceFromId;
exports.getSourceByURL = getSourceByURL;
@ -17,27 +14,20 @@ exports.getGeneratedSource = getGeneratedSource;
exports.getPendingSelectedLocation = getPendingSelectedLocation;
exports.getPrettySource = getPrettySource;
exports.hasPrettySource = hasPrettySource;
exports.getSourceByUrlInSources = getSourceByUrlInSources;
exports.getSourceInSources = getSourceInSources;
exports.getSources = getSources;
exports.getUrls = getUrls;
exports.getSourceList = getSourceList;
exports.getSourceCount = getSourceCount;
var _reselect = require("devtools/client/debugger/new/dist/vendors").vendored["reselect"];
var _lodashMove = require("devtools/client/debugger/new/dist/vendors").vendored["lodash-move"];
var _lodashMove2 = _interopRequireDefault(_lodashMove);
var _source = require("../utils/source");
var _devtoolsSourceMap = require("devtools/client/shared/source-map/index.js");
var _lodash = require("devtools/client/shared/vendor/lodash");
var _prefs = require("../utils/prefs");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/* 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/>. */
@ -49,9 +39,9 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
function initialSourcesState() {
return {
sources: {},
urls: {},
selectedLocation: undefined,
pendingSelectedLocation: _prefs.prefs.pendingSelectedLocation,
tabs: restoreTabs()
pendingSelectedLocation: _prefs.prefs.pendingSelectedLocation
};
}
@ -125,28 +115,6 @@ function update(state = initialSourcesState(), action) {
pendingSelectedLocation: location
};
case "ADD_TAB":
return { ...state,
tabs: updateTabList(state.tabs, action.url)
};
case "MOVE_TAB":
return { ...state,
tabs: updateTabList(state.tabs, action.url, action.tabIndex)
};
case "CLOSE_TAB":
_prefs.prefs.tabs = action.tabs;
return { ...state,
tabs: action.tabs
};
case "CLOSE_TABS":
_prefs.prefs.tabs = action.tabs;
return { ...state,
tabs: action.tabs
};
case "LOAD_SOURCE_TEXT":
return setSourceTextProps(state, action);
@ -228,45 +196,18 @@ function updateSource(state, source) {
const updatedSource = existingSource ? { ...existingSource,
...source
} : createSource(source);
const existingUrls = state.urls[source.url];
const urls = existingUrls ? [...existingUrls, source.id] : [source.id];
return { ...state,
sources: { ...state.sources,
[source.id]: updatedSource
},
urls: { ...state.urls,
[source.url]: urls
}
};
}
function removeSourceFromTabList(tabs, url) {
return tabs.filter(tab => tab !== url);
}
function removeSourcesFromTabList(tabs, urls) {
return urls.reduce((t, url) => removeSourceFromTabList(t, url), tabs);
}
function restoreTabs() {
const prefsTabs = _prefs.prefs.tabs || [];
return prefsTabs;
}
/**
* Adds the new source to the tab list if it is not already there
* @memberof reducers/sources
* @static
*/
function updateTabList(tabs, url, newIndex) {
const currentIndex = tabs.indexOf(url);
if (currentIndex === -1) {
tabs = [url, ...tabs];
} else if (newIndex !== undefined) {
tabs = (0, _lodashMove2.default)(tabs, currentIndex, newIndex);
}
_prefs.prefs.tabs = tabs;
return tabs;
}
function updateBlackBoxList(url, isBlackBoxed) {
const tabs = getBlackBoxList();
const i = tabs.indexOf(url);
@ -284,59 +225,6 @@ function updateBlackBoxList(url, isBlackBoxed) {
function getBlackBoxList() {
return _prefs.prefs.tabsBlackBoxed || [];
}
/**
* Gets the next tab to select when a tab closes. Heuristics:
* 1. if the selected tab is available, it remains selected
* 2. if it is gone, the next available tab to the left should be active
* 3. if the first tab is active and closed, select the second tab
*
* @memberof reducers/sources
* @static
*/
function getNewSelectedSourceId(state, availableTabs) {
const selectedLocation = state.sources.selectedLocation;
if (!selectedLocation) {
return "";
}
const selectedTab = getSource(state, selectedLocation.sourceId);
if (!selectedTab) {
return "";
}
if (availableTabs.includes(selectedTab.url)) {
const sources = state.sources.sources;
if (!sources) {
return "";
}
const selectedSource = getSourceByURL(state, selectedTab.url);
if (selectedSource) {
return selectedSource.id;
}
return "";
}
const tabUrls = state.sources.tabs;
const leftNeighborIndex = Math.max(tabUrls.indexOf(selectedTab.url) - 1, 0);
const lastAvailbleTabIndex = availableTabs.length - 1;
const newSelectedTabIndex = Math.min(leftNeighborIndex, lastAvailbleTabIndex);
const availableTab = availableTabs[newSelectedTabIndex];
const tabSource = getSourceByUrlInSources(state.sources.sources, availableTab);
if (tabSource) {
return tabSource.id;
}
return "";
} // Selectors
// Unfortunately, it's really hard to make these functions accept just
// the state that we care about and still type it with Flow. The
@ -358,7 +246,7 @@ function getSourceFromId(state, id) {
}
function getSourceByURL(state, url) {
return getSourceByUrlInSources(state.sources.sources, url);
return getSourceByUrlInSources(getSources(state), getUrls(state), url);
}
function getGeneratedSource(state, source) {
@ -387,12 +275,22 @@ function hasPrettySource(state, id) {
return !!getPrettySource(state, id);
}
function getSourceByUrlInSources(sources, url) {
if (!url) {
function getSourceByUrlInSources(sources, urls, url) {
const foundSources = getSourcesByUrlInSources(sources, urls, url);
if (!foundSources) {
return null;
}
return (0, _lodash.find)(sources, source => source.url === url);
return foundSources[0];
}
function getSourcesByUrlInSources(sources, urls, url) {
if (!url || !urls[url]) {
return [];
}
return urls[url].map(id => sources[id]);
}
function getSourceInSources(sources, id) {
@ -403,19 +301,15 @@ function getSources(state) {
return state.sources.sources;
}
function getUrls(state) {
return state.sources.urls;
}
function getSourceList(state) {
return Object.values(getSources(state));
}
function getSourceCount(state) {
return Object.keys(getSources(state)).length;
}
const getTabs = exports.getTabs = (0, _reselect.createSelector)(getSourcesState, sources => sources.tabs);
const getSourceTabs = exports.getSourceTabs = (0, _reselect.createSelector)(getTabs, getSources, (tabs, sources) => tabs.filter(tab => getSourceByUrlInSources(sources, tab)));
const getSourcesForTabs = exports.getSourcesForTabs = (0, _reselect.createSelector)(getSourceTabs, getSources, (tabs, sources) => {
return tabs.map(tab => getSourceByUrlInSources(sources, tab)).filter(source => source);
});
const getSourceCount = exports.getSourceCount = (0, _reselect.createSelector)(getSources, sources => Object.keys(sources).length);
const getSelectedLocation = exports.getSelectedLocation = (0, _reselect.createSelector)(getSourcesState, sources => sources.selectedLocation);
const getSelectedSource = exports.getSelectedSource = (0, _reselect.createSelector)(getSelectedLocation, getSources, (selectedLocation, sources) => {
if (!selectedLocation) {

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

@ -0,0 +1,143 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getSourcesForTabs = exports.getSourceTabs = exports.getTabs = undefined;
exports.removeSourceFromTabList = removeSourceFromTabList;
exports.removeSourcesFromTabList = removeSourcesFromTabList;
exports.getNewSelectedSourceId = getNewSelectedSourceId;
var _reselect = require("devtools/client/debugger/new/dist/vendors").vendored["reselect"];
var _lodashMove = require("devtools/client/debugger/new/dist/vendors").vendored["lodash-move"];
var _lodashMove2 = _interopRequireDefault(_lodashMove);
var _prefs = require("../utils/prefs");
var _sources = require("./sources");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/* 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/>. */
/**
* Tabs reducer
* @module reducers/tabs
*/
function update(state = _prefs.prefs.tabs || [], action) {
switch (action.type) {
case "ADD_TAB":
return updateTabList(state, action.url);
case "MOVE_TAB":
return updateTabList(state, action.url, action.tabIndex);
case "CLOSE_TAB":
case "CLOSE_TABS":
_prefs.prefs.tabs = action.tabs;
return action.tabs;
default:
return state;
}
}
function removeSourceFromTabList(tabs, url) {
return tabs.filter(tab => tab !== url);
}
function removeSourcesFromTabList(tabs, urls) {
return urls.reduce((t, url) => removeSourceFromTabList(t, url), tabs);
}
/**
* Adds the new source to the tab list if it is not already there
* @memberof reducers/tabs
* @static
*/
function updateTabList(tabs, url, newIndex) {
const currentIndex = tabs.indexOf(url);
if (currentIndex === -1) {
tabs = [url, ...tabs];
} else if (newIndex !== undefined) {
tabs = (0, _lodashMove2.default)(tabs, currentIndex, newIndex);
}
_prefs.prefs.tabs = tabs;
return tabs;
}
/**
* Gets the next tab to select when a tab closes. Heuristics:
* 1. if the selected tab is available, it remains selected
* 2. if it is gone, the next available tab to the left should be active
* 3. if the first tab is active and closed, select the second tab
*
* @memberof reducers/tabs
* @static
*/
function getNewSelectedSourceId(state, availableTabs) {
const selectedLocation = state.sources.selectedLocation;
if (!selectedLocation) {
return "";
}
const selectedTab = (0, _sources.getSource)(state, selectedLocation.sourceId);
if (!selectedTab) {
return "";
}
if (availableTabs.includes(selectedTab.url)) {
const sources = state.sources.sources;
if (!sources) {
return "";
}
const selectedSource = (0, _sources.getSourceByURL)(state, selectedTab.url);
if (selectedSource) {
return selectedSource.id;
}
return "";
}
const tabUrls = state.tabs;
const leftNeighborIndex = Math.max(tabUrls.indexOf(selectedTab.url) - 1, 0);
const lastAvailbleTabIndex = availableTabs.length - 1;
const newSelectedTabIndex = Math.min(leftNeighborIndex, lastAvailbleTabIndex);
const availableTab = availableTabs[newSelectedTabIndex];
const tabSource = (0, _sources.getSourceByUrlInSources)((0, _sources.getSources)(state), (0, _sources.getUrls)(state), availableTab);
if (tabSource) {
return tabSource.id;
}
return "";
} // Selectors
// Unfortunately, it's really hard to make these functions accept just
// the state that we care about and still type it with Flow. The
// problem is that we want to re-export all selectors from a single
// module for the UI, and all of those selectors should take the
// top-level app state, so we'd have to "wrap" them to automatically
// pick off the piece of state we're interested in. It's impossible
// (right now) to type those wrapped functions.
const getTabs = exports.getTabs = state => state.tabs;
const getSourceTabs = exports.getSourceTabs = (0, _reselect.createSelector)(getTabs, _sources.getSources, _sources.getUrls, (tabs, sources, urls) => tabs.filter(tab => (0, _sources.getSourceByUrlInSources)(sources, urls, tab)));
const getSourcesForTabs = exports.getSourcesForTabs = (0, _reselect.createSelector)(getSourceTabs, _sources.getSources, _sources.getUrls, (tabs, sources, urls) => {
return tabs.map(tab => (0, _sources.getSourceByUrlInSources)(sources, urls, tab)).filter(source => source);
});
exports.default = update;

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

@ -28,6 +28,18 @@ Object.keys(_sources).forEach(function (key) {
});
});
var _tabs = require("../reducers/tabs");
Object.keys(_tabs).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _tabs[key];
}
});
});
var _pause = require("../reducers/pause");
Object.keys(_pause).forEach(function (key) {

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

@ -193,9 +193,13 @@ function isVisible(codeMirror, top, left) {
const scrollArea = codeMirror.getScrollInfo();
const charWidth = codeMirror.defaultCharWidth();
const inXView = withinBounds(left, scrollArea.left, scrollArea.left + (scrollArea.clientWidth - 30) - charWidth);
const fontHeight = codeMirror.defaultTextHeight();
const inYView = withinBounds(top, scrollArea.top, scrollArea.top + scrollArea.clientHeight - fontHeight);
const {
scrollTop,
scrollLeft
} = codeMirror.doc;
const inXView = withinBounds(left, scrollLeft, scrollLeft + (scrollArea.clientWidth - 30) - charWidth);
const inYView = withinBounds(top, scrollTop, scrollTop + scrollArea.clientHeight - fontHeight);
return inXView && inYView;
}

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

@ -28,8 +28,18 @@ function findMessages(win, query) {
)
}
async function hasMessage(dbg, msg) {
const webConsole = await dbg.toolbox.getPanel("webconsole")
return waitFor(async () => findMessages(
webConsole._frameWindow,
msg
).length > 0)
}
add_task(async function() {
Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true);
Services.prefs.setBoolPref("devtools.map-await-expression", true);
const dbg = await initDebugger("doc-script-switching.html");
await selectSource(dbg, "switching-01");
@ -41,12 +51,14 @@ add_task(async function() {
const webConsole = await dbg.toolbox.getPanel("webconsole")
const jsterm = webConsole.hud.jsterm;
await jsterm.execute(`let sleep = async (time, v) => new Promise(
res => setTimeout(() => res(v+'!!!'), time)
)`);
await jsterm.execute(`
let sleep = async (time, v) => new Promise(
res => setTimeout(() => res(v+'!!!'), time)
)
`);
await jsterm.execute(`await sleep(200, "DONE")`)
await waitFor(async () => findMessages(webConsole._frameWindow, "DONE!!!").length > 0)
await hasMessage(dbg, "sleep");
await jsterm.execute(`await sleep(200, "DONE")`);
await hasMessage(dbg, "DONE!!!");
});

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

@ -4,6 +4,8 @@
"use strict";
const { dumpn } = require("devtools/shared/DevToolsUtils");
loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
loader.lazyImporter(this, "ExtensionParent", "resource://gre/modules/ExtensionParent.jsm");
loader.lazyRequireGetter(this, "Services");
@ -14,21 +16,34 @@ loader.lazyRequireGetter(this, "NetUtil",
loader.lazyGetter(this, "UNPACKED_ROOT_PATH", () => {
return OS.Path.join(OS.Constants.Path.localProfileDir, "adb");
});
loader.lazyGetter(this, "EXTENSION_ID", () => {
return Services.prefs.getCharPref("devtools.remote.adb.extensionID");
});
loader.lazyGetter(this, "ADB_BINARY_PATH", () => {
let adbBinaryPath = OS.Path.join(UNPACKED_ROOT_PATH, "adb");
if (Services.appinfo.OS === "WINNT") {
adbBinaryPath += ".exe";
}
return adbBinaryPath;
});
// FIXME: Bug 1481691 - Read the extension ID from a pref.
const EXTENSION_ID = "adb@mozilla.org";
const MANIFEST = "manifest.json";
async function getAdbInfo(adbUri) {
/**
* Read contents from a given uri in the devtools-adb-extension and parse the
* contents as JSON.
*/
async function readFromExtension(fileUri) {
return new Promise(resolve => {
NetUtil.asyncFetch({
uri: adbUri,
uri: fileUri,
loadUsingSystemPrincipal: true
}, (input) => {
try {
const string = NetUtil.readInputStreamToString(input, input.available());
resolve(JSON.parse(string));
} catch (e) {
console.log(`Could not read adb.json in the extension: ${e}`);
dumpn(`Could not read ${fileUri} in the extension: ${e}`);
resolve(null);
}
});
@ -40,8 +55,7 @@ async function getAdbInfo(adbUri) {
* Uses NetUtil to read and write, since it's required for reading.
*
* @param {string} file
* The path name of the file in the extension, such as "win32/adb.exe".
* This has to have a '/' in the path string.
* The path name of the file in the extension.
*/
async function unpackFile(file) {
const policy = ExtensionParent.WebExtensionPolicy.getByID(EXTENSION_ID);
@ -50,7 +64,8 @@ async function unpackFile(file) {
}
// Assumes that destination dir already exists.
const filePath = OS.Path.join(UNPACKED_ROOT_PATH, file.split("/")[1]);
const basePath = file.substring(file.lastIndexOf("/") + 1);
const filePath = OS.Path.join(UNPACKED_ROOT_PATH, basePath);
await new Promise((resolve, reject) => {
NetUtil.asyncFetch({
uri: policy.getURL(file),
@ -63,7 +78,7 @@ async function unpackFile(file) {
const output = FileUtils.openAtomicFileOutputStream(outputFile);
NetUtil.asyncCopy(input, output, resolve);
} catch (e) {
console.log(`Could not unpack file ${file} in the extension: ${e}`);
dumpn(`Could not unpack file ${file} in the extension: ${e}`);
reject(e);
}
});
@ -74,18 +89,17 @@ async function unpackFile(file) {
/**
* Extract files in the extension into local profile directory and returns
* the path for the adb binary.
* if it fails.
*/
async function extractBinary() {
async function extractFiles() {
const policy = ExtensionParent.WebExtensionPolicy.getByID(EXTENSION_ID);
if (!policy) {
return null;
return false;
}
const uri = policy.getURL("adb.json");
const adbInfo = await getAdbInfo(uri);
const adbInfo = await readFromExtension(uri);
if (!adbInfo) {
return null;
return false;
}
let filesForAdb;
@ -106,25 +120,88 @@ async function extractBinary() {
// XPCOMABI looks this; x86_64-gcc3, so drop the compiler name.
adbInfo[Services.appinfo.OS][Services.appinfo.XPCOMABI.split("-")[0]];
} catch (e) {
return null;
return false;
}
// manifest.json isn't in adb.json but has to be unpacked for version
// comparison
filesForAdb.push(MANIFEST);
await OS.File.makeDir(UNPACKED_ROOT_PATH);
for (const file of filesForAdb) {
try {
await unpackFile(file);
} catch (e) {
return null;
return false;
}
}
let adbBinaryPath = OS.Path.join(UNPACKED_ROOT_PATH, "adb");
return true;
}
if (Services.appinfo.OS === "WINNT") {
adbBinaryPath += ".exe";
/**
* Read the manifest from inside the devtools-adb-extension.
* Uses NetUtil since data is packed inside the extension, not a local file.
*/
async function getManifestFromExtension() {
const policy = ExtensionParent.WebExtensionPolicy.getByID(EXTENSION_ID);
if (!policy) {
return null;
}
return adbBinaryPath;
const manifestUri = policy.getURL(MANIFEST);
return readFromExtension(manifestUri);
}
/**
* Returns whether manifest.json has already been unpacked.
*/
async function isManifestUnpacked() {
const manifestPath = OS.Path.join(UNPACKED_ROOT_PATH, MANIFEST);
return OS.File.exists(manifestPath);
}
/**
* Read the manifest from the unpacked binary directory.
* Uses OS.File since this is a local file.
*/
async function getManifestFromUnpacked() {
if (!await isManifestUnpacked()) {
throw new Error("Manifest doesn't exist at unpacked path");
}
const manifestPath = OS.Path.join(UNPACKED_ROOT_PATH, MANIFEST);
const binary = await OS.File.read(manifestPath);
const json = new TextDecoder().decode(binary);
let data;
try {
data = JSON.parse(json);
} catch (e) {
}
return data;
}
/**
* Check state of binary unpacking, including the location and manifest.
*/
async function isUnpacked() {
if (!await isManifestUnpacked()) {
dumpn("Needs unpacking, no manifest found");
return false;
}
const manifestInExtension = await getManifestFromExtension();
const unpackedManifest = await getManifestFromUnpacked();
if (manifestInExtension.version != unpackedManifest.version) {
dumpn(
`Needs unpacking, extension version ${manifestInExtension.version} != ` +
`unpacked version ${unpackedManifest.version}`
);
return false;
}
dumpn("Already unpacked");
return true;
}
/**
@ -135,11 +212,11 @@ async function extractBinary() {
* File object for the binary.
*/
async function getFileForBinary() {
const path = await extractBinary();
if (!path) {
if (!await isUnpacked() &&
!await extractFiles()) {
return null;
}
console.log(`Binary path: ${path}`);
return new FileUtils.File(path);
return new FileUtils.File(ADB_BINARY_PATH);
}
exports.getFileForBinary = getFileForBinary;

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

@ -11,6 +11,7 @@
"use strict";
const { AdbSocket } = require("./adb-socket");
const { dumpn } = require("devtools/shared/DevToolsUtils");
const OKAY = 0x59414b4f;
const FAIL = 0x4c494146;
@ -27,9 +28,9 @@ function getBuffer(packet) {
// @return A js object { length:...; data:... }
function unpackPacket(packet, ignoreResponse) {
const buffer = getBuffer(packet);
console.debug("Len buffer: " + buffer.byteLength);
dumpn("Len buffer: " + buffer.byteLength);
if (buffer.byteLength === 4 && !ignoreResponse) {
console.debug("Packet empty");
dumpn("Packet empty");
return { length: 0, data: "" };
}
const lengthView = new Uint8Array(buffer, ignoreResponse ? 0 : 4, 4);
@ -45,9 +46,9 @@ function checkResponse(packet, expected = OKAY) {
const buffer = getBuffer(packet);
const view = new Uint32Array(buffer, 0, 1);
if (view[0] == FAIL) {
console.debug("Response: FAIL");
dumpn("Response: FAIL");
}
console.debug("view[0] = " + view[0]);
dumpn("view[0] = " + view[0]);
return view[0] == expected;
}
@ -62,7 +63,7 @@ function createRequest(command) {
}
const encoder = new TextEncoder();
console.debug("Created request: " + length + command);
dumpn("Created request: " + length + command);
return encoder.encode(length + command);
}

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

@ -19,25 +19,6 @@ Device.prototype = {
shell: ADB.shell.bind(ADB),
forwardPort: ADB.forwardPort.bind(ADB),
push: ADB.push.bind(ADB),
pull: ADB.pull.bind(ADB),
reboot: ADB.reboot.bind(ADB),
rebootRecovery: ADB.rebootRecovery.bind(ADB),
rebootBootloader: ADB.rebootBootloader.bind(ADB),
isRoot() {
return ADB.shell("id").then(stdout => {
if (stdout) {
const uid = stdout.match(/uid=(\d+)/)[1];
return uid == "0";
}
return false;
});
},
summonRoot() {
return ADB.root();
},
getModel() {
if (this._modelPromise) {
@ -47,6 +28,7 @@ Device.prototype = {
.then(model => model.trim());
return this._modelPromise;
}
// push, pull were removed in Bug 1481691.
};
module.exports = Device;

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

@ -11,6 +11,7 @@
"use strict";
const client = require("./adb-client");
const { dumpn } = require("devtools/shared/DevToolsUtils");
exports.check = async function check() {
let socket;
@ -18,7 +19,7 @@ exports.check = async function check() {
let timerID;
const TIMEOUT_TIME = 1000;
console.debug("Asking for host:version");
dumpn("Asking for host:version");
return new Promise(resolve => {
// On MacOSX connecting to a port which is not started listening gets
@ -35,7 +36,7 @@ exports.check = async function check() {
}
const runFSM = function runFSM(packetData) {
console.debug("runFSM " + state);
dumpn("runFSM " + state);
switch (state) {
case "start":
const req = client.createRequest("host:version");
@ -46,40 +47,40 @@ exports.check = async function check() {
// TODO: Actually check the version number to make sure the daemon
// supports the commands we want to use
const { length, data } = client.unpackPacket(packetData);
console.debug("length: ", length, "data: ", data);
dumpn("length: ", length, "data: ", data);
socket.close();
const version = parseInt(data, 16);
if (version >= 31) {
finish(true);
} else {
console.log("killing existing adb as we need version >= 31");
dumpn("killing existing adb as we need version >= 31");
finish(false);
}
break;
default:
console.debug("Unexpected State: " + state);
dumpn("Unexpected State: " + state);
finish(false);
}
};
const setupSocket = function() {
socket.s.onerror = function(event) {
console.debug("running checker onerror");
dumpn("running checker onerror");
finish(false);
};
socket.s.onopen = function(event) {
console.debug("running checker onopen");
dumpn("running checker onopen");
state = "start";
runFSM();
};
socket.s.onclose = function(event) {
console.debug("running checker onclose");
dumpn("running checker onclose");
};
socket.s.ondata = function(event) {
console.debug("running checker ondata");
dumpn("running checker ondata");
runFSM(event.data);
};
};

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

@ -7,8 +7,8 @@
const EventEmitter = require("devtools/shared/event-emitter");
const { ConnectionManager } =
require("devtools/shared/client/connection-manager");
const { Devices } =
require("devtools/shared/apps/Devices.jsm");
const { Devices } = require("devtools/shared/apps/Devices.jsm");
const { dumpn } = require("devtools/shared/DevToolsUtils");
const { RuntimeTypes } =
require("devtools/client/webide/modules/runtime-types");
@ -56,9 +56,8 @@ const ADBScanner = {
_detectRuntimes: async function(device) {
const model = await device.getModel();
let detectedRuntimes = await FirefoxOSRuntime.detect(device, model);
this._runtimes.push(...detectedRuntimes);
detectedRuntimes = await FirefoxOnAndroidRuntime.detect(device, model);
const detectedRuntimes =
await FirefoxOnAndroidRuntime.detect(device, model);
this._runtimes.push(...detectedRuntimes);
},
@ -102,40 +101,6 @@ Runtime.prototype = {
},
};
// FIXME: Bug 1481691 - Drop code for support FirefoxOS.
function FirefoxOSRuntime(device, model) {
Runtime.call(this, device, model, "/data/local/debugger-socket");
}
FirefoxOSRuntime.detect = async function(device, model) {
const runtimes = [];
const query = "test -f /system/b2g/b2g; echo $?";
let b2gExists = await device.shell(query);
// XXX: Sometimes we get an empty response back. Likely a bug in our shell
// code in this add-on.
// There are also some Android devices that do not have `test` installed.
for (let attempts = 3; attempts > 0; attempts--) {
b2gExists = await device.shell(query);
if (b2gExists.length == 3) {
break;
}
}
if (b2gExists === "0\r\n") {
const runtime = new FirefoxOSRuntime(device, model);
console.log("Found " + runtime.name);
runtimes.push(runtime);
}
return runtimes;
};
FirefoxOSRuntime.prototype = Object.create(Runtime.prototype);
Object.defineProperty(FirefoxOSRuntime.prototype, "name", {
get() {
return this._model || this.device.id;
}
});
function FirefoxOnAndroidRuntime(device, model, socketPath) {
Runtime.call(this, device, model, socketPath);
}
@ -159,7 +124,7 @@ FirefoxOnAndroidRuntime.detect = async function(device, model) {
}
for (const socketPath of socketPaths) {
const runtime = new FirefoxOnAndroidRuntime(device, model, socketPath);
console.log("Found " + runtime.name);
dumpn("Found " + runtime.name);
runtimes.push(runtime);
}
return runtimes;

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

@ -5,13 +5,12 @@
"use strict";
const { Cu } = require("chrome");
const { dumpn } = require("devtools/shared/DevToolsUtils");
function createTCPSocket(location, port, options) {
// Starting with FF57, jsm share the same global and requires some special code
const { TCPSocket } =
Cu.getGlobalForObject(Cu.import("resource://gre/modules/Services.jsm", {}));
// Starting with FF43, TCPSocket is now exposed via WebIDL
return new TCPSocket(location, port, options);
}
@ -53,7 +52,7 @@ class AdbSocket {
dbg += s[i];
}
}
console.debug(dbg);
dumpn(dbg);
}
// debugging version of tcpsocket.send()

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

@ -9,10 +9,9 @@
const { Cc, Ci } = require("chrome");
const EventEmitter = require("devtools/shared/event-emitter");
const client = require("./adb-client");
const { dumpn } = require("devtools/shared/DevToolsUtils");
const { getFileForBinary } = require("./adb-binary");
const { setTimeout } = require("resource://gre/modules/Timer.jsm");
const { PromiseUtils } = require("resource://gre/modules/PromiseUtils.jsm");
const { OS } = require("resource://gre/modules/osfile.jsm");
const { Services } = require("resource://gre/modules/Services.jsm");
loader.lazyRequireGetter(this, "check",
"devtools/shared/adb/adb-running-checker", true);
@ -21,10 +20,6 @@ let ready = false;
let didRunInitially = false;
const OKAY = 0x59414b4f;
// const FAIL = 0x4c494146;
// const STAT = 0x54415453;
const DATA = 0x41544144;
const DONE = 0x454e4f44;
const ADB = {
get didRunInitially() {
@ -94,11 +89,11 @@ const ADB = {
const isAdbRunning = await check();
if (isAdbRunning) {
this.didRunInitially = false;
console.log("Found ADB process running, not restarting");
dumpn("Found ADB process running, not restarting");
onSuccessfulStart();
return;
}
console.log("Didn't find ADB process running, restarting");
dumpn("Didn't find ADB process running, restarting");
this.didRunInitially = true;
const process = Cc["@mozilla.org/process/util;1"]
@ -107,13 +102,8 @@ const ADB = {
const adbFile = await this.adbFilePromise;
process.init(adbFile);
// Hide command prompt window on Windows
try {
// startHidden is 55+
process.startHidden = true;
// noShell attribute is 58+
process.noShell = true;
} catch (e) {
}
process.startHidden = true;
process.noShell = true;
const params = ["start-server"];
let isStarted = false;
try {
@ -134,16 +124,12 @@ const ADB = {
/**
* Stop the ADB server, but only if we started it. If it was started before
* us, we return immediately.
*
* @param boolean sync
* In case, we do need to kill the server, this param is passed through
* to kill to determine whether it's a sync operation.
*/
async stop(sync) {
async stop() {
if (!this.didRunInitially) {
return; // We didn't start the server, nothing to do
}
await this.kill(sync);
await this.kill();
// Make sure the ADB server stops listening because kill() above doesn't
// mean that the ADB server stops, it means that 'adb kill-server' command
// just finished, so that it's possible that the ADB server is still alive.
@ -155,84 +141,46 @@ const ADB = {
/**
* Kill the ADB server. We do this by running ADB again, passing it
* the "kill-server" argument.
*
* @param {Boolean} sync
* Whether or not to kill the server synchronously. In general,
* this should be false. But on Windows, an add-on may fail to update
* if its copy of ADB is running when Firefox tries to update it.
* So add-ons who observe their own updates and kill the ADB server
* beforehand should do so synchronously on Windows to make sure
* the update doesn't race the killing.
*/
async kill(sync) {
async kill() {
const process = Cc["@mozilla.org/process/util;1"]
.createInstance(Ci.nsIProcess);
const adbFile = await this.adbFilePromise;
process.init(adbFile);
// Hide command prompt window on Windows
try {
// startHidden is 55+
process.startHidden = true;
// noShell attribute is 58+
process.noShell = true;
} catch (e) {
}
process.startHidden = true;
process.noShell = true;
const params = ["kill-server"];
if (sync) {
process.run(true, params, params.length);
console.log("adb kill-server: " + process.exitValue);
this.ready = false;
this.didRunInitially = false;
} else {
const self = this;
process.runAsync(params, params.length, {
observe(subject, topic, data) {
switch (topic) {
case "process-finished":
console.log("adb kill-server: " + process.exitValue);
Services.obs.notifyObservers(null, "adb-killed");
self.ready = false;
self.didRunInitially = false;
break;
case "process-failed":
console.log("adb kill-server failure: " + process.exitValue);
// It's hard to say whether or not ADB is ready at this point,
// but it seems safer to assume that it isn't, so code that wants
// to use it later will try to restart it.
Services.obs.notifyObservers(null, "adb-killed");
self.ready = false;
self.didRunInitially = false;
break;
}
}
}, false);
}
process.run(true, params, params.length);
dumpn("adb kill-server: " + process.exitValue);
this.ready = false;
this.didRunInitially = false;
},
// Start tracking devices connecting and disconnecting from the host.
// We can't reuse runCommand here because we keep the socket alive.
// @return The socket used.
trackDevices() {
console.log("trackDevices");
dumpn("trackDevices");
const socket = client.connect();
let waitForFirst = true;
const devices = {};
socket.s.onopen = function() {
console.log("trackDevices onopen");
dumpn("trackDevices onopen");
Services.obs.notifyObservers(null, "adb-track-devices-start");
const req = client.createRequest("host:track-devices");
socket.send(req);
};
socket.s.onerror = function(event) {
console.log("trackDevices onerror: " + event);
dumpn("trackDevices onerror: " + event);
Services.obs.notifyObservers(null, "adb-track-devices-stop");
};
socket.s.onclose = function() {
console.log("trackDevices onclose");
dumpn("trackDevices onclose");
// Report all devices as disconnected
for (const dev in devices) {
@ -256,11 +204,11 @@ const ADB = {
};
socket.s.ondata = function(event) {
console.log("trackDevices ondata");
dumpn("trackDevices ondata");
const data = event.data;
console.log("length=" + data.byteLength);
dumpn("length=" + data.byteLength);
const dec = new TextDecoder();
console.log(dec.decode(new Uint8Array(data)).trim());
dumpn(dec.decode(new Uint8Array(data)).trim());
// check the OKAY or FAIL on first packet.
if (waitForFirst) {
@ -308,7 +256,7 @@ const ADB = {
// Sends back an array of device names.
listDevices() {
console.log("listDevices");
dumpn("listDevices");
return this.runCommand("host:devices").then(
function onSuccess(data) {
@ -328,7 +276,7 @@ const ADB = {
// sends adb forward localPort devicePort
forwardPort(localPort, devicePort) {
console.log("forwardPort " + localPort + " -- " + devicePort);
dumpn("forwardPort " + localPort + " -- " + devicePort);
// <host-prefix>:forward:<local>;<remote>
return this.runCommand("host:forward:" + localPort + ";" + devicePort)
@ -337,632 +285,139 @@ const ADB = {
});
},
// pulls a file from the device.
// send "host:transport-any" why??
// if !OKAY, return
// send "sync:"
// if !OKAY, return
// send STAT + hex4(path.length) + path
// recv STAT + 12 bytes (3 x 32 bits: mode, size, time)
// send RECV + hex4(path.length) + path
// while(needs data):
// recv DATA + hex4 + data
// recv DONE + hex4(0)
// send QUIT + hex4(0)
pull(from, dest) {
const deferred = PromiseUtils.defer();
let state;
let fileData = null;
let currentPos = 0;
let chunkSize = 0;
let pkgData;
const headerArray = new Uint32Array(2);
let currentHeaderLength = 0;
const encoder = new TextEncoder();
let infoLengthPacket;
console.log("pulling " + from + " -> " + dest);
const shutdown = function() {
console.log("pull shutdown");
socket.close();
deferred.reject("BAD_RESPONSE");
};
// extract chunk data header info. to headerArray.
const extractChunkDataHeader = function(data) {
const tmpArray = new Uint8Array(headerArray.buffer);
for (let i = 0; i < 8 - currentHeaderLength; i++) {
tmpArray[currentHeaderLength + i] = data[i];
}
};
// chunk data header is 8 bytes length,
// the first 4 bytes: hex4("DATA"), and
// the second 4 bytes: hex4(chunk size)
const checkChunkDataHeader = function(data) {
if (data.length + currentHeaderLength >= 8) {
extractChunkDataHeader(data);
if (headerArray[0] != DATA) {
shutdown();
return false;
}
// remove header info. from socket package data
pkgData = data.subarray(8 - currentHeaderLength, data.length);
chunkSize = headerArray[1];
currentHeaderLength = 0;
return true;
}
// If chunk data header info. is separated into more than one
// socket package, keep partial header info. in headerArray.
const tmpArray = new Uint8Array(headerArray.buffer);
for (let i = 0; i < data.length; i++) {
tmpArray[currentHeaderLength + i] = data[i];
}
currentHeaderLength += data.length;
return true;
};
// The last remaining package data contains 8 bytes,
// they are "DONE(0x454e4f44)" and 0x0000.
const checkDone = function(data) {
if (data.length != 8) {
return false;
}
const doneFlagArray = new Uint32Array(1);
const tmpArray = new Uint8Array(doneFlagArray.buffer);
for (let i = 0; i < 4; i++) {
tmpArray[i] = data[i];
}
// Check DONE flag
if (doneFlagArray[0] == DONE) {
return true;
}
return false;
};
const runFSM = function runFSM(data) {
console.log("runFSM " + state);
let req;
switch (state) {
case "start":
state = "send-transport";
runFSM();
break;
case "send-transport":
req = client.createRequest("host:transport-any");
socket.send(req);
state = "wait-transport";
break;
case "wait-transport":
if (!client.checkResponse(data, OKAY)) {
shutdown();
return;
}
console.log("transport: OK");
state = "send-sync";
runFSM();
break;
case "send-sync":
req = client.createRequest("sync:");
socket.send(req);
state = "wait-sync";
break;
case "wait-sync":
if (!client.checkResponse(data, OKAY)) {
shutdown();
return;
}
console.log("sync: OK");
state = "send-recv";
runFSM();
break;
case "send-recv":
infoLengthPacket = new Uint32Array(1);
infoLengthPacket[0] = from.length;
socket.send(encoder.encode("RECV"));
socket.send(infoLengthPacket);
socket.send(encoder.encode(from));
state = "wait-recv";
break;
case "wait-recv":
// After sending "RECV" command, adb server will send chunks data back,
// Handle every single socket package here.
// Note: One socket package maybe contain many chunks, and often
// partial chunk at the end.
pkgData = new Uint8Array(client.getBuffer(data));
// Handle all data in a single socket package.
while (pkgData.length > 0) {
if (chunkSize == 0 && checkDone(pkgData)) {
OS.File.writeAtomic(dest, fileData, {}).then(
function onSuccess(number) {
console.log(number);
deferred.resolve("SUCCESS");
},
function onFailure(reason) {
console.log(reason);
deferred.reject("CANT_ACCESS_FILE");
}
);
state = "send-quit";
runFSM();
return;
}
if (chunkSize == 0 && !checkChunkDataHeader(pkgData)) {
shutdown();
return;
}
// handle full chunk
if (chunkSize > 0 && pkgData.length >= chunkSize) {
const chunkData = pkgData.subarray(0, chunkSize);
const tmpData = new Uint8Array(currentPos + chunkSize);
if (fileData) {
tmpData.set(fileData, 0);
}
tmpData.set(chunkData, currentPos);
fileData = tmpData;
pkgData = pkgData.subarray(chunkSize, pkgData.length);
currentPos += chunkSize;
chunkSize = 0;
}
// handle partial chunk at the end of socket package
if (chunkSize > 0 && pkgData.length > 0 && pkgData.length < chunkSize) {
const tmpData = new Uint8Array(currentPos + pkgData.length);
if (fileData) {
tmpData.set(fileData, 0);
}
tmpData.set(pkgData, currentPos);
fileData = tmpData;
currentPos += pkgData.length;
chunkSize -= pkgData.length;
break; // Break while loop.
}
}
break;
case "send-quit":
infoLengthPacket = new Uint32Array(1);
infoLengthPacket[0] = 0;
socket.send(encoder.encode("QUIT"));
socket.send(infoLengthPacket);
state = "end";
runFSM();
break;
case "end":
socket.close();
break;
default:
console.log("pull Unexpected State: " + state);
deferred.reject("UNEXPECTED_STATE");
}
};
const setupSocket = function() {
socket.s.onerror = function(event) {
console.log("pull onerror");
deferred.reject("SOCKET_ERROR");
};
socket.s.onopen = function(event) {
console.log("pull onopen");
state = "start";
runFSM();
};
socket.s.onclose = function(event) {
console.log("pull onclose");
};
socket.s.ondata = function(event) {
console.log("pull ondata:");
runFSM(event.data);
};
};
const socket = client.connect();
setupSocket();
return deferred.promise;
},
// pushes a file to the device.
// from and dest are full paths.
// XXX we should STAT the remote path before sending.
push(from, dest) {
const deferred = PromiseUtils.defer();
let socket;
let state;
let fileSize;
let fileData;
let remaining;
let currentPos = 0;
let fileTime;
console.log("pushing " + from + " -> " + dest);
const shutdown = function() {
console.log("push shutdown");
socket.close();
deferred.reject("BAD_RESPONSE");
};
const runFSM = function runFSM(data) {
console.log("runFSM " + state);
let req;
switch (state) {
case "start":
state = "send-transport";
runFSM();
break;
case "send-transport":
req = client.createRequest("host:transport-any");
socket.send(req);
state = "wait-transport";
break;
case "wait-transport":
if (!client.checkResponse(data, OKAY)) {
shutdown();
return;
}
console.log("transport: OK");
state = "send-sync";
runFSM();
break;
case "send-sync":
req = client.createRequest("sync:");
socket.send(req);
state = "wait-sync";
break;
case "wait-sync":
if (!client.checkResponse(data, OKAY)) {
shutdown();
return;
}
console.log("sync: OK");
state = "send-send";
runFSM();
break;
case "send-send":
// need to send SEND + length($dest,$fileMode)
// $fileMode is not the octal one there.
const encoder = new TextEncoder();
const infoLengthPacket = new Uint32Array(1), info = dest + ",33204";
infoLengthPacket[0] = info.length;
socket.send(encoder.encode("SEND"));
socket.send(infoLengthPacket);
socket.send(encoder.encode(info));
// now sending file data.
while (remaining > 0) {
const toSend = remaining > 65536 ? 65536 : remaining;
console.log("Sending " + toSend + " bytes");
const dataLengthPacket = new Uint32Array(1);
// We have to create a new ArrayBuffer for the fileData slice
// because nsIDOMTCPSocket (or ArrayBufferInputStream) chokes on
// reused buffers, even when we don't modify their contents.
const dataPacket = new Uint8Array(new ArrayBuffer(toSend));
dataPacket.set(new Uint8Array(fileData.buffer, currentPos, toSend));
dataLengthPacket[0] = toSend;
socket.send(encoder.encode("DATA"));
socket.send(dataLengthPacket);
socket.send(dataPacket);
currentPos += toSend;
remaining -= toSend;
}
// Ending up with DONE + mtime (wtf???)
const fileTimePacket = new Uint32Array(1);
fileTimePacket[0] = fileTime;
socket.send(encoder.encode("DONE"));
socket.send(fileTimePacket);
state = "wait-done";
break;
case "wait-done":
if (!client.checkResponse(data, OKAY)) {
shutdown();
return;
}
console.log("DONE: OK");
state = "end";
runFSM();
break;
case "end":
socket.close();
deferred.resolve("SUCCESS");
break;
default:
console.log("push Unexpected State: " + state);
deferred.reject("UNEXPECTED_STATE");
}
};
const setupSocket = function() {
socket.s.onerror = function(event) {
console.log("push onerror");
deferred.reject("SOCKET_ERROR");
};
socket.s.onopen = function(event) {
console.log("push onopen");
state = "start";
runFSM();
};
socket.s.onclose = function(event) {
console.log("push onclose");
};
socket.s.ondata = function(event) {
console.log("push ondata");
runFSM(event.data);
};
};
// Stat the file, get its size.
OS.File.stat(from).then(
function onSuccess(stat) {
if (stat.isDir) {
// The path represents a directory
deferred.reject("CANT_PUSH_DIR");
} else {
// The path represents a file, not a directory
fileSize = stat.size;
// We want seconds since epoch
fileTime = stat.lastModificationDate.getTime() / 1000;
remaining = fileSize;
console.log(from + " size is " + fileSize);
const readPromise = OS.File.read(from);
readPromise.then(
function readSuccess(data) {
fileData = data;
socket = client.connect();
setupSocket();
},
function readError() {
deferred.reject("READ_FAILED");
}
);
}
},
function onFailure(reason) {
console.log(reason);
deferred.reject("CANT_ACCESS_FILE");
}
);
return deferred.promise;
},
// Run a shell command
shell(command) {
const deferred = PromiseUtils.defer();
async shell(command) {
let state;
let stdout = "";
console.log("shell " + command);
dumpn("shell " + command);
const shutdown = function() {
console.log("shell shutdown");
socket.close();
deferred.reject("BAD_RESPONSE");
};
return new Promise((resolve, reject) => {
const shutdown = function() {
dumpn("shell shutdown");
socket.close();
reject("BAD_RESPONSE");
};
const runFSM = function runFSM(data) {
console.log("runFSM " + state);
let req;
let ignoreResponseCode = false;
switch (state) {
case "start":
state = "send-transport";
runFSM();
break;
case "send-transport":
req = client.createRequest("host:transport-any");
socket.send(req);
state = "wait-transport";
break;
case "wait-transport":
if (!client.checkResponse(data, OKAY)) {
shutdown();
return;
}
state = "send-shell";
runFSM();
break;
case "send-shell":
req = client.createRequest("shell:" + command);
socket.send(req);
state = "rec-shell";
break;
case "rec-shell":
if (!client.checkResponse(data, OKAY)) {
shutdown();
return;
}
state = "decode-shell";
if (client.getBuffer(data).byteLength == 4) {
const runFSM = function runFSM(data) {
dumpn("runFSM " + state);
let req;
let ignoreResponseCode = false;
switch (state) {
case "start":
state = "send-transport";
runFSM();
break;
}
ignoreResponseCode = true;
// eslint-disable-next-lined no-fallthrough
case "decode-shell":
const decoder = new TextDecoder();
const text = new Uint8Array(client.getBuffer(data),
ignoreResponseCode ? 4 : 0);
stdout += decoder.decode(text);
break;
default:
console.log("shell Unexpected State: " + state);
deferred.reject("UNEXPECTED_STATE");
}
};
case "send-transport":
req = client.createRequest("host:transport-any");
socket.send(req);
state = "wait-transport";
break;
case "wait-transport":
if (!client.checkResponse(data, OKAY)) {
shutdown();
return;
}
state = "send-shell";
runFSM();
break;
case "send-shell":
req = client.createRequest("shell:" + command);
socket.send(req);
state = "rec-shell";
break;
case "rec-shell":
if (!client.checkResponse(data, OKAY)) {
shutdown();
return;
}
state = "decode-shell";
if (client.getBuffer(data).byteLength == 4) {
break;
}
ignoreResponseCode = true;
// eslint-disable-next-lined no-fallthrough
case "decode-shell":
const decoder = new TextDecoder();
const text = new Uint8Array(client.getBuffer(data),
ignoreResponseCode ? 4 : 0);
stdout += decoder.decode(text);
break;
default:
dumpn("shell Unexpected State: " + state);
reject("UNEXPECTED_STATE");
}
};
const socket = client.connect();
socket.s.onerror = function(event) {
console.log("shell onerror");
deferred.reject("SOCKET_ERROR");
};
const socket = client.connect();
socket.s.onerror = function(event) {
dumpn("shell onerror");
reject("SOCKET_ERROR");
};
socket.s.onopen = function(event) {
console.log("shell onopen");
state = "start";
runFSM();
};
socket.s.onopen = function(event) {
dumpn("shell onopen");
state = "start";
runFSM();
};
socket.s.onclose = function(event) {
deferred.resolve(stdout);
console.log("shell onclose");
};
socket.s.onclose = function(event) {
resolve(stdout);
dumpn("shell onclose");
};
socket.s.ondata = function(event) {
console.log("shell ondata");
runFSM(event.data);
};
return deferred.promise;
},
reboot() {
return this.shell("reboot");
},
rebootRecovery() {
return this.shell("reboot recovery");
},
rebootBootloader() {
return this.shell("reboot bootloader");
},
root() {
const deferred = PromiseUtils.defer();
let state;
console.log("root");
const shutdown = function() {
console.log("root shutdown");
socket.close();
deferred.reject("BAD_RESPONSE");
};
const runFSM = function runFSM(data) {
console.log("runFSM " + state);
let req;
switch (state) {
case "start":
state = "send-transport";
runFSM();
break;
case "send-transport":
req = client.createRequest("host:transport-any");
socket.send(req);
state = "wait-transport";
break;
case "wait-transport":
if (!client.checkResponse(data, OKAY)) {
shutdown();
return;
}
state = "send-root";
runFSM();
break;
case "send-root":
req = client.createRequest("root:");
socket.send(req);
state = "rec-root";
break;
case "rec-root":
// Nothing to do
break;
default:
console.log("root Unexpected State: " + state);
deferred.reject("UNEXPECTED_STATE");
}
};
const socket = client.connect();
socket.s.onerror = function(event) {
console.log("root onerror");
deferred.reject("SOCKET_ERROR");
};
socket.s.onopen = function(event) {
console.log("root onopen");
state = "start";
runFSM();
};
socket.s.onclose = function(event) {
deferred.resolve();
console.log("root onclose");
};
socket.s.ondata = function(event) {
console.log("root ondata");
runFSM(event.data);
};
return deferred.promise;
socket.s.ondata = function(event) {
dumpn("shell ondata");
runFSM(event.data);
};
});
},
// Asynchronously runs an adb command.
// @param command The command as documented in
// http://androidxref.com/4.0.4/xref/system/core/adb/SERVICES.TXT
runCommand(command) {
console.log("runCommand " + command);
const deferred = PromiseUtils.defer();
if (!this.ready) {
setTimeout(function() {
deferred.reject("ADB_NOT_READY");
});
return deferred.promise;
}
const socket = client.connect();
socket.s.onopen = function() {
console.log("runCommand onopen");
const req = client.createRequest(command);
socket.send(req);
};
socket.s.onerror = function() {
console.log("runCommand onerror");
deferred.reject("NETWORK_ERROR");
};
socket.s.onclose = function() {
console.log("runCommand onclose");
};
socket.s.ondata = function(event) {
console.log("runCommand ondata");
const data = event.data;
const packet = client.unpackPacket(data, false);
if (!client.checkResponse(data, OKAY)) {
socket.close();
console.log("Error: " + packet.data);
deferred.reject("PROTOCOL_ERROR");
dumpn("runCommand " + command);
return new Promise((resolve, reject) => {
if (!this.ready) {
setTimeout(function() {
reject("ADB_NOT_READY");
});
return;
}
deferred.resolve(packet.data);
};
const socket = client.connect();
return deferred.promise;
socket.s.onopen = function() {
dumpn("runCommand onopen");
const req = client.createRequest(command);
socket.send(req);
};
socket.s.onerror = function() {
dumpn("runCommand onerror");
reject("NETWORK_ERROR");
};
socket.s.onclose = function() {
dumpn("runCommand onclose");
};
socket.s.ondata = function(event) {
dumpn("runCommand ondata");
const data = event.data;
const packet = client.unpackPacket(data, false);
if (!client.checkResponse(data, OKAY)) {
socket.close();
dumpn("Error: " + packet.data);
reject("PROTOCOL_ERROR");
return;
}
resolve(packet.data);
};
});
}
};

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

@ -34,6 +34,7 @@ const ADB_JSON = {
]
}
};
let extension_version = 1.0;
ExtensionTestUtils.init(this);
@ -65,7 +66,7 @@ add_task(async function testAdbIsNotRunningInitially() {
add_task(async function testNoAdbExtension() {
const extension = ExtensionTestUtils.loadExtension({
manifest: {
version: "1.0",
version: (extension_version++).toString(),
applications: {
gecko: { id: "not-adb@mozilla.org" }
}
@ -83,7 +84,7 @@ add_task(async function testNoAdbExtension() {
add_task(async function testNoAdbJSON() {
const extension = ExtensionTestUtils.loadExtension({
manifest: {
version: "1.0",
version: (extension_version++).toString(),
applications: {
// The extension id here and in later test cases should match the
// corresponding prefrece value.
@ -105,7 +106,7 @@ add_task({
}, async function testNoTargetBinaries() {
const extension = ExtensionTestUtils.loadExtension({
manifest: {
version: "1.0",
version: (extension_version++).toString(),
applications: {
gecko: { id: "adb@mozilla.org" }
}
@ -126,7 +127,7 @@ add_task({
add_task(async function testExtract() {
const extension = ExtensionTestUtils.loadExtension({
manifest: {
version: "1.0",
version: (extension_version++).toString(),
applications: {
gecko: { id: "adb@mozilla.org" }
}
@ -155,7 +156,7 @@ add_task({
}, async function testStartAndStop() {
const extension = ExtensionTestUtils.loadExtension({
manifest: {
version: "1.0",
version: (extension_version++).toString(),
applications: {
gecko: { id: "adb@mozilla.org" }
}
@ -178,7 +179,7 @@ add_task({
ok(await check(), "adb is now running");
await ADB.stop(true /* sync */);
await ADB.stop();
ok(!ADB.ready);
await extension.unload();
@ -189,7 +190,7 @@ add_task({
}, async function testTrackDevices() {
const extension = ExtensionTestUtils.loadExtension({
manifest: {
version: "1.0",
version: (extension_version++).toString(),
applications: {
gecko: { id: "adb@mozilla.org" }
}
@ -221,7 +222,7 @@ add_task({
equal(receivedDeviceId, "1234567890");
await ADB.stop(true /* sync */);
await ADB.stop();
ok(!ADB.ready);
await extension.unload();

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

@ -59,6 +59,9 @@ pref("devtools.remote.wifi.scan", true);
// Client must complete TLS handshake within this window (ms)
pref("devtools.remote.tls-handshake-timeout", 10000);
// The extension ID for devtools-adb-extension
pref("devtools.remote.adb.extensionID", "adb@mozilla.org");
// URL of the remote JSON catalog used for device simulation
pref("devtools.devices.url", "https://code.cdn.mozilla.net/devices/devices.json");

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

@ -1429,26 +1429,28 @@ MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register
Label done;
Label notPointerEqual;
// Fast path for identical strings.
// If operands point to the same instance, the strings are trivially equal.
branchPtr(Assembler::NotEqual, left, right, &notPointerEqual);
move32(Imm32(op == JSOP_EQ || op == JSOP_STRICTEQ), result);
jump(&done);
bind(&notPointerEqual);
Label notAtom;
// Optimize the equality operation to a pointer compare for two atoms.
Label leftIsNotAtom;
Label setNotEqualResult;
// Atoms cannot be equal to each other if they point to different strings.
Imm32 nonAtomBit(JSString::NON_ATOM_BIT);
branchTest32(Assembler::NonZero, Address(left, JSString::offsetOfFlags()), nonAtomBit, &notAtom);
branchTest32(Assembler::NonZero, Address(right, JSString::offsetOfFlags()), nonAtomBit, &notAtom);
branchTest32(Assembler::NonZero, Address(left, JSString::offsetOfFlags()), nonAtomBit,
&leftIsNotAtom);
branchTest32(Assembler::Zero, Address(right, JSString::offsetOfFlags()), nonAtomBit,
&setNotEqualResult);
cmpPtrSet(JSOpToCondition(MCompare::Compare_String, op), left, right, result);
jump(&done);
bind(&notAtom);
bind(&leftIsNotAtom);
// Strings of different length can never be equal.
loadStringLength(left, result);
branch32(Assembler::Equal, Address(right, JSString::offsetOfLength()), result, fail);
bind(&setNotEqualResult);
move32(Imm32(op == JSOP_NE || op == JSOP_STRICTNE), result);
bind(&done);

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

@ -15,6 +15,7 @@
# include "dlfcn.h"
#endif
#include <stdio.h>
#include <string.h>
#include "mozilla/Assertions.h"
#include "mozilla/Sprintf.h"

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

@ -9,7 +9,7 @@ ac_add_options --enable-debug
# advertise a bad API level. This may confuse people. As an example, please look at bug 1384482.
# If you think you can't handle the whole set of changes, please reach out to the Release
# Engineering team.
ac_add_options --target=i386-linux-android
ac_add_options --target=i686-linux-android
ac_add_options --with-android-min-sdk=16
export MOZILLA_OFFICIAL=1

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

@ -5,7 +5,7 @@
# advertise a bad API level. This may confuse people. As an example, please look at bug 1384482.
# If you think you can't handle the whole set of changes, please reach out to the Release
# Engineering team.
ac_add_options --target=i386-linux-android
ac_add_options --target=i686-linux-android
ac_add_options --with-android-min-sdk=16
ac_add_options --with-branding=mobile/android/branding/nightly

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

@ -14,7 +14,7 @@ import sys
# We need the NDK version in multiple different places, and it's inconvenient
# to pass down the NDK version to all relevant places, so we have this global
# variable.
NDK_VERSION = 'r15c'
NDK_VERSION = 'r17b'
ANDROID_NDK_EXISTS = '''
Looks like you have the Android NDK installed at:

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

@ -11,10 +11,10 @@ import subprocess
import glob
from mozboot.base import BaseBootstrapper
from mozboot.linux_common import StyloInstall
from mozboot.linux_common import NodeInstall, StyloInstall
class ArchlinuxBootstrapper(StyloInstall, BaseBootstrapper):
class ArchlinuxBootstrapper(NodeInstall, StyloInstall, BaseBootstrapper):
'''Archlinux experimental bootstrapper.'''
SYSTEM_PACKAGES = [

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

@ -157,9 +157,10 @@ MODERN_RUST_VERSION = LooseVersion('1.28.0')
class BaseBootstrapper(object):
"""Base class for system bootstrappers."""
def __init__(self, no_interactive=False):
def __init__(self, no_interactive=False, no_system_changes=False):
self.package_manager_updated = False
self.no_interactive = no_interactive
self.no_system_changes = no_system_changes
self.state_dir = None
def install_system_packages(self):
@ -260,6 +261,13 @@ class BaseBootstrapper(object):
'%s does not yet implement ensure_stylo_packages()'
% __name__)
def ensure_node_packages(self, state_dir, checkout_root):
'''
Install any necessary packages needed to supply NodeJS'''
raise NotImplementedError(
'%s does not yet implement ensure_node_packages()'
% __name__)
def install_toolchain_artifact(self, state_dir, checkout_root, toolchain_job):
mach_binary = os.path.join(checkout_root, 'mach')
mach_binary = os.path.abspath(mach_binary)

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

@ -75,19 +75,19 @@ Would you like to create this directory?
Your choice: '''
STYLO_DIRECTORY_MESSAGE = '''
Stylo packages require a directory to store shared, persistent state.
On this machine, that directory is:
STYLO_NODEJS_DIRECTORY_MESSAGE = '''
Stylo and NodeJS packages require a directory to store shared, persistent
state. On this machine, that directory is:
{statedir}
Please restart bootstrap and create that directory when prompted.
'''
STYLO_REQUIRES_CLONE = '''
Installing Stylo packages requires a checkout of mozilla-central. Once you
have such a checkout, please re-run `./mach bootstrap` from the checkout
directory.
STYLE_NODEJS_REQUIRES_CLONE = '''
Installing Stylo and NodeJS packages requires a checkout of mozilla-central.
Once you have such a checkout, please re-run `./mach bootstrap` from the
checkout directory.
'''
FINISHED = '''
@ -171,13 +171,15 @@ class Bootstrapper(object):
"""Main class that performs system bootstrap."""
def __init__(self, finished=FINISHED, choice=None, no_interactive=False,
hg_configure=False):
hg_configure=False, no_system_changes=False):
self.instance = None
self.finished = finished
self.choice = choice
self.hg_configure = hg_configure
self.no_system_changes = no_system_changes
cls = None
args = {'no_interactive': no_interactive}
args = {'no_interactive': no_interactive,
'no_system_changes': no_system_changes}
if sys.platform.startswith('linux'):
distro, version, dist_id = platform.linux_distribution()
@ -262,33 +264,12 @@ class Bootstrapper(object):
continue
return ''
def bootstrap(self):
if self.choice is None:
# Like ['1. Firefox for Desktop', '2. Firefox for Android Artifact Mode', ...].
labels = ['%s. %s' % (i + 1, name) for (i, (name, _)) in enumerate(APPLICATIONS_LIST)]
prompt = APPLICATION_CHOICE % '\n'.join(labels)
prompt_choice = self.instance.prompt_int(prompt=prompt, low=1, high=len(APPLICATIONS))
name, application = APPLICATIONS_LIST[prompt_choice-1]
elif self.choice not in APPLICATIONS.keys():
raise Exception('Please pick a valid application choice: (%s)' %
'/'.join(APPLICATIONS.keys()))
else:
name, application = APPLICATIONS[self.choice]
self.instance.install_system_packages()
# Like 'install_browser_packages' or 'install_mobile_android_packages'.
getattr(self.instance, 'install_%s_packages' % application)()
hg_installed, hg_modern = self.instance.ensure_mercurial_modern()
self.instance.ensure_python_modern()
self.instance.ensure_rust_modern()
# The state directory code is largely duplicated from mach_bootstrap.py.
# We can't easily import mach_bootstrap.py because the bootstrapper may
# run in self-contained mode and only the files in this directory will
# be available. We /could/ refactor parts of mach_bootstrap.py to be
# part of this directory to avoid the code duplication.
# The state directory code is largely duplicated from mach_bootstrap.py.
# We can't easily import mach_bootstrap.py because the bootstrapper may
# run in self-contained mode and only the files in this directory will
# be available. We /could/ refactor parts of mach_bootstrap.py to be
# part of this directory to avoid the code duplication.
def try_to_create_state_dir(self):
state_dir, _ = get_state_dir()
if not os.path.exists(state_dir):
@ -303,6 +284,73 @@ class Bootstrapper(object):
os.makedirs(state_dir, mode=0o770)
state_dir_available = os.path.exists(state_dir)
return state_dir_available, state_dir
def maybe_install_private_packages_or_exit(self, state_dir,
state_dir_available,
have_clone,
checkout_root):
# Install the clang packages needed for developing stylo, as well
# as the version of NodeJS that we currently support.
if not self.instance.no_interactive:
# The best place to install our packages is in the state directory
# we have. If the user doesn't have one, we need them to re-run
# bootstrap and create the directory.
#
# XXX Android bootstrap just assumes the existence of the state
# directory and writes the NDK into it. Should we do the same?
if not state_dir_available:
print(STYLO_NODEJS_DIRECTORY_MESSAGE.format(statedir=state_dir))
sys.exit(1)
if not have_clone:
print(STYLE_NODEJS_REQUIRES_CLONE)
sys.exit(1)
self.instance.state_dir = state_dir
self.instance.ensure_stylo_packages(state_dir, checkout_root)
self.instance.ensure_node_packages(state_dir, checkout_root)
def bootstrap(self):
if self.choice is None:
# Like ['1. Firefox for Desktop', '2. Firefox for Android Artifact Mode', ...].
labels = ['%s. %s' % (i + 1, name) for (i, (name, _)) in enumerate(APPLICATIONS_LIST)]
prompt = APPLICATION_CHOICE % '\n'.join(labels)
prompt_choice = self.instance.prompt_int(prompt=prompt, low=1, high=len(APPLICATIONS))
name, application = APPLICATIONS_LIST[prompt_choice-1]
elif self.choice not in APPLICATIONS.keys():
raise Exception('Please pick a valid application choice: (%s)' %
'/'.join(APPLICATIONS.keys()))
else:
name, application = APPLICATIONS[self.choice]
if self.instance.no_system_changes:
state_dir_available, state_dir = self.try_to_create_state_dir()
# We need to enable the loading of hgrc in case extensions are
# required to open the repo.
r = current_firefox_checkout(
check_output=self.instance.check_output,
env=self.instance._hg_cleanenv(load_hgrc=True),
hg=self.instance.which('hg'))
(checkout_type, checkout_root) = r
have_clone = bool(checkout_type)
self.maybe_install_private_packages_or_exit(state_dir,
state_dir_available,
have_clone,
checkout_root)
sys.exit(0)
self.instance.install_system_packages()
# Like 'install_browser_packages' or 'install_mobile_android_packages'.
getattr(self.instance, 'install_%s_packages' % application)()
hg_installed, hg_modern = self.instance.ensure_mercurial_modern()
self.instance.ensure_python_modern()
self.instance.ensure_rust_modern()
state_dir_available, state_dir = self.try_to_create_state_dir()
# We need to enable the loading of hgrc in case extensions are
# required to open the repo.
@ -340,24 +388,10 @@ class Bootstrapper(object):
if not have_clone:
print(SOURCE_ADVERTISE)
# Install the clang packages needed for developing stylo.
if not self.instance.no_interactive:
# The best place to install our packages is in the state directory
# we have. If the user doesn't have one, we need them to re-run
# bootstrap and create the directory.
#
# XXX Android bootstrap just assumes the existence of the state
# directory and writes the NDK into it. Should we do the same?
if not state_dir_available:
print(STYLO_DIRECTORY_MESSAGE.format(statedir=state_dir))
sys.exit(1)
if not have_clone:
print(STYLO_REQUIRES_CLONE)
sys.exit(1)
self.instance.state_dir = state_dir
self.instance.ensure_stylo_packages(state_dir, checkout_root)
self.maybe_install_private_packages_or_exit(state_dir,
state_dir_available,
have_clone,
checkout_root)
print(self.finished % name)
if not (self.instance.which('rustc') and self.instance._parse_version('rustc')

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

@ -7,10 +7,10 @@ from __future__ import absolute_import
import platform
from mozboot.base import BaseBootstrapper
from mozboot.linux_common import StyloInstall
from mozboot.linux_common import NodeInstall, StyloInstall
class CentOSFedoraBootstrapper(StyloInstall, BaseBootstrapper):
class CentOSFedoraBootstrapper(NodeInstall, StyloInstall, BaseBootstrapper):
def __init__(self, distro, version, dist_id, **kwargs):
BaseBootstrapper.__init__(self, **kwargs)

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

@ -5,7 +5,7 @@
from __future__ import absolute_import, print_function
from mozboot.base import BaseBootstrapper
from mozboot.linux_common import StyloInstall
from mozboot.linux_common import NodeInstall, StyloInstall
MERCURIAL_INSTALL_PROMPT = '''
@ -28,7 +28,7 @@ Choice:
'''.strip()
class DebianBootstrapper(StyloInstall, BaseBootstrapper):
class DebianBootstrapper(NodeInstall, StyloInstall, BaseBootstrapper):
# These are common packages for all Debian-derived distros (such as
# Ubuntu).
COMMON_PACKAGES = [

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

@ -5,10 +5,10 @@
from __future__ import absolute_import
from mozboot.base import BaseBootstrapper
from mozboot.linux_common import StyloInstall
from mozboot.linux_common import NodeInstall, StyloInstall
class GentooBootstrapper(StyloInstall, BaseBootstrapper):
class GentooBootstrapper(NodeInstall, StyloInstall, BaseBootstrapper):
def __init__(self, version, dist_id, **kwargs):
BaseBootstrapper.__init__(self, **kwargs)

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

@ -3,7 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# An easy way for distribution-specific bootstrappers to share the code
# needed to install Stylo dependencies. This class must come before
# needed to install Stylo and Node dependencies. This class must come before
# BaseBootstrapper in the inheritance list.
from __future__ import absolute_import
@ -16,3 +16,12 @@ class StyloInstall(object):
def ensure_stylo_packages(self, state_dir, checkout_root):
from mozboot import stylo
self.install_toolchain_artifact(state_dir, checkout_root, stylo.LINUX)
class NodeInstall(object):
def __init__(self, **kwargs):
pass
def ensure_node_packages(self, state_dir, checkout_root):
from mozboot import node
self.install_toolchain_artifact(state_dir, checkout_root, node.LINUX)

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

@ -25,10 +25,16 @@ class Bootstrap(object):
'instead of using the default interactive prompt.')
@CommandArgument('--no-interactive', dest='no_interactive', action='store_true',
help='Answer yes to any (Y/n) interactive prompts.')
def bootstrap(self, application_choice=None, no_interactive=False):
@CommandArgument('--no-system-changes', dest='no_system_changes',
action='store_true',
help='Only execute actions that leave the system '
'configuration alone.')
def bootstrap(self, application_choice=None, no_interactive=False, no_system_changes=False):
from mozboot.bootstrap import Bootstrapper
bootstrapper = Bootstrapper(choice=application_choice, no_interactive=no_interactive)
bootstrapper = Bootstrapper(choice=application_choice,
no_interactive=no_interactive,
no_system_changes=no_system_changes)
bootstrapper.bootstrap()

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

@ -13,8 +13,9 @@ from mozboot.base import BaseBootstrapper
class MozillaBuildBootstrapper(BaseBootstrapper):
'''Bootstrapper for MozillaBuild to install rustup.'''
def __init__(self, no_interactive=False):
BaseBootstrapper.__init__(self, no_interactive=no_interactive)
def __init__(self, no_interactive=False, no_system_changes=False):
BaseBootstrapper.__init__(self, no_interactive=no_interactive,
no_system_changes=no_system_changes)
print("mach bootstrap is not fully implemented in MozillaBuild")
def which(self, name):
@ -48,6 +49,11 @@ class MozillaBuildBootstrapper(BaseBootstrapper):
from mozboot import stylo
self.install_toolchain_artifact(state_dir, checkout_root, stylo.WINDOWS)
def ensure_node_packages(self, state_dir, checkout_root):
from mozboot import node
self.install_toolchain_artifact(
state_dir, checkout_root, node.WINDOWS)
def _update_package_manager(self):
pass

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

@ -0,0 +1,9 @@
# 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/.
from __future__ import absolute_import, print_function, unicode_literals
WINDOWS = 'win64-node'
LINUX = 'linux64-node'
OSX = 'macosx64-node'

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

@ -506,6 +506,11 @@ class OSXBootstrapper(BaseBootstrapper):
# We installed these via homebrew earlier.
pass
def ensure_node_packages(self, state_dir, checkout_root):
# XXX from necessary?
from mozboot import node
self.install_toolchain_artifact(state_dir, checkout_root, node.OSX)
def install_homebrew(self):
print(PACKAGE_MANAGER_INSTALL % ('Homebrew', 'Homebrew', 'Homebrew', 'brew'))
bootstrap = urlopen(url=HOMEBREW_BOOTSTRAP, timeout=20).read()

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

@ -75,6 +75,11 @@ class WindowsBootstrapper(BaseBootstrapper):
from mozboot import stylo
self.install_toolchain_artifact(state_dir, checkout_root, stylo.WINDOWS)
def ensure_node_packages(self, state_dir, checkout_root):
from mozboot import node
self.install_toolchain_artifact(
state_dir, checkout_root, node.WINDOWS)
def _update_package_manager(self):
self.pacman_update()