зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central. a=merge
This commit is contained in:
Коммит
d30168f45c
|
@ -108,3 +108,4 @@ support-files =
|
|||
support-files =
|
||||
iframe_navigation.html
|
||||
[browser_navigation_failures.js]
|
||||
[browser_secure_transport_insecure_scheme.js]
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that an insecure resource routed over a secure transport is considered
|
||||
// insecure in terms of the site identity panel. We achieve this by running an
|
||||
// HTTP-over-TLS "proxy" and having Firefox request an http:// URI over it.
|
||||
|
||||
// But first, a quick test that we don't incorrectly treat a
|
||||
// blob:https://example.com URI as secure.
|
||||
add_task(async function() {
|
||||
let uri = getRootDirectory(gTestPath).replace("chrome://mochitests/content",
|
||||
"https://example.com") + "dummy_page.html";
|
||||
await BrowserTestUtils.withNewTab(uri, async (browser) => {
|
||||
await ContentTask.spawn(browser, null, async () => {
|
||||
Cu.importGlobalProperties(["Blob", "URL"]);
|
||||
let debug = {hello: "world"};
|
||||
let blob = new Blob([JSON.stringify(debug, null, 2)], {type: "application/json"});
|
||||
let blobUri = URL.createObjectURL(blob);
|
||||
content.document.location = blobUri;
|
||||
});
|
||||
await BrowserTestUtils.browserLoaded(browser);
|
||||
let identityMode = window.document.getElementById("identity-box").className;
|
||||
is(identityMode, "unknownIdentity", "identity should be 'unknown'");
|
||||
});
|
||||
});
|
||||
|
||||
// This server pretends to be a HTTP over TLS proxy. It isn't really, but this
|
||||
// is sufficient for the purposes of this test.
|
||||
function startServer(cert) {
|
||||
let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"]
|
||||
.createInstance(Ci.nsITLSServerSocket);
|
||||
tlsServer.init(-1, true, -1);
|
||||
tlsServer.serverCert = cert;
|
||||
|
||||
let input, output;
|
||||
|
||||
let listener = {
|
||||
onSocketAccepted(socket, transport) {
|
||||
let connectionInfo = transport.securityInfo
|
||||
.QueryInterface(Ci.nsITLSServerConnectionInfo);
|
||||
connectionInfo.setSecurityObserver(listener);
|
||||
input = transport.openInputStream(0, 0, 0);
|
||||
output = transport.openOutputStream(0, 0, 0);
|
||||
},
|
||||
|
||||
onHandshakeDone(socket, status) {
|
||||
input.asyncWait({
|
||||
onInputStreamReady(readyInput) {
|
||||
try {
|
||||
let request = NetUtil.readInputStreamToString(readyInput,
|
||||
readyInput.available());
|
||||
ok(request.startsWith("GET ") && request.includes("HTTP/1.1"),
|
||||
"expecting an HTTP/1.1 GET request");
|
||||
let response = "HTTP/1.1 200 OK\r\nContent-Type:text/plain\r\n" +
|
||||
"Connection:Close\r\nContent-Length:2\r\n\r\nOK";
|
||||
output.write(response, response.length);
|
||||
} catch (e) {
|
||||
info(e);
|
||||
}
|
||||
},
|
||||
}, 0, 0, Services.tm.currentThread);
|
||||
},
|
||||
|
||||
onStopListening() {
|
||||
input.close();
|
||||
output.close();
|
||||
},
|
||||
};
|
||||
|
||||
tlsServer.setSessionTickets(false);
|
||||
tlsServer.asyncListen(listener);
|
||||
|
||||
return tlsServer;
|
||||
}
|
||||
|
||||
add_task(async function() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
// This test fails on some platforms if we leave IPv6 enabled.
|
||||
set: [["network.dns.disableIPv6", true]],
|
||||
});
|
||||
|
||||
let certService = Cc["@mozilla.org/security/local-cert-service;1"]
|
||||
.getService(Ci.nsILocalCertService);
|
||||
let certOverrideService = Cc["@mozilla.org/security/certoverride;1"]
|
||||
.getService(Ci.nsICertOverrideService);
|
||||
|
||||
let cert = await new Promise((resolve, reject) => {
|
||||
certService.getOrCreateCert("http-over-https-proxy", {
|
||||
handleCert(c, rv) {
|
||||
if (!Components.isSuccessCode(rv)) {
|
||||
reject(rv);
|
||||
return;
|
||||
}
|
||||
resolve(c);
|
||||
},
|
||||
});
|
||||
});
|
||||
// Start the proxy and configure Firefox to trust its certificate.
|
||||
let server = startServer(cert);
|
||||
let overrideBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED |
|
||||
Ci.nsICertOverrideService.ERROR_MISMATCH;
|
||||
certOverrideService.rememberValidityOverride("localhost", server.port, cert,
|
||||
overrideBits, true);
|
||||
// Configure Firefox to use the proxy.
|
||||
let systemProxySettings = {
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsISystemProxySettings]),
|
||||
mainThreadOnly: true,
|
||||
PACURI: null,
|
||||
getProxyForURI: (aSpec, aScheme, aHost, aPort) => {
|
||||
return `HTTPS localhost:${server.port}`;
|
||||
},
|
||||
};
|
||||
let oldProxyType = Services.prefs.getIntPref("network.proxy.type");
|
||||
Services.prefs.setIntPref("network.proxy.type", Ci.nsIProtocolProxyService.PROXYCONFIG_SYSTEM);
|
||||
let { MockRegistrar } = ChromeUtils.import("resource://testing-common/MockRegistrar.jsm", {});
|
||||
let mockProxy = MockRegistrar.register("@mozilla.org/system-proxy-settings;1",
|
||||
systemProxySettings);
|
||||
// Register cleanup to undo the configuration changes we've made.
|
||||
registerCleanupFunction(() => {
|
||||
certOverrideService.clearValidityOverride("localhost", server.port);
|
||||
Services.prefs.setIntPref("network.proxy.type", oldProxyType);
|
||||
MockRegistrar.unregister(mockProxy);
|
||||
server.close();
|
||||
});
|
||||
|
||||
// Navigate to 'http://example.com'. Our proxy settings will route this via
|
||||
// the "proxy" we just started. Even though our connection to the proxy is
|
||||
// secure, in a real situation the connection from the proxy to
|
||||
// http://example.com won't be secure, so we treat it as not secure.
|
||||
await BrowserTestUtils.withNewTab("http://example.com/", async (browser) => {
|
||||
let identityMode = window.document.getElementById("identity-box").className;
|
||||
is(identityMode, "unknownIdentity", "identity should be 'unknown'");
|
||||
});
|
||||
});
|
|
@ -7,15 +7,11 @@
|
|||
const { PureComponent } = require("devtools/client/shared/vendor/react");
|
||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
const { translateNodeFrontToGrip } = require("devtools/client/inspector/shared/utils");
|
||||
|
||||
loader.lazyGetter(this, "Rep", function() {
|
||||
return require("devtools/client/shared/components/reps/reps").REPS.Rep;
|
||||
});
|
||||
loader.lazyGetter(this, "MODE", function() {
|
||||
return require("devtools/client/shared/components/reps/reps").MODE;
|
||||
});
|
||||
|
||||
loader.lazyRequireGetter(this, "translateNodeFrontToGrip", "devtools/client/inspector/shared/utils", true);
|
||||
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
|
||||
const { Rep } = REPS;
|
||||
const ElementNode = REPS.ElementNode;
|
||||
|
||||
const Types = require("../types");
|
||||
|
||||
|
@ -23,22 +19,35 @@ class FlexItem extends PureComponent {
|
|||
static get propTypes() {
|
||||
return {
|
||||
flexItem: PropTypes.shape(Types.flexItem).isRequired,
|
||||
onHideBoxModelHighlighter: PropTypes.func.isRequired,
|
||||
onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
|
||||
setSelectedNode: PropTypes.func.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { nodeFront } = this.props.flexItem;
|
||||
const {
|
||||
flexItem,
|
||||
onHideBoxModelHighlighter,
|
||||
onShowBoxModelHighlighterForNode,
|
||||
setSelectedNode,
|
||||
} = this.props;
|
||||
const { nodeFront } = flexItem;
|
||||
|
||||
return (
|
||||
dom.li({},
|
||||
dom.button(
|
||||
{
|
||||
className: "devtools-button devtools-monospace",
|
||||
onClick: () => this.props.setSelectedNode(nodeFront),
|
||||
onClick: () => {
|
||||
setSelectedNode(nodeFront);
|
||||
onHideBoxModelHighlighter();
|
||||
},
|
||||
onMouseOut: () => onHideBoxModelHighlighter(),
|
||||
onMouseOver: () => onShowBoxModelHighlighterForNode(nodeFront),
|
||||
},
|
||||
Rep({
|
||||
defaultRep: Rep.ElementNode,
|
||||
defaultRep: ElementNode,
|
||||
mode: MODE.TINY,
|
||||
object: translateNodeFrontToGrip(nodeFront),
|
||||
})
|
||||
|
|
|
@ -16,6 +16,8 @@ class FlexItemList extends PureComponent {
|
|||
static get propTypes() {
|
||||
return {
|
||||
flexItems: PropTypes.arrayOf(PropTypes.shape(Types.flexItem)).isRequired,
|
||||
onHideBoxModelHighlighter: PropTypes.func.isRequired,
|
||||
onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
|
||||
setSelectedNode: PropTypes.func.isRequired,
|
||||
};
|
||||
}
|
||||
|
@ -23,6 +25,8 @@ class FlexItemList extends PureComponent {
|
|||
render() {
|
||||
const {
|
||||
flexItems,
|
||||
onHideBoxModelHighlighter,
|
||||
onShowBoxModelHighlighterForNode,
|
||||
setSelectedNode,
|
||||
} = this.props;
|
||||
|
||||
|
@ -32,6 +36,8 @@ class FlexItemList extends PureComponent {
|
|||
flexItems.map(flexItem => FlexItem({
|
||||
key: flexItem.actorID,
|
||||
flexItem,
|
||||
onHideBoxModelHighlighter,
|
||||
onShowBoxModelHighlighterForNode,
|
||||
setSelectedNode,
|
||||
}))
|
||||
)
|
||||
|
|
|
@ -47,23 +47,23 @@ class Flexbox extends PureComponent {
|
|||
}
|
||||
|
||||
renderFlexContainerProperties() {
|
||||
// Don't show the flex container properties for the parent flex container of the
|
||||
// selected element.
|
||||
if (this.props.flexContainer.isFlexItemContainer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return FlexContainerProperties({
|
||||
properties: this.props.flexContainer.properties,
|
||||
});
|
||||
}
|
||||
|
||||
renderFlexItemList() {
|
||||
const { setSelectedNode } = this.props;
|
||||
const {
|
||||
onHideBoxModelHighlighter,
|
||||
onShowBoxModelHighlighterForNode,
|
||||
setSelectedNode,
|
||||
} = this.props;
|
||||
const { flexItems } = this.props.flexContainer;
|
||||
|
||||
return FlexItemList({
|
||||
flexItems,
|
||||
onHideBoxModelHighlighter,
|
||||
onShowBoxModelHighlighterForNode,
|
||||
setSelectedNode,
|
||||
});
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ class Flexbox extends PureComponent {
|
|||
}),
|
||||
!flexItemShown && flexItems.length > 0 ? this.renderFlexItemList() : null,
|
||||
flexItemShown ? this.renderFlexItemSizing() : null,
|
||||
this.renderFlexContainerProperties()
|
||||
!flexItemShown ? this.renderFlexContainerProperties() : null
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -97,9 +97,14 @@ class Header extends PureComponent {
|
|||
flexItems,
|
||||
flexItemShown,
|
||||
} = flexContainer;
|
||||
const flexItem = flexItems.find(item => item.nodeFront.actorID === flexItemShown);
|
||||
|
||||
if (!flexItem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return FlexItemSelector({
|
||||
flexItem: flexItems.find(item => item.nodeFront.actorID === flexItemShown),
|
||||
flexItem,
|
||||
flexItems,
|
||||
setSelectedNode,
|
||||
});
|
||||
|
|
|
@ -97,7 +97,6 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
border-bottom: 1px solid var(--theme-splitter-color);
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
|
@ -123,8 +122,11 @@
|
|||
* Flex Item List
|
||||
*/
|
||||
|
||||
.flex-header + .flex-item-list {
|
||||
border-block-start: 1px solid var(--theme-splitter-color);
|
||||
}
|
||||
|
||||
.flex-item-list {
|
||||
border-bottom: 1px solid var(--theme-splitter-color);
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
padding-inline-start: 34px;
|
||||
|
@ -163,6 +165,7 @@
|
|||
*/
|
||||
|
||||
.flex-outline-container {
|
||||
border-block-start: 1px solid var(--theme-splitter-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
@ -348,6 +351,7 @@
|
|||
*/
|
||||
|
||||
#flex-container-properties {
|
||||
border-block-start: 1px solid var(--theme-splitter-color);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,9 @@ DOM4_MSG_DEF(TypeError, "The expression cannot be converted to return the specif
|
|||
/* IndexedDB errors http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#exceptions */
|
||||
|
||||
DOM4_MSG_DEF(UnknownError, "The operation failed for reasons unrelated to the database itself and not covered by any other error code.", NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR)
|
||||
DOM4_MSG_DEF(ConstraintError, "A mutation operation in the transaction failed because a constraint was not satisfied. For example, an object such as an object store or index already exists and a new one was being attempted to be created.", NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR)
|
||||
DOM4_MSG_DEF(ConstraintError, "A mutation operation in the transaction failed because a constraint was not satisfied.", NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR)
|
||||
DOM4_MSG_DEF(ConstraintError, "Object store renamed to an already existing object store.", NS_ERROR_DOM_INDEXEDDB_RENAME_OBJECT_STORE_ERR)
|
||||
DOM4_MSG_DEF(ConstraintError, "Index already exists and a new one was being attempted to be created.", NS_ERROR_DOM_INDEXEDDB_RENAME_INDEX_ERR)
|
||||
DOM4_MSG_DEF(DataError, "Data provided to an operation does not meet requirements.", NS_ERROR_DOM_INDEXEDDB_DATA_ERR)
|
||||
DOM4_MSG_DEF(DataError, "No key or key range specified.", NS_ERROR_DOM_INDEXEDDB_KEY_ERR)
|
||||
DOM4_MSG_DEF(TransactionInactiveError, "A request was placed against a transaction which is currently not active, or which is finished.", NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR)
|
||||
|
@ -72,7 +74,6 @@ DOM4_MSG_DEF(TimeoutError, "A lock for the transaction could not be obtained in
|
|||
DOM4_MSG_DEF(QuotaExceededError, "The current transaction exceeded its quota limitations.", NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR)
|
||||
|
||||
/* A Non-standard IndexedDB error */
|
||||
|
||||
DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_RECOVERABLE_ERR, "The operation failed because the database was prevented from taking an action. The operation might be able to succeed if the application performs some recovery steps and retries the entire transaction. For example, there was not enough remaining storage space, or the storage quota was reached and the user declined to give more space to the database.")
|
||||
|
||||
/* FileSystem DOM errors. */
|
||||
|
|
|
@ -449,7 +449,10 @@ IDBDatabase::CreateObjectStore(
|
|||
index < count;
|
||||
index++) {
|
||||
if (aName == objectStores[index].metadata().name()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR);
|
||||
aRv.ThrowDOMException(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR,
|
||||
nsPrintfCString(
|
||||
"Object store named '%s' already exists at index '%u'",
|
||||
NS_ConvertUTF16toUTF8(aName).get(), index));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -1323,7 +1326,7 @@ IDBDatabase::RenameObjectStore(int64_t aObjectStoreId, const nsAString& aName)
|
|||
continue;
|
||||
}
|
||||
if (aName == objSpec.metadata().name()) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
|
||||
return NS_ERROR_DOM_INDEXEDDB_RENAME_OBJECT_STORE_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1371,7 +1374,7 @@ IDBDatabase::RenameIndex(int64_t aObjectStoreId,
|
|||
continue;
|
||||
}
|
||||
if (aName == metadata.name()) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
|
||||
return NS_ERROR_DOM_INDEXEDDB_RENAME_INDEX_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2239,7 +2239,9 @@ IDBObjectStore::CreateIndex(const nsAString& aName,
|
|||
index < count;
|
||||
index++) {
|
||||
if (aName == indexes[index].name()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR);
|
||||
aRv.ThrowDOMException(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR,
|
||||
nsPrintfCString("Index named '%s' already exists at index '%u'",
|
||||
NS_ConvertUTF16toUTF8(aName).get(), index));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ support-files =
|
|||
unit/test_blocked_order.js
|
||||
unit/test_clear.js
|
||||
unit/test_complex_keyPaths.js
|
||||
unit/test_constraint_error_messages.js
|
||||
unit/test_count.js
|
||||
unit/test_create_index.js
|
||||
unit/test_create_index_with_integer_keys.js
|
||||
|
@ -138,6 +139,7 @@ skip-if = e10s && os == 'win' && os_version == '6.1' # Bug 1342415
|
|||
[test_bug937006.html]
|
||||
[test_clear.html]
|
||||
[test_complex_keyPaths.html]
|
||||
[test_constraint_error_messages.html]
|
||||
[test_count.html]
|
||||
[test_create_index.html]
|
||||
[test_create_index_with_integer_keys.html]
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Property Test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript" src="unit/test_constraint_error_messages.js"></script>
|
||||
<script type="text/javascript" src="helpers.js"></script>
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var testGenerator = testSteps();
|
||||
|
||||
function* testSteps()
|
||||
{
|
||||
const name = this.window ? window.location.pathname : "Splendid Test";
|
||||
const objectStoreName = "foo";
|
||||
const indexName = "bar", keyPath = "bar";
|
||||
|
||||
info("Opening database");
|
||||
|
||||
let request = indexedDB.open(name);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = unexpectedSuccessHandler;
|
||||
|
||||
let event = yield undefined;
|
||||
let db = event.target.result;
|
||||
|
||||
info("Creating objectStore");
|
||||
|
||||
let objectStore = db.createObjectStore(objectStoreName);
|
||||
|
||||
info("Creating a duplicated object store to get an error");
|
||||
|
||||
try {
|
||||
db.createObjectStore(objectStoreName);
|
||||
ok(false,
|
||||
"ConstraintError should be thrown if object store already exists");
|
||||
} catch (e) {
|
||||
ok(true, "ConstraintError should be thrown if object store already exists");
|
||||
is(e.message,
|
||||
"Object store named '" + objectStoreName +
|
||||
"' already exists at index '0'",
|
||||
"Threw with correct error message");
|
||||
}
|
||||
|
||||
info("Creating an index");
|
||||
|
||||
objectStore.createIndex(indexName, keyPath);
|
||||
|
||||
info("Creating a duplicated indexes to verify the error message");
|
||||
|
||||
try {
|
||||
objectStore.createIndex(indexName, keyPath);
|
||||
|
||||
ok(false, "ConstraintError should be thrown if index already exists");
|
||||
} catch (e) {
|
||||
ok(true, "ConstraintError should be thrown if index already exists");
|
||||
is(e.message,
|
||||
"Index named '" + indexName + "' already exists at index '0'",
|
||||
"Threw with correct error message");
|
||||
}
|
||||
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
yield undefined;
|
||||
db.close();
|
||||
|
||||
finishTest();
|
||||
}
|
|
@ -675,22 +675,6 @@ gfxDWriteFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle)
|
|||
bool needsBold = aFontStyle->NeedsSyntheticBold(this);
|
||||
DWRITE_FONT_SIMULATIONS sims =
|
||||
needsBold ? DWRITE_FONT_SIMULATIONS_BOLD : DWRITE_FONT_SIMULATIONS_NONE;
|
||||
if (HasVariations() && !aFontStyle->variationSettings.IsEmpty()) {
|
||||
// If we need to apply variations, we can't use the cached mUnscaledFont
|
||||
// or mUnscaledFontBold here.
|
||||
// XXX todo: consider caching a small number of variation instances?
|
||||
RefPtr<IDWriteFontFace> fontFace;
|
||||
nsresult rv = CreateFontFace(getter_AddRefs(fontFace),
|
||||
aFontStyle,
|
||||
sims);
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<UnscaledFontDWrite> unscaledFont =
|
||||
new UnscaledFontDWrite(fontFace, mIsSystemFont ? mFont : nullptr, sims);
|
||||
return new gfxDWriteFont(unscaledFont, this, aFontStyle);
|
||||
}
|
||||
|
||||
ThreadSafeWeakPtr<UnscaledFontDWrite>& unscaledFontPtr =
|
||||
needsBold ? mUnscaledFontBold : mUnscaledFont;
|
||||
RefPtr<UnscaledFontDWrite> unscaledFont(unscaledFontPtr);
|
||||
|
@ -705,7 +689,16 @@ gfxDWriteFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle)
|
|||
mIsSystemFont ? mFont : nullptr, sims);
|
||||
unscaledFontPtr = unscaledFont;
|
||||
}
|
||||
return new gfxDWriteFont(unscaledFont, this, aFontStyle);
|
||||
RefPtr<IDWriteFontFace> fontFace;
|
||||
if (HasVariations() && !aFontStyle->variationSettings.IsEmpty()) {
|
||||
nsresult rv = CreateFontFace(getter_AddRefs(fontFace),
|
||||
aFontStyle,
|
||||
sims);
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return new gfxDWriteFont(unscaledFont, this, aFontStyle, fontFace);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -81,8 +81,10 @@ GetSystemTextQuality()
|
|||
gfxDWriteFont::gfxDWriteFont(const RefPtr<UnscaledFontDWrite>& aUnscaledFont,
|
||||
gfxFontEntry *aFontEntry,
|
||||
const gfxFontStyle *aFontStyle,
|
||||
RefPtr<IDWriteFontFace> aFontFace,
|
||||
AntialiasOption anAAOption)
|
||||
: gfxFont(aUnscaledFont, aFontEntry, aFontStyle, anAAOption)
|
||||
, mFontFace(aFontFace ? aFontFace : aUnscaledFont->GetFontFace())
|
||||
, mCairoFontFace(nullptr)
|
||||
, mMetrics(nullptr)
|
||||
, mSpaceGlyph(0)
|
||||
|
@ -90,8 +92,6 @@ gfxDWriteFont::gfxDWriteFont(const RefPtr<UnscaledFontDWrite>& aUnscaledFont,
|
|||
, mAllowManualShowGlyphs(true)
|
||||
, mAzureScaledFontUsedClearType(false)
|
||||
{
|
||||
mFontFace = aUnscaledFont->GetFontFace();
|
||||
|
||||
// If the IDWriteFontFace1 interface is available, we can use that for
|
||||
// faster glyph width retrieval.
|
||||
mFontFace->QueryInterface(__uuidof(IDWriteFontFace1),
|
||||
|
@ -150,7 +150,7 @@ gfxDWriteFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
|
|||
{
|
||||
auto entry = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
|
||||
RefPtr<UnscaledFontDWrite> unscaledFont = static_cast<UnscaledFontDWrite*>(mUnscaledFont.get());
|
||||
return MakeUnique<gfxDWriteFont>(unscaledFont, entry, &mStyle, anAAOption);
|
||||
return MakeUnique<gfxDWriteFont>(unscaledFont, entry, &mStyle, mFontFace, anAAOption);
|
||||
}
|
||||
|
||||
const gfxFont::Metrics&
|
||||
|
|
|
@ -30,6 +30,7 @@ public:
|
|||
gfxDWriteFont(const RefPtr<mozilla::gfx::UnscaledFontDWrite>& aUnscaledFont,
|
||||
gfxFontEntry *aFontEntry,
|
||||
const gfxFontStyle *aFontStyle,
|
||||
RefPtr<IDWriteFontFace> aFontFace = nullptr,
|
||||
AntialiasOption = kAntialiasDefault);
|
||||
~gfxDWriteFont();
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIDocShellTreeItem.h"
|
||||
|
@ -206,6 +207,71 @@ nsSecureBrowserUIImpl::CheckForBlockedContent()
|
|||
}
|
||||
}
|
||||
|
||||
// Helper function to determine if the given URI can be considered secure.
|
||||
// Essentially, only "https" URIs can be considered secure. However, the URI we
|
||||
// have may be e.g. view-source:https://example.com or
|
||||
// wyciwyg://https://example.com, in which case we have to evaluate the
|
||||
// innermost URI.
|
||||
static nsresult
|
||||
URICanBeConsideredSecure(nsIURI* uri, /* out */ bool& canBeConsideredSecure)
|
||||
{
|
||||
MOZ_ASSERT(uri);
|
||||
NS_ENSURE_ARG(uri);
|
||||
|
||||
canBeConsideredSecure = false;
|
||||
|
||||
nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(uri);
|
||||
if (!innermostURI) {
|
||||
MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
|
||||
(" couldn't get innermost URI"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
|
||||
(" innermost URI is '%s'", innermostURI->GetSpecOrDefault().get()));
|
||||
|
||||
// Unfortunately, wyciwyg URIs don't know about innermost URIs, so we have to
|
||||
// manually get the innermost URI if we have such a URI.
|
||||
bool isWyciwyg;
|
||||
nsresult rv = innermostURI->SchemeIs("wyciwyg", &isWyciwyg);
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
|
||||
(" nsIURI->SchemeIs failed"));
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (isWyciwyg) {
|
||||
nsCOMPtr<nsIURI> nonWyciwygURI;
|
||||
rv = nsContentUtils::RemoveWyciwygScheme(innermostURI,
|
||||
getter_AddRefs(nonWyciwygURI));
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
|
||||
(" nsContentUtils::RemoveWyciwygScheme failed"));
|
||||
return rv;
|
||||
}
|
||||
if (!nonWyciwygURI) {
|
||||
MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
|
||||
(" apparently that wasn't a valid wyciwyg URI"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
innermostURI = nonWyciwygURI;
|
||||
MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
|
||||
(" innermost URI is now '%s'",
|
||||
innermostURI->GetSpecOrDefault().get()));
|
||||
}
|
||||
|
||||
bool isHttps;
|
||||
rv = innermostURI->SchemeIs("https", &isHttps);
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
|
||||
(" nsIURI->SchemeIs failed"));
|
||||
return rv;
|
||||
}
|
||||
|
||||
canBeConsideredSecure = isHttps;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Helper function to get the securityInfo from a channel as a
|
||||
// nsITransportSecurityInfo. The out parameter will be set to null if there is
|
||||
// no securityInfo set.
|
||||
|
@ -246,13 +312,27 @@ nsSecureBrowserUIImpl::UpdateStateAndSecurityInfo(nsIChannel* channel,
|
|||
mState = STATE_IS_INSECURE;
|
||||
mTopLevelSecurityInfo = nullptr;
|
||||
|
||||
// Only https is considered secure (it is possible to have e.g. an http URI
|
||||
// with a channel that has a securityInfo that indicates the connection is
|
||||
// secure - e.g. h2/alt-svc or by visiting an http URI over an https proxy).
|
||||
bool canBeConsideredSecure;
|
||||
nsresult rv = URICanBeConsideredSecure(uri, canBeConsideredSecure);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (!canBeConsideredSecure) {
|
||||
MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
|
||||
(" URI can't be considered secure"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITransportSecurityInfo> securityInfo;
|
||||
GetSecurityInfoFromChannel(channel, getter_AddRefs(securityInfo));
|
||||
if (securityInfo) {
|
||||
MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
|
||||
(" we have a security info %p", securityInfo.get()));
|
||||
|
||||
nsresult rv = securityInfo->GetSecurityState(&mState);
|
||||
rv = securityInfo->GetSecurityState(&mState);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -329,8 +409,15 @@ nsSecureBrowserUIImpl::OnLocationChange(nsIWebProgress* aWebProgress,
|
|||
MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
|
||||
(" we have a channel %p", channel.get()));
|
||||
nsresult rv = UpdateStateAndSecurityInfo(channel, aLocation);
|
||||
// Even if this failed, we still want to notify downstream so that we don't
|
||||
// leave a stale security indicator. We set everything to "not secure" to be
|
||||
// safe.
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
|
||||
(" Failed to update security info. "
|
||||
"Setting everything to 'not secure' to be safe."));
|
||||
mState = STATE_IS_INSECURE;
|
||||
mTopLevelSecurityInfo = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"use strict";
|
||||
|
||||
document.open();
|
||||
// This test depends on nsSecureBrowserUIImpl handling wyciwyg:https:// URIs properly.
|
||||
// eslint-disable-next-line no-unsanitized/method
|
||||
document.write("This is insecure XSS script " + document.cookie);
|
||||
isSecurityState("broken", "security broken after document write from unsecure script");
|
||||
|
|
|
@ -443,16 +443,6 @@ class FennecInstance(GeckoInstance):
|
|||
exc, val, tb = sys.exc_info()
|
||||
message = "Error possibly due to runner or device args: {}"
|
||||
reraise(exc, message.format(e.message), tb)
|
||||
# gecko_log comes from logcat when running with device/emulator
|
||||
logcat_args = {
|
||||
"filterspec": "Gecko",
|
||||
"serial": self.runner.device.app_ctx.device_serial
|
||||
}
|
||||
if self.gecko_log == "-":
|
||||
logcat_args["stream"] = sys.stdout
|
||||
else:
|
||||
logcat_args["logfile"] = self.gecko_log
|
||||
self.runner.device.start_logcat(**logcat_args)
|
||||
|
||||
# forward marionette port
|
||||
self.runner.device.device.forward(
|
||||
|
|
|
@ -13,12 +13,10 @@ import tempfile
|
|||
import time
|
||||
|
||||
from mozdevice import ADBHost, ADBError
|
||||
from mozprocess import ProcessHandler
|
||||
|
||||
|
||||
class Device(object):
|
||||
connected = False
|
||||
logcat_proc = None
|
||||
|
||||
def __init__(self, app_ctx, logdir=None, serial=None, restore=True):
|
||||
self.app_ctx = app_ctx
|
||||
|
@ -141,28 +139,6 @@ class Device(object):
|
|||
|
||||
self.connected = True
|
||||
|
||||
if self.logdir:
|
||||
# save logcat
|
||||
logcat_log = os.path.join(self.logdir, '%s.log' % self.serial)
|
||||
if os.path.isfile(logcat_log):
|
||||
self._rotate_log(logcat_log)
|
||||
self.logcat_proc = self.start_logcat(self.serial, logfile=logcat_log)
|
||||
|
||||
def start_logcat(self, serial, logfile=None, stream=None, filterspec=None):
|
||||
logcat_args = [self.app_ctx.adb, '-s', '%s' % serial,
|
||||
'logcat', '-v', 'time', '-b', 'main', '-b', 'radio']
|
||||
# only log filterspec
|
||||
if filterspec:
|
||||
logcat_args.extend(['-s', filterspec])
|
||||
process_args = {}
|
||||
if logfile:
|
||||
process_args['logfile'] = logfile
|
||||
elif stream:
|
||||
process_args['stream'] = stream
|
||||
proc = ProcessHandler(logcat_args, **process_args)
|
||||
proc.run()
|
||||
return proc
|
||||
|
||||
def reboot(self):
|
||||
"""
|
||||
Reboots the device via adb.
|
||||
|
|
|
@ -101,7 +101,7 @@ def FennecEmulatorRunner(avd='mozemulator-4.3',
|
|||
Typically obtained via tooltool: either 'mozemulator-4.3' or 'mozemulator-x86'.
|
||||
Defaults to 'mozemulator-4.3'
|
||||
:param avd_home: Path to avd parent directory
|
||||
:param logdir: Path to save logfiles such as logcat and qemu output.
|
||||
:param logdir: Path to save logfiles such as qemu output.
|
||||
:param serial: Serial of emulator to connect to as seen in `adb devices`.
|
||||
Defaults to the first entry in `adb devices`.
|
||||
:param binary: Path to emulator binary.
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# 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/.
|
||||
# ***** END LICENSE BLOCK *****
|
||||
import os
|
||||
|
||||
config = {
|
||||
"options": [
|
||||
"--prefs-root=%(test_path)s/prefs",
|
||||
"--processes=1",
|
||||
"--config=%(test_path)s/wptrunner.ini",
|
||||
"--ca-cert-path=%(test_path)s/tests/tools/certs/cacert.pem",
|
||||
"--host-key-path=%(test_path)s/tests/tools/certs/web-platform.test.key",
|
||||
"--host-cert-path=%(test_path)s/tests/tools/certs/web-platform.test.pem",
|
||||
"--certutil-binary=%(xre_path)s/certutil",
|
||||
"--product=fennec",
|
||||
],
|
||||
"avds_dir": "/builds/worker/workspace/build/.android",
|
||||
"binary_path": "/tmp",
|
||||
"download_minidump_stackwalk": False,
|
||||
"emulator_avd_name": "test-1",
|
||||
"emulator_extra_args": "-gpu swiftshader_indirect -skip-adb-auth -verbose -show-kernel -use-system-libs -ranchu -selinux permissive -memory 3072 -cores 4",
|
||||
"emulator_manifest": """
|
||||
[
|
||||
{
|
||||
"size": 135064025,
|
||||
"digest": "125678c5b0d93ead8bbf01ba94253e532909417b40637460624cfca34e92f431534fc77a0225e9c4728dcbcf2884a8f7fa1ee059efdfa82d827ca20477d41705",
|
||||
"algorithm": "sha512",
|
||||
"filename": "android-sdk_r27.1.12-linux-x86emu.tar.gz",
|
||||
"unpack": "True"
|
||||
}
|
||||
] """,
|
||||
"emulator_process_name": "emulator64-x86",
|
||||
"env": {
|
||||
"PATH": "%(PATH)s:%(abs_work_dir)s/android-sdk-linux/emulator:%(abs_work_dir)s/android-sdk-linux/tools:%(abs_work_dir)s/android-sdk-linux/platform-tools",
|
||||
},
|
||||
"exes": {
|
||||
'adb': '%(abs_work_dir)s/android-sdk-linux/platform-tools/adb',
|
||||
},
|
||||
"geckodriver": "%(abs_test_bin_dir)s/geckodriver",
|
||||
"hostutils_manifest_path": "testing/config/tooltool-manifests/linux64/hostutils.manifest",
|
||||
"log_tbpl_level": "info",
|
||||
"log_raw_level": "info",
|
||||
"minidump_stackwalk_path": "/usr/local/bin/linux64-minidump_stackwalk",
|
||||
"per_test_category": "web-platform",
|
||||
"tooltool_cache": os.environ.get("TOOLTOOL_CACHE"),
|
||||
"tooltool_manifest_path": "testing/config/tooltool-manifests/androidx86_7_0/releng.manifest",
|
||||
"tooltool_servers": ['http://relengapi/tooltool/'],
|
||||
}
|
|
@ -5,12 +5,16 @@
|
|||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
import datetime
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
import subprocess
|
||||
import time
|
||||
import tempfile
|
||||
from mozharness.mozilla.automation import TBPL_RETRY, EXIT_STATUS_DICT
|
||||
from mozharness.base.script import PostScriptAction
|
||||
|
||||
|
||||
class AndroidMixin(object):
|
||||
|
@ -21,6 +25,7 @@ class AndroidMixin(object):
|
|||
def __init__(self, **kwargs):
|
||||
self._adb_path = None
|
||||
self._device = None
|
||||
self.app_name = None
|
||||
self.device_name = os.environ.get('DEVICE_NAME', None)
|
||||
self.device_serial = os.environ.get('DEVICE_SERIAL', None)
|
||||
self.device_ip = os.environ.get('DEVICE_IP', None)
|
||||
|
@ -63,6 +68,24 @@ class AndroidMixin(object):
|
|||
pass
|
||||
return self._device
|
||||
|
||||
@property
|
||||
def is_android(self):
|
||||
try:
|
||||
c = self.config
|
||||
installer_url = c.get('installer_url', None)
|
||||
return self.device_serial is not None or self.is_emulator or \
|
||||
(installer_url is not None and installer_url.endswith(".apk"))
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_emulator(self):
|
||||
try:
|
||||
c = self.config
|
||||
return True if c.get('emulator_manifest') else False
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
def _get_repo_url(self, path):
|
||||
"""
|
||||
Return a url for a file (typically a tooltool manifest) in this hg repo
|
||||
|
@ -102,6 +125,112 @@ class AndroidMixin(object):
|
|||
output_dir=dir,
|
||||
cache=c.get("tooltool_cache", None))
|
||||
|
||||
def _launch_emulator(self):
|
||||
env = self.query_env()
|
||||
|
||||
# Write a default ddms.cfg to avoid unwanted prompts
|
||||
avd_home_dir = self.abs_dirs['abs_avds_dir']
|
||||
DDMS_FILE = os.path.join(avd_home_dir, "ddms.cfg")
|
||||
with open(DDMS_FILE, 'w') as f:
|
||||
f.write("pingOptIn=false\npingId=0\n")
|
||||
self.info("wrote dummy %s" % DDMS_FILE)
|
||||
|
||||
# Delete emulator auth file, so it doesn't prompt
|
||||
AUTH_FILE = os.path.join(os.path.expanduser('~'), '.emulator_console_auth_token')
|
||||
if os.path.exists(AUTH_FILE):
|
||||
try:
|
||||
os.remove(AUTH_FILE)
|
||||
self.info("deleted %s" % AUTH_FILE)
|
||||
except Exception:
|
||||
self.warning("failed to remove %s" % AUTH_FILE)
|
||||
|
||||
avd_path = os.path.join(avd_home_dir, 'avd')
|
||||
if os.path.exists(avd_path):
|
||||
env['ANDROID_AVD_HOME'] = avd_path
|
||||
self.info("Found avds at %s" % avd_path)
|
||||
else:
|
||||
self.warning("AVDs missing? Not found at %s" % avd_path)
|
||||
|
||||
if "deprecated_sdk_path" in self.config:
|
||||
sdk_path = os.path.abspath(os.path.join(avd_home_dir, '..'))
|
||||
else:
|
||||
sdk_path = os.path.join(self.abs_dirs['abs_work_dir'], 'android-sdk-linux')
|
||||
if os.path.exists(sdk_path):
|
||||
env['ANDROID_SDK_HOME'] = sdk_path
|
||||
self.info("Found sdk at %s" % sdk_path)
|
||||
else:
|
||||
self.warning("Android sdk missing? Not found at %s" % sdk_path)
|
||||
|
||||
# extra diagnostics for kvm acceleration
|
||||
emu = self.config.get('emulator_process_name')
|
||||
if os.path.exists('/dev/kvm') and emu and 'x86' in emu:
|
||||
try:
|
||||
self.run_command(['ls', '-l', '/dev/kvm'])
|
||||
self.run_command(['kvm-ok'])
|
||||
self.run_command(["emulator", "-accel-check"], env=env)
|
||||
except Exception as e:
|
||||
self.warning("Extra kvm diagnostics failed: %s" % str(e))
|
||||
|
||||
command = ["emulator", "-avd", self.config["emulator_avd_name"]]
|
||||
if "emulator_extra_args" in self.config:
|
||||
command += self.config["emulator_extra_args"].split()
|
||||
|
||||
dir = self.query_abs_dirs()['abs_blob_upload_dir']
|
||||
tmp_file = tempfile.NamedTemporaryFile(mode='w', prefix='emulator-',
|
||||
suffix='.log', dir=dir, delete=False)
|
||||
self.info("Launching the emulator with: %s" % ' '.join(command))
|
||||
self.info("Writing log to %s" % tmp_file.name)
|
||||
proc = subprocess.Popen(command, stdout=tmp_file, stderr=tmp_file, env=env, bufsize=0)
|
||||
return proc
|
||||
|
||||
def _verify_emulator(self):
|
||||
boot_ok = self._retry(30, 10, self.is_boot_completed, "Verify Android boot completed",
|
||||
max_time=330)
|
||||
if not boot_ok:
|
||||
self.warning('Unable to verify Android boot completion')
|
||||
return False
|
||||
return True
|
||||
|
||||
def _verify_emulator_and_restart_on_fail(self):
|
||||
emulator_ok = self._verify_emulator()
|
||||
if not emulator_ok:
|
||||
self.screenshot("emulator-startup-screenshot-")
|
||||
self.kill_processes(self.config["emulator_process_name"])
|
||||
subprocess.check_call(['ps', '-ef'])
|
||||
# remove emulator tmp files
|
||||
for dir in glob.glob("/tmp/android-*"):
|
||||
self.rmtree(dir)
|
||||
time.sleep(5)
|
||||
self.emulator_proc = self._launch_emulator()
|
||||
return emulator_ok
|
||||
|
||||
def _retry(self, max_attempts, interval, func, description, max_time=0):
|
||||
'''
|
||||
Execute func until it returns True, up to max_attempts times, waiting for
|
||||
interval seconds between each attempt. description is logged on each attempt.
|
||||
If max_time is specified, no further attempts will be made once max_time
|
||||
seconds have elapsed; this provides some protection for the case where
|
||||
the run-time for func is long or highly variable.
|
||||
'''
|
||||
status = False
|
||||
attempts = 0
|
||||
if max_time > 0:
|
||||
end_time = datetime.datetime.now() + datetime.timedelta(seconds=max_time)
|
||||
else:
|
||||
end_time = None
|
||||
while attempts < max_attempts and not status:
|
||||
if (end_time is not None) and (datetime.datetime.now() > end_time):
|
||||
self.info("Maximum retry run-time of %d seconds exceeded; "
|
||||
"remaining attempts abandoned" % max_time)
|
||||
break
|
||||
if attempts != 0:
|
||||
self.info("Sleeping %d seconds" % interval)
|
||||
time.sleep(interval)
|
||||
attempts += 1
|
||||
self.info(">> %s: Attempt #%d of %d" % (description, attempts, max_attempts))
|
||||
status = func()
|
||||
return status
|
||||
|
||||
def dump_perf_info(self):
|
||||
'''
|
||||
Dump some host and android device performance-related information
|
||||
|
@ -161,7 +290,6 @@ class AndroidMixin(object):
|
|||
"""
|
||||
Start recording logcat. Writes logcat to the upload directory.
|
||||
"""
|
||||
self.mkdir_p(self.query_abs_dirs()['abs_blob_upload_dir'])
|
||||
# Start logcat for the device. The adb process runs until the
|
||||
# corresponding device is stopped. Output is written directly to
|
||||
# the blobber upload directory so that it is uploaded automatically
|
||||
|
@ -268,3 +396,112 @@ class AndroidMixin(object):
|
|||
self.run_command(unzip_cmd, cwd=apk_dir, halt_on_failure=True)
|
||||
self.app_name = str(self.read_from_file(package_path, verbose=True)).rstrip()
|
||||
return self.app_name
|
||||
|
||||
def kill_processes(self, process_name):
|
||||
self.info("Killing every process called %s" % process_name)
|
||||
out = subprocess.check_output(['ps', '-A'])
|
||||
for line in out.splitlines():
|
||||
if process_name in line:
|
||||
pid = int(line.split(None, 1)[0])
|
||||
self.info("Killing pid %d." % pid)
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
|
||||
# Script actions
|
||||
|
||||
def setup_avds(self):
|
||||
"""
|
||||
If tooltool cache mechanism is enabled, the cached version is used by
|
||||
the fetch command. If the manifest includes an "unpack" field, tooltool
|
||||
will unpack all compressed archives mentioned in the manifest.
|
||||
"""
|
||||
if not self.is_emulator:
|
||||
return
|
||||
|
||||
c = self.config
|
||||
dirs = self.query_abs_dirs()
|
||||
self.mkdir_p(dirs['abs_work_dir'])
|
||||
self.mkdir_p(dirs['abs_blob_upload_dir'])
|
||||
|
||||
# Always start with a clean AVD: AVD includes Android images
|
||||
# which can be stateful.
|
||||
self.rmtree(dirs['abs_avds_dir'])
|
||||
self.mkdir_p(dirs['abs_avds_dir'])
|
||||
if 'avd_url' in c:
|
||||
# Intended for experimental setups to evaluate an avd prior to
|
||||
# tooltool deployment.
|
||||
url = c['avd_url']
|
||||
self.download_unpack(url, dirs['abs_avds_dir'])
|
||||
else:
|
||||
url = self._get_repo_url(c["tooltool_manifest_path"])
|
||||
self._tooltool_fetch(url, dirs['abs_avds_dir'])
|
||||
|
||||
avd_home_dir = self.abs_dirs['abs_avds_dir']
|
||||
if avd_home_dir != "/home/cltbld/.android":
|
||||
# Modify the downloaded avds to point to the right directory.
|
||||
cmd = [
|
||||
'bash', '-c',
|
||||
'sed -i "s|/home/cltbld/.android|%s|" %s/test-*.ini' %
|
||||
(avd_home_dir, os.path.join(avd_home_dir, 'avd'))
|
||||
]
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
def start_emulator(self):
|
||||
"""
|
||||
Starts the emulator
|
||||
"""
|
||||
if not self.is_emulator:
|
||||
return
|
||||
|
||||
if 'emulator_url' in self.config or 'emulator_manifest' in self.config:
|
||||
dirs = self.query_abs_dirs()
|
||||
if self.config.get('emulator_url'):
|
||||
self.download_unpack(self.config['emulator_url'], dirs['abs_work_dir'])
|
||||
elif self.config.get('emulator_manifest'):
|
||||
manifest_path = self.create_tooltool_manifest(self.config['emulator_manifest'])
|
||||
dirs = self.query_abs_dirs()
|
||||
cache = self.config.get("tooltool_cache", None)
|
||||
if self.tooltool_fetch(manifest_path,
|
||||
output_dir=dirs['abs_work_dir'],
|
||||
cache=cache):
|
||||
self.fatal("Unable to download emulator via tooltool!")
|
||||
else:
|
||||
self.fatal("Cannot get emulator: configure emulator_url or emulator_manifest")
|
||||
if not os.path.isfile(self.adb_path):
|
||||
self.fatal("The adb binary '%s' is not a valid file!" % self.adb_path)
|
||||
self.kill_processes("xpcshell")
|
||||
self.emulator_proc = self._launch_emulator()
|
||||
|
||||
def verify_device(self):
|
||||
"""
|
||||
Check to see if the emulator can be contacted via adb.
|
||||
If any communication attempt fails, kill the emulator, re-launch, and re-check.
|
||||
"""
|
||||
if not self.is_android:
|
||||
return
|
||||
|
||||
if self.is_emulator:
|
||||
max_restarts = 5
|
||||
emulator_ok = self._retry(max_restarts, 10,
|
||||
self._verify_emulator_and_restart_on_fail,
|
||||
"Check emulator")
|
||||
if not emulator_ok:
|
||||
self.fatal('INFRA-ERROR: Unable to start emulator after %d attempts'
|
||||
% max_restarts, EXIT_STATUS_DICT[TBPL_RETRY])
|
||||
|
||||
self.mkdir_p(self.query_abs_dirs()['abs_blob_upload_dir'])
|
||||
self.dump_perf_info()
|
||||
self.logcat_start()
|
||||
# Get a post-boot device process list for diagnostics
|
||||
self.info(self.shell_output('ps'))
|
||||
|
||||
@PostScriptAction('run-tests')
|
||||
def stop_device(self, action, success=None):
|
||||
"""
|
||||
Stop logcat and kill the emulator, if necessary.
|
||||
"""
|
||||
if not self.is_android:
|
||||
return
|
||||
|
||||
self.logcat_stop()
|
||||
if self.is_emulator:
|
||||
self.kill_processes(self.config["emulator_process_name"])
|
||||
|
|
|
@ -7,23 +7,16 @@
|
|||
|
||||
import copy
|
||||
import datetime
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import signal
|
||||
import subprocess
|
||||
import time
|
||||
import tempfile
|
||||
|
||||
# load modules from parent dir
|
||||
sys.path.insert(1, os.path.dirname(sys.path[0]))
|
||||
|
||||
from mozprocess import ProcessHandler
|
||||
|
||||
from mozharness.base.log import FATAL
|
||||
from mozharness.base.script import BaseScript, PreScriptAction, PostScriptAction
|
||||
from mozharness.mozilla.automation import TBPL_RETRY, EXIT_STATUS_DICT
|
||||
from mozharness.base.script import BaseScript, PreScriptAction
|
||||
from mozharness.mozilla.mozbase import MozbaseMixin
|
||||
from mozharness.mozilla.testing.android import AndroidMixin
|
||||
from mozharness.mozilla.testing.testbase import TestingMixin, testing_config_options
|
||||
|
@ -85,8 +78,6 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
|||
]] + copy.deepcopy(testing_config_options) + \
|
||||
copy.deepcopy(code_coverage_config_options)
|
||||
|
||||
app_name = None
|
||||
|
||||
def __init__(self, require_config_file=False):
|
||||
super(AndroidEmulatorTest, self).__init__(
|
||||
config_options=self.config_options,
|
||||
|
@ -173,124 +164,6 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
|||
test_dir = test_suite
|
||||
return os.path.join(dirs['abs_test_install_dir'], test_dir)
|
||||
|
||||
def _launch_emulator(self):
|
||||
env = self.query_env()
|
||||
|
||||
# Write a default ddms.cfg to avoid unwanted prompts
|
||||
avd_home_dir = self.abs_dirs['abs_avds_dir']
|
||||
DDMS_FILE = os.path.join(avd_home_dir, "ddms.cfg")
|
||||
with open(DDMS_FILE, 'w') as f:
|
||||
f.write("pingOptIn=false\npingId=0\n")
|
||||
self.info("wrote dummy %s" % DDMS_FILE)
|
||||
|
||||
# Delete emulator auth file, so it doesn't prompt
|
||||
AUTH_FILE = os.path.join(os.path.expanduser('~'), '.emulator_console_auth_token')
|
||||
if os.path.exists(AUTH_FILE):
|
||||
try:
|
||||
os.remove(AUTH_FILE)
|
||||
self.info("deleted %s" % AUTH_FILE)
|
||||
except Exception:
|
||||
self.warning("failed to remove %s" % AUTH_FILE)
|
||||
|
||||
avd_path = os.path.join(avd_home_dir, 'avd')
|
||||
if os.path.exists(avd_path):
|
||||
env['ANDROID_AVD_HOME'] = avd_path
|
||||
self.info("Found avds at %s" % avd_path)
|
||||
else:
|
||||
self.warning("AVDs missing? Not found at %s" % avd_path)
|
||||
|
||||
if "deprecated_sdk_path" in self.config:
|
||||
sdk_path = os.path.abspath(os.path.join(avd_home_dir, '..'))
|
||||
else:
|
||||
sdk_path = os.path.join(self.abs_dirs['abs_work_dir'], 'android-sdk-linux')
|
||||
if os.path.exists(sdk_path):
|
||||
env['ANDROID_SDK_HOME'] = sdk_path
|
||||
self.info("Found sdk at %s" % sdk_path)
|
||||
else:
|
||||
self.warning("Android sdk missing? Not found at %s" % sdk_path)
|
||||
|
||||
# extra diagnostics for kvm acceleration
|
||||
emu = self.config.get('emulator_process_name')
|
||||
if os.path.exists('/dev/kvm') and emu and 'x86' in emu:
|
||||
try:
|
||||
self.run_command(['ls', '-l', '/dev/kvm'])
|
||||
self.run_command(['kvm-ok'])
|
||||
self.run_command(["emulator", "-accel-check"], env=env)
|
||||
except Exception as e:
|
||||
self.warning("Extra kvm diagnostics failed: %s" % str(e))
|
||||
|
||||
command = ["emulator", "-avd", self.config["emulator_avd_name"]]
|
||||
if "emulator_extra_args" in self.config:
|
||||
command += self.config["emulator_extra_args"].split()
|
||||
|
||||
dir = self.query_abs_dirs()['abs_blob_upload_dir']
|
||||
tmp_file = tempfile.NamedTemporaryFile(mode='w', prefix='emulator-',
|
||||
suffix='.log', dir=dir, delete=False)
|
||||
self.info("Launching the emulator with: %s" % ' '.join(command))
|
||||
self.info("Writing log to %s" % tmp_file.name)
|
||||
proc = subprocess.Popen(command, stdout=tmp_file, stderr=tmp_file, env=env, bufsize=0)
|
||||
return {
|
||||
"process": proc,
|
||||
}
|
||||
|
||||
def _retry(self, max_attempts, interval, func, description, max_time=0):
|
||||
'''
|
||||
Execute func until it returns True, up to max_attempts times, waiting for
|
||||
interval seconds between each attempt. description is logged on each attempt.
|
||||
If max_time is specified, no further attempts will be made once max_time
|
||||
seconds have elapsed; this provides some protection for the case where
|
||||
the run-time for func is long or highly variable.
|
||||
'''
|
||||
status = False
|
||||
attempts = 0
|
||||
if max_time > 0:
|
||||
end_time = datetime.datetime.now() + datetime.timedelta(seconds=max_time)
|
||||
else:
|
||||
end_time = None
|
||||
while attempts < max_attempts and not status:
|
||||
if (end_time is not None) and (datetime.datetime.now() > end_time):
|
||||
self.info("Maximum retry run-time of %d seconds exceeded; "
|
||||
"remaining attempts abandoned" % max_time)
|
||||
break
|
||||
if attempts != 0:
|
||||
self.info("Sleeping %d seconds" % interval)
|
||||
time.sleep(interval)
|
||||
attempts += 1
|
||||
self.info(">> %s: Attempt #%d of %d" % (description, attempts, max_attempts))
|
||||
status = func()
|
||||
return status
|
||||
|
||||
def _verify_emulator(self):
|
||||
boot_ok = self._retry(30, 10, self.is_boot_completed, "Verify Android boot completed",
|
||||
max_time=330)
|
||||
if not boot_ok:
|
||||
self.warning('Unable to verify Android boot completion')
|
||||
return False
|
||||
return True
|
||||
|
||||
def _verify_emulator_and_restart_on_fail(self):
|
||||
emulator_ok = self._verify_emulator()
|
||||
if not emulator_ok:
|
||||
self.screenshot("emulator-startup-screenshot-")
|
||||
self._kill_processes(self.config["emulator_process_name"])
|
||||
subprocess.check_call(['ps', '-ef'])
|
||||
# remove emulator tmp files
|
||||
for dir in glob.glob("/tmp/android-*"):
|
||||
self.rmtree(dir)
|
||||
time.sleep(5)
|
||||
self.emulator_proc = self._launch_emulator()
|
||||
return emulator_ok
|
||||
|
||||
def _kill_processes(self, process_name):
|
||||
p = subprocess.Popen(['ps', '-A'], stdout=subprocess.PIPE, bufsize=0)
|
||||
out, err = p.communicate()
|
||||
self.info("Killing every process called %s" % process_name)
|
||||
for line in out.splitlines():
|
||||
if process_name in line:
|
||||
pid = int(line.split(None, 1)[0])
|
||||
self.info("Killing pid %d." % pid)
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
|
||||
def _build_command(self):
|
||||
c = self.config
|
||||
dirs = self.query_abs_dirs()
|
||||
|
@ -380,21 +253,6 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
|||
|
||||
return cmd
|
||||
|
||||
def _install_emulator(self):
|
||||
dirs = self.query_abs_dirs()
|
||||
if self.config.get('emulator_url'):
|
||||
self.download_unpack(self.config['emulator_url'], dirs['abs_work_dir'])
|
||||
elif self.config.get('emulator_manifest'):
|
||||
manifest_path = self.create_tooltool_manifest(self.config['emulator_manifest'])
|
||||
dirs = self.query_abs_dirs()
|
||||
cache = self.config.get("tooltool_cache", None)
|
||||
if self.tooltool_fetch(manifest_path,
|
||||
output_dir=dirs['abs_work_dir'],
|
||||
cache=cache):
|
||||
self.fatal("Unable to download emulator via tooltool!")
|
||||
else:
|
||||
self.warning("Cannot get emulator: configure emulator_url or emulator_manifest")
|
||||
|
||||
def _query_suites(self):
|
||||
if self.test_suite:
|
||||
return [(self.test_suite, self.test_suite)]
|
||||
|
@ -451,73 +309,6 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
|||
self.register_virtualenv_module(requirements=[requirements],
|
||||
two_pass=True)
|
||||
|
||||
def setup_avds(self):
|
||||
'''
|
||||
If tooltool cache mechanism is enabled, the cached version is used by
|
||||
the fetch command. If the manifest includes an "unpack" field, tooltool
|
||||
will unpack all compressed archives mentioned in the manifest.
|
||||
'''
|
||||
c = self.config
|
||||
dirs = self.query_abs_dirs()
|
||||
self.mkdir_p(dirs['abs_work_dir'])
|
||||
self.mkdir_p(dirs['abs_blob_upload_dir'])
|
||||
|
||||
# Always start with a clean AVD: AVD includes Android images
|
||||
# which can be stateful.
|
||||
self.rmtree(dirs['abs_avds_dir'])
|
||||
self.mkdir_p(dirs['abs_avds_dir'])
|
||||
if 'avd_url' in c:
|
||||
# Intended for experimental setups to evaluate an avd prior to
|
||||
# tooltool deployment.
|
||||
url = c['avd_url']
|
||||
self.download_unpack(url, dirs['abs_avds_dir'])
|
||||
else:
|
||||
url = self._get_repo_url(c["tooltool_manifest_path"])
|
||||
self._tooltool_fetch(url, dirs['abs_avds_dir'])
|
||||
|
||||
avd_home_dir = self.abs_dirs['abs_avds_dir']
|
||||
if avd_home_dir != "/home/cltbld/.android":
|
||||
# Modify the downloaded avds to point to the right directory.
|
||||
cmd = [
|
||||
'bash', '-c',
|
||||
'sed -i "s|/home/cltbld/.android|%s|" %s/test-*.ini' %
|
||||
(avd_home_dir, os.path.join(avd_home_dir, 'avd'))
|
||||
]
|
||||
proc = ProcessHandler(cmd)
|
||||
proc.run()
|
||||
proc.wait()
|
||||
|
||||
def start_emulator(self):
|
||||
'''
|
||||
Starts the emulator
|
||||
'''
|
||||
if 'emulator_url' in self.config or 'emulator_manifest' in self.config:
|
||||
self._install_emulator()
|
||||
|
||||
if not os.path.isfile(self.adb_path):
|
||||
self.fatal("The adb binary '%s' is not a valid file!" % self.adb_path)
|
||||
|
||||
if not self.config.get("developer_mode"):
|
||||
self._kill_processes("xpcshell")
|
||||
|
||||
self.emulator_proc = self._launch_emulator()
|
||||
|
||||
def verify_device(self):
|
||||
'''
|
||||
Check to see if the emulator can be contacted via adb.
|
||||
If any communication attempt fails, kill the emulator, re-launch, and re-check.
|
||||
'''
|
||||
max_restarts = 5
|
||||
emulator_ok = self._retry(max_restarts, 10, self._verify_emulator_and_restart_on_fail,
|
||||
"Check emulator")
|
||||
if not emulator_ok:
|
||||
self.fatal('INFRA-ERROR: Unable to start emulator after %d attempts' % max_restarts,
|
||||
EXIT_STATUS_DICT[TBPL_RETRY])
|
||||
self.dump_perf_info()
|
||||
self.logcat_start()
|
||||
# Get a post-boot device process list for diagnostics
|
||||
self.info(self.shell_output('ps'))
|
||||
|
||||
def download_and_extract(self):
|
||||
"""
|
||||
Download and extract fennec APK, tests.zip, host utils, and robocop (if required).
|
||||
|
@ -619,14 +410,6 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
|||
self.log("The %s suite: %s ran with return status: %s" %
|
||||
(suite_category, suite, tbpl_status), level=log_level)
|
||||
|
||||
@PostScriptAction('run-tests')
|
||||
def stop_device(self, action, success=None):
|
||||
'''
|
||||
Make sure that the emulator has been stopped
|
||||
'''
|
||||
self.logcat_stop()
|
||||
self._kill_processes(self.config["emulator_process_name"])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test = AndroidEmulatorTest()
|
||||
|
|
|
@ -16,7 +16,7 @@ import subprocess
|
|||
sys.path.insert(1, os.path.dirname(sys.path[0]))
|
||||
|
||||
from mozharness.base.log import FATAL
|
||||
from mozharness.base.script import BaseScript, PreScriptAction, PostScriptAction
|
||||
from mozharness.base.script import BaseScript, PreScriptAction
|
||||
from mozharness.mozilla.mozbase import MozbaseMixin
|
||||
from mozharness.mozilla.testing.android import AndroidMixin
|
||||
from mozharness.mozilla.testing.testbase import TestingMixin, testing_config_options
|
||||
|
@ -68,8 +68,6 @@ class AndroidHardwareTest(TestingMixin, BaseScript, MozbaseMixin,
|
|||
}
|
||||
]] + copy.deepcopy(testing_config_options)
|
||||
|
||||
app_name = None
|
||||
|
||||
def __init__(self, require_config_file=False):
|
||||
super(AndroidHardwareTest, self).__init__(
|
||||
config_options=self.config_options,
|
||||
|
@ -99,9 +97,6 @@ class AndroidHardwareTest(TestingMixin, BaseScript, MozbaseMixin,
|
|||
self.test_packages_url = c.get('test_packages_url')
|
||||
self.test_manifest = c.get('test_manifest')
|
||||
self.robocop_path = os.path.join(abs_dirs['abs_work_dir'], "robocop.apk")
|
||||
self.device_name = os.environ['DEVICE_NAME']
|
||||
self.device_serial = os.environ['DEVICE_SERIAL']
|
||||
self.device_ip = os.environ['DEVICE_IP']
|
||||
self.test_suite = c.get('test_suite')
|
||||
self.this_chunk = c.get('this_chunk')
|
||||
self.total_chunks = c.get('total_chunks')
|
||||
|
@ -290,16 +285,6 @@ class AndroidHardwareTest(TestingMixin, BaseScript, MozbaseMixin,
|
|||
self.register_virtualenv_module(requirements=[requirements],
|
||||
two_pass=True)
|
||||
|
||||
def verify_device(self):
|
||||
'''
|
||||
Check to see if the device can be contacted via adb.
|
||||
'''
|
||||
self.mkdir_p(self.query_abs_dirs()['abs_blob_upload_dir'])
|
||||
self.dump_perf_info()
|
||||
self.logcat_start()
|
||||
# Get a post-boot device process list for diagnostics
|
||||
self.info(self.shell_output('ps'))
|
||||
|
||||
def download_and_extract(self):
|
||||
"""
|
||||
Download and extract fennec APK, tests.zip, host utils, and robocop (if required).
|
||||
|
@ -400,13 +385,6 @@ class AndroidHardwareTest(TestingMixin, BaseScript, MozbaseMixin,
|
|||
self.log("The %s suite: %s ran with return status: %s" %
|
||||
(suite_category, suite, tbpl_status), level=log_level)
|
||||
|
||||
@PostScriptAction('run-tests')
|
||||
def stop_device(self, action, success=None):
|
||||
'''
|
||||
Cleanup after test run.
|
||||
'''
|
||||
self.logcat_stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test = AndroidHardwareTest()
|
||||
|
|
|
@ -18,6 +18,7 @@ import mozinfo
|
|||
from mozharness.base.errors import BaseErrorList
|
||||
from mozharness.base.script import PreScriptAction
|
||||
from mozharness.base.vcs.vcsbase import MercurialScript
|
||||
from mozharness.mozilla.testing.android import AndroidMixin
|
||||
from mozharness.mozilla.testing.testbase import TestingMixin, testing_config_options
|
||||
from mozharness.mozilla.testing.codecoverage import (
|
||||
CodeCoverageMixin,
|
||||
|
@ -29,7 +30,7 @@ from mozharness.mozilla.structuredlog import StructuredOutputParser
|
|||
from mozharness.base.log import INFO
|
||||
|
||||
|
||||
class WebPlatformTest(TestingMixin, MercurialScript, CodeCoverageMixin):
|
||||
class WebPlatformTest(TestingMixin, MercurialScript, CodeCoverageMixin, AndroidMixin):
|
||||
config_options = [
|
||||
[['--test-type'], {
|
||||
"action": "extend",
|
||||
|
@ -104,9 +105,12 @@ class WebPlatformTest(TestingMixin, MercurialScript, CodeCoverageMixin):
|
|||
config_options=self.config_options,
|
||||
all_actions=[
|
||||
'clobber',
|
||||
'setup-avds',
|
||||
'start-emulator',
|
||||
'download-and-extract',
|
||||
'create-virtualenv',
|
||||
'pull',
|
||||
'verify-device',
|
||||
'install',
|
||||
'run-tests',
|
||||
],
|
||||
|
@ -121,6 +125,9 @@ class WebPlatformTest(TestingMixin, MercurialScript, CodeCoverageMixin):
|
|||
self.installer_path = c.get('installer_path')
|
||||
self.binary_path = c.get('binary_path')
|
||||
self.abs_app_dir = None
|
||||
self.xre_path = None
|
||||
if self.is_emulator:
|
||||
self.device_serial = 'emulator-5554'
|
||||
|
||||
def query_abs_app_dir(self):
|
||||
"""We can't set this in advance, because OSX install directories
|
||||
|
@ -144,6 +151,10 @@ class WebPlatformTest(TestingMixin, MercurialScript, CodeCoverageMixin):
|
|||
dirs['abs_test_bin_dir'] = os.path.join(dirs['abs_test_install_dir'], 'bin')
|
||||
dirs["abs_wpttest_dir"] = os.path.join(dirs['abs_test_install_dir'], "web-platform")
|
||||
dirs['abs_blob_upload_dir'] = os.path.join(abs_dirs['abs_work_dir'], 'blobber_upload_dir')
|
||||
if self.is_android:
|
||||
dirs['abs_xre_dir'] = os.path.join(abs_dirs['abs_work_dir'], 'hostutils')
|
||||
if self.is_emulator:
|
||||
dirs['abs_avds_dir'] = self.config.get('avds_dir')
|
||||
|
||||
abs_dirs.update(dirs)
|
||||
self.abs_dirs = abs_dirs
|
||||
|
@ -187,7 +198,8 @@ class WebPlatformTest(TestingMixin, MercurialScript, CodeCoverageMixin):
|
|||
'test_path': dirs["abs_wpttest_dir"],
|
||||
'test_install_path': dirs["abs_test_install_dir"],
|
||||
'abs_app_dir': abs_app_dir,
|
||||
'abs_work_dir': dirs["abs_work_dir"]
|
||||
'abs_work_dir': dirs["abs_work_dir"],
|
||||
'xre_path': self.xre_path,
|
||||
}
|
||||
|
||||
cmd = [self.query_python_path('python'), '-u']
|
||||
|
@ -214,6 +226,10 @@ class WebPlatformTest(TestingMixin, MercurialScript, CodeCoverageMixin):
|
|||
"--run-by-dir=%i" % (3 if not mozinfo.info["asan"] else 0),
|
||||
"--no-pause-after-test"]
|
||||
|
||||
if self.is_android:
|
||||
cmd += ["--device-serial=%s" % self.device_serial]
|
||||
cmd += ["--package-name=%s" % self.query_package_name()]
|
||||
|
||||
if not sys.platform.startswith("linux"):
|
||||
cmd += ["--exclude=css"]
|
||||
|
||||
|
@ -282,8 +298,19 @@ class WebPlatformTest(TestingMixin, MercurialScript, CodeCoverageMixin):
|
|||
"mozpack/*",
|
||||
"mozbuild/*"],
|
||||
suite_categories=["web-platform"])
|
||||
if self.is_android:
|
||||
dirs = self.query_abs_dirs()
|
||||
self.xre_path = self.download_hostutils(dirs['abs_xre_dir'])
|
||||
|
||||
def install(self):
|
||||
if self.is_android:
|
||||
self.install_apk(self.installer_path)
|
||||
else:
|
||||
super(WebPlatformTest, self).install()
|
||||
|
||||
def _install_fonts(self):
|
||||
if self.is_android:
|
||||
return
|
||||
# Ensure the Ahem font is available
|
||||
dirs = self.query_abs_dirs()
|
||||
|
||||
|
@ -327,6 +354,9 @@ class WebPlatformTest(TestingMixin, MercurialScript, CodeCoverageMixin):
|
|||
else:
|
||||
env['STYLO_THREADS'] = '4'
|
||||
|
||||
if self.is_android:
|
||||
env['ADB_PATH'] = self.adb_path
|
||||
|
||||
env = self.query_env(partial_env=env, log_level=INFO)
|
||||
|
||||
start_time = datetime.now()
|
||||
|
|
|
@ -220,16 +220,6 @@ class FennecBrowser(FirefoxBrowser):
|
|||
|
||||
self.runner.start(debug_args=debug_args, interactive=self.debug_info and self.debug_info.interactive)
|
||||
|
||||
# gecko_log comes from logcat when running with device/emulator
|
||||
logcat_args = {
|
||||
"filterspec": "Gecko",
|
||||
"serial": self.runner.device.app_ctx.device_serial
|
||||
}
|
||||
# TODO setting logcat_args["logfile"] yields an almost empty file
|
||||
# even without filterspec
|
||||
logcat_args["stream"] = sys.stdout
|
||||
self.runner.device.start_logcat(**logcat_args)
|
||||
|
||||
self.runner.device.device.forward(
|
||||
local="tcp:{}".format(self.marionette_port),
|
||||
remote="tcp:{}".format(self.marionette_port))
|
||||
|
|
|
@ -37,6 +37,31 @@ InProcessWinCompositorWidget::InProcessWinCompositorWidget(const WinCompositorWi
|
|||
MOZ_ASSERT(mWindow);
|
||||
}
|
||||
|
||||
void
|
||||
InProcessWinCompositorWidget::OnDestroyWindow()
|
||||
{
|
||||
EnterPresentLock();
|
||||
WinCompositorWidget::OnDestroyWindow();
|
||||
LeavePresentLock();
|
||||
}
|
||||
|
||||
void
|
||||
InProcessWinCompositorWidget::UpdateTransparency(nsTransparencyMode aMode)
|
||||
{
|
||||
EnterPresentLock();
|
||||
WinCompositorWidget::UpdateTransparency(aMode);
|
||||
LeavePresentLock();
|
||||
}
|
||||
|
||||
void
|
||||
InProcessWinCompositorWidget::ClearTransparentWindow()
|
||||
{
|
||||
EnterPresentLock();
|
||||
WinCompositorWidget::ClearTransparentWindow();
|
||||
LeavePresentLock();
|
||||
}
|
||||
|
||||
|
||||
nsIWidget*
|
||||
InProcessWinCompositorWidget::RealWidget()
|
||||
{
|
||||
|
|
|
@ -24,6 +24,10 @@ public:
|
|||
const layers::CompositorOptions& aOptions,
|
||||
nsWindow* aWindow);
|
||||
|
||||
void OnDestroyWindow() override;
|
||||
void UpdateTransparency(nsTransparencyMode aMode) override;
|
||||
void ClearTransparentWindow() override;
|
||||
|
||||
void ObserveVsync(VsyncObserver* aObserver) override;
|
||||
nsIWidget* RealWidget() override;
|
||||
|
||||
|
|
|
@ -975,6 +975,8 @@ with modules["DOM_INDEXEDDB"]:
|
|||
errors["NS_ERROR_DOM_INDEXEDDB_VERSION_ERR"] = FAILURE(12)
|
||||
errors["NS_ERROR_DOM_INDEXEDDB_RECOVERABLE_ERR"] = FAILURE(1001)
|
||||
errors["NS_ERROR_DOM_INDEXEDDB_KEY_ERR"] = FAILURE(1002)
|
||||
errors["NS_ERROR_DOM_INDEXEDDB_RENAME_OBJECT_STORE_ERR"] = FAILURE(1003)
|
||||
errors["NS_ERROR_DOM_INDEXEDDB_RENAME_INDEX_ERR"] = FAILURE(1004)
|
||||
|
||||
|
||||
# =======================================================================
|
||||
|
|
Загрузка…
Ссылка в новой задаче