зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. a=merge on a CLOSED TREE
This commit is contained in:
Коммит
2f1ab64d7d
|
@ -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, ¬PointerEqual);
|
||||
move32(Imm32(op == JSOP_EQ || op == JSOP_STRICTEQ), result);
|
||||
jump(&done);
|
||||
|
||||
bind(¬PointerEqual);
|
||||
|
||||
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, ¬Atom);
|
||||
branchTest32(Assembler::NonZero, Address(right, JSString::offsetOfFlags()), nonAtomBit, ¬Atom);
|
||||
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(¬Atom);
|
||||
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()
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче