зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound.
This commit is contained in:
Коммит
ef0d8282c6
|
@ -8,7 +8,7 @@
|
|||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(ba0e6c8e-8c03-4b9b-8f9b-4fb14216f56e)]
|
||||
interface mozIApplicationClearPrivateDataParams
|
||||
interface mozIApplicationClearPrivateDataParams : nsISupports
|
||||
{
|
||||
readonly attribute unsigned long appId;
|
||||
readonly attribute boolean browserOnly;
|
||||
|
|
|
@ -37,6 +37,8 @@ using mozilla::dom::ContentChild;
|
|||
#include "mozilla/Telemetry.h"
|
||||
#include "DictionaryHelpers.h"
|
||||
#include "GeneratedEvents.h"
|
||||
#include "nsIAppsService.h"
|
||||
#include "mozIApplication.h"
|
||||
|
||||
// calls FlushAndDeleteTemporaryTables(false)
|
||||
#define NS_DOMSTORAGE_FLUSH_TIMER_TOPIC "domstorage-flush-timer"
|
||||
|
@ -162,6 +164,8 @@ nsDOMStorageManager::Initialize()
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = os->AddObserver(gStorageManager, "last-pb-context-exited", true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = os->AddObserver(gStorageManager, "webapps-uninstall", true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -307,6 +311,23 @@ nsDOMStorageManager::Observe(nsISupports *aSubject,
|
|||
if (DOMStorageImpl::gStorageDB) {
|
||||
return DOMStorageImpl::gStorageDB->DropPrivateBrowsingStorages();
|
||||
}
|
||||
} else if (!strcmp(aTopic, "webapps-uninstall")) {
|
||||
if (!DOMStorageImpl::gStorageDB) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1");
|
||||
nsCOMPtr<mozIApplication> app;
|
||||
|
||||
appsService->GetAppFromObserverMessage(nsAutoString(aData), getter_AddRefs(app));
|
||||
NS_ENSURE_TRUE(app, NS_ERROR_UNEXPECTED);
|
||||
|
||||
uint32_t appId;
|
||||
app->GetLocalId(&appId);
|
||||
MOZ_ASSERT(appId != nsIScriptSecurityManager::NO_APP_ID);
|
||||
|
||||
return DOMStorageImpl::gStorageDB->RemoveAllForApp(appId,
|
||||
/* onlyBrowserElements */ false);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -202,6 +202,15 @@ nsDOMStorageDBWrapper::RemoveAll()
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::RemoveAllForApp(uint32_t aAppId, bool aOnlyBrowserElement)
|
||||
{
|
||||
// We only care about removing the permament storage. Temporary storage such
|
||||
// as session storage or private browsing storage will not be re-used anyway
|
||||
// and will be automatically deleted at some point.
|
||||
return mPersistentDB.RemoveAllForApp(aAppId, aOnlyBrowserElement);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::GetUsage(DOMStorageImpl* aStorage, int32_t *aUsage)
|
||||
{
|
||||
|
|
|
@ -137,6 +137,15 @@ public:
|
|||
nsresult
|
||||
RemoveAll();
|
||||
|
||||
/**
|
||||
* Removes all keys from storage for a specific app.
|
||||
* If aOnlyBrowserElement is true, it will remove only keys with the
|
||||
* browserElement flag set.
|
||||
* aAppId has to be a valid app id. It can't be NO_APP_ID or UNKNOWN_APP_ID.
|
||||
*/
|
||||
nsresult
|
||||
RemoveAllForApp(uint32_t aAppId, bool aOnlyBrowserElement);
|
||||
|
||||
/**
|
||||
* Returns usage for a storage using its GetQuotaDBKey() as a key.
|
||||
*/
|
||||
|
|
|
@ -664,6 +664,39 @@ nsDOMStoragePersistentDB::RemoveAll()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStoragePersistentDB::RemoveAllForApp(uint32_t aAppId, bool aOnlyBrowserElement)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
rv = MaybeCommitInsertTransaction();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
|
||||
"DELETE FROM webappsstore2_view "
|
||||
"WHERE scope LIKE :scope"
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
mozStorageStatementScoper scopeStmt(stmt);
|
||||
|
||||
nsAutoCString scope;
|
||||
scope.AppendInt(aAppId);
|
||||
if (aOnlyBrowserElement) {
|
||||
scope.Append(NS_LITERAL_CSTRING(":t:%"));
|
||||
} else {
|
||||
scope.Append(NS_LITERAL_CSTRING(":_:%"));
|
||||
}
|
||||
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), scope);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
MarkAllScopesDirty();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStoragePersistentDB::GetUsage(DOMStorageImpl* aStorage,
|
||||
int32_t *aUsage)
|
||||
|
|
|
@ -98,6 +98,15 @@ public:
|
|||
nsresult
|
||||
RemoveAll();
|
||||
|
||||
/**
|
||||
* Removes all keys from storage for a specific app.
|
||||
* If aOnlyBrowserElement is true, it will remove only keys with the
|
||||
* browserElement flag set.
|
||||
* aAppId has to be a valid app id. It can't be NO_APP_ID or UNKNOWN_APP_ID.
|
||||
*/
|
||||
nsresult
|
||||
RemoveAllForApp(uint32_t aAppId, bool aOnlyBrowserElement);
|
||||
|
||||
/**
|
||||
* Returns usage for a storage using its GetQuotaDBKey() as a key.
|
||||
*/
|
||||
|
|
|
@ -59,6 +59,7 @@ MOCHITEST_FILES = \
|
|||
|
||||
MOCHITEST_CHROME_FILES = \
|
||||
test_localStorageFromChrome.xhtml \
|
||||
test_app_uninstall.html \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=786301
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tests that uninstalling app removes the localStorage data</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=786296">Mozilla Bug 786301</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<iframe src="http://example.com/tests/error404"></iframe>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
/** Test for Bug 786301 **/
|
||||
|
||||
/*
|
||||
* This test will check that localStorage data are correctly deleted when an app
|
||||
* is uninstalled.
|
||||
* Here is the big picture of what the test does:
|
||||
* 1. Setup permissions and preferences.
|
||||
* 2. Install a dummy application and embed it in an iframe.
|
||||
* 3. Load a mozbrowser iframe from this application.
|
||||
* 4. Fill storages for the app and the mozbrowser iframe.
|
||||
* 5. Uninstall the application.
|
||||
*
|
||||
* Expected result: all localStorage data from the app and mozbrowser have been
|
||||
* deleted but sessionStorage stays untouched such as non-app/browser data.
|
||||
*
|
||||
* This test is asynchronous and methods are called in a reading order.
|
||||
* Each method has a deeper explanation of what it does
|
||||
*/
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
const Cu = Components.utils;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var permManager = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
var appsService = Cc['@mozilla.org/AppsService;1']
|
||||
.getService(Ci.nsIAppsService);
|
||||
|
||||
/**
|
||||
* This function will make sure that the next applications we try to install
|
||||
* will be installed. That means it will behave like if the user allowed the app
|
||||
* to be installed in the door hanger.
|
||||
*/
|
||||
function confirmNextInstall() {
|
||||
var panel = window.top.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell)
|
||||
.chromeEventHandler.ownerDocument.defaultView
|
||||
.PopupNotifications.panel
|
||||
|
||||
panel.addEventListener("popupshown", function() {
|
||||
panel.removeEventListener("popupshown", arguments.callee, false);
|
||||
this.childNodes[0].button.doCommand();
|
||||
}, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the |storage| that has been given with "foo" => "bar".
|
||||
* Checks that the storage wasn't initialized and checks that the initialization
|
||||
* was successful.
|
||||
*/
|
||||
function setupStorage(storage) {
|
||||
is(storage.getItem("foo"), null, "no data");
|
||||
|
||||
storage.setItem("foo", "bar");
|
||||
is(storage.getItem("foo"), "bar", "data written");
|
||||
}
|
||||
|
||||
permManager.addFromPrincipal(window.document.nodePrincipal, "webapps-manage",
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
permManager.addFromPrincipal(window.document.nodePrincipal, "browser",
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
|
||||
var previousPrefs = {
|
||||
mozBrowserFramesEnabled: null,
|
||||
installerDryRun: null,
|
||||
};
|
||||
|
||||
// Save the prefs we want to change (so we can re-set them later) and set them
|
||||
// to the needed value.
|
||||
try {
|
||||
previousPrefs.mozBrowserFramesEnabled = SpecialPowers.getBoolPref('dom.mozBrowserFramesEnabled');
|
||||
} catch(e)
|
||||
{
|
||||
}
|
||||
SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', true);
|
||||
|
||||
try {
|
||||
previousPrefs.installerDryRun = SpecialPowers.getBoolPref('browser.mozApps.installer.dry_run');
|
||||
} catch(e) {
|
||||
}
|
||||
SpecialPowers.setBoolPref('browser.mozApps.installer.dry_run', true);
|
||||
|
||||
// URL of the manifest of the app we want to install.
|
||||
const gManifestURL = "http://www.example.com:80/chrome/dom/tests/mochitest/webapps/apps/basic.webapp";
|
||||
// ID of the installed app.
|
||||
var gTestAppId = 0;
|
||||
// Cookies currently in the system.
|
||||
var gCurrentCookiesCount = 0;
|
||||
// Storages from a non-app to make sure we do not remove cookies from everywhere.
|
||||
var gWitnessStorage = {};
|
||||
// Storages for the app.
|
||||
var gAppStorage = {};
|
||||
// Storage for a mozbrowser inside the app.
|
||||
var gBrowserStorage = {};
|
||||
|
||||
addLoadEvent(function() {
|
||||
/*
|
||||
* We are setuping the witness storage (non-app) and will install the
|
||||
* application.
|
||||
* When the application is installed, we will insert it in an iframe and wait
|
||||
* for the load event. to be fired.
|
||||
*/
|
||||
|
||||
confirmNextInstall();
|
||||
|
||||
gWitnessStorage.localStorage = window.frames[0].localStorage;
|
||||
gWitnessStorage.sessionStorage = window.frames[0].sessionStorage;
|
||||
|
||||
setupStorage(gWitnessStorage.localStorage);
|
||||
setupStorage(gWitnessStorage.sessionStorage);
|
||||
|
||||
navigator.mozApps.install(gManifestURL, null).onsuccess = function() {
|
||||
gTestAppId = appsService.getAppLocalIdByManifestURL(gManifestURL);
|
||||
|
||||
var frame = document.createElement('iframe');
|
||||
frame.setAttribute('mozbrowser', '');
|
||||
frame.setAttribute('mozapp', gManifestURL);
|
||||
frame.src = 'http://example.com/tests/error404';
|
||||
frame.name = 'app';
|
||||
|
||||
frame.addEventListener('load', appFrameLoadEvent);
|
||||
|
||||
document.body.appendChild(frame);
|
||||
};
|
||||
});
|
||||
|
||||
function appFrameLoadEvent() {
|
||||
/*
|
||||
* The app frame has been loaded. We can now add permissions for the app to
|
||||
* create browsers and we will load a page in this browser and wait for the
|
||||
* load event.
|
||||
*/
|
||||
permManager.addFromPrincipal(window.frames[1].document.nodePrincipal, "browser",
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
|
||||
var frame = document.createElement('iframe');
|
||||
frame.setAttribute('mozbrowser', '');
|
||||
frame.src = 'http://example.com/tests/error404_2';
|
||||
|
||||
frame.addEventListener('load', browserLoadEvent);
|
||||
|
||||
document.getElementsByName('app')[0].contentDocument.body.appendChild(frame);
|
||||
}
|
||||
|
||||
function browserLoadEvent() {
|
||||
/*
|
||||
* The browser inside the app has loaded.
|
||||
* We can now setup the app and browser storages and uninstall the app.
|
||||
*/
|
||||
|
||||
gAppStorage.localStorage = window.frames[1].localStorage;
|
||||
gAppStorage.sessionStorage = window.frames[1].sessionStorage;
|
||||
|
||||
gBrowserStorage.localStorage = window.frames[1].frames[0].localStorage;
|
||||
gBrowserStorage.sessionStorage = window.frames[1].frames[0].sessionStorage;
|
||||
|
||||
setupStorage(gAppStorage.localStorage);
|
||||
setupStorage(gAppStorage.sessionStorage);
|
||||
setupStorage(gBrowserStorage.localStorage);
|
||||
setupStorage(gBrowserStorage.sessionStorage);
|
||||
|
||||
navigator.mozApps.mgmt.getNotInstalled().onsuccess = function() {
|
||||
for (i in this.result) {
|
||||
var app = this.result[i];
|
||||
if (app.manifestURL == gManifestURL) {
|
||||
app.uninstall().onsuccess = function() {
|
||||
/*
|
||||
* Now that the app is uninstalled, we should not find any more
|
||||
* localStorage data from the app or its browsers. However,
|
||||
* sessionStorage is expected to stay.
|
||||
* The witness storage (non-app) should not have changed.
|
||||
*/
|
||||
is(gAppStorage.localStorage.getItem("foo"), null, "localstorage data have been deleted");
|
||||
is(gBrowserStorage.localStorage.getItem("foo"), null, "localstorage data have been deleted");
|
||||
|
||||
is(gAppStorage.sessionStorage.getItem("foo"), "bar", "sessionstorage data have not been deleted");
|
||||
is(gBrowserStorage.sessionStorage.getItem("foo"), "bar", "sessionstorage data have not been deleted");
|
||||
|
||||
is(gWitnessStorage.localStorage.getItem("foo"), "bar", "data are still there");
|
||||
is(gWitnessStorage.sessionStorage.getItem("foo"), "bar", "data are still there");
|
||||
|
||||
finish();
|
||||
|
||||
return;
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be called when the test will be done. It is going to clear
|
||||
* all storage data, permissions, etc.
|
||||
*/
|
||||
function finish() {
|
||||
gWitnessStorage.localStorage.clear();
|
||||
gWitnessStorage.sessionStorage.clear();
|
||||
|
||||
permManager.removeFromPrincipal(window.document.nodePrincipal, "webapps-manage",
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
permManager.removeFromPrincipal(window.document.nodePrincipal, "browser",
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
|
||||
SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', previousPrefs.mozBrowserFramesEnabled);
|
||||
SpecialPowers.setBoolPref('browser.mozApps.installer.dry_run', previousPrefs.installerDryRun);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -103,14 +103,20 @@ class MozbuildObject(object):
|
|||
# files are tightly coupled with the environment by definition. In the
|
||||
# future, perhaps we'll have a more sanitized environment for mozconfig
|
||||
# execution.
|
||||
#
|
||||
# The force of str is required because subprocess on Python <2.7.3
|
||||
# does not like unicode in environment keys or values. At the time this
|
||||
# was written, Mozilla shipped Python 2.7.2 with MozillaBuild.
|
||||
env = dict(os.environ)
|
||||
if path is not None:
|
||||
env['MOZCONFIG'] = path
|
||||
env[str('MOZCONFIG')] = path
|
||||
|
||||
env['CONFIG_GUESS'] = self._config_guess
|
||||
env[str('CONFIG_GUESS')] = self._config_guess
|
||||
|
||||
output = subprocess.check_output([loader, self.topsrcdir],
|
||||
stderr=subprocess.PIPE, cwd=self.topsrcdir, env=env)
|
||||
args = self._normalize_command([loader, self.topsrcdir], True)
|
||||
|
||||
output = subprocess.check_output(args, stderr=subprocess.PIPE,
|
||||
cwd=self.topsrcdir, env=env)
|
||||
|
||||
# The output is make syntax. We parse this in a specialized make
|
||||
# context.
|
||||
|
@ -147,7 +153,8 @@ class MozbuildObject(object):
|
|||
if self._config_guess_output is None:
|
||||
p = os.path.join(self.topsrcdir, 'build', 'autoconf',
|
||||
'config.guess')
|
||||
self._config_guess_output = subprocess.check_output([p],
|
||||
args = self._normalize_command([p], True)
|
||||
self._config_guess_output = subprocess.check_output(args,
|
||||
cwd=self.topsrcdir).strip()
|
||||
|
||||
return self._config_guess_output
|
||||
|
@ -292,20 +299,7 @@ class MozbuildObject(object):
|
|||
within a UNIX environment. Basically, if we are on Windows, it will
|
||||
execute the command via an appropriate UNIX-like shell.
|
||||
"""
|
||||
assert isinstance(args, list) and len(args)
|
||||
|
||||
if require_unix_environment and _in_msys:
|
||||
# Always munge Windows-style into Unix style for the command.
|
||||
prog = args[0].replace('\\', '/')
|
||||
|
||||
# PyMake removes the C: prefix. But, things seem to work here
|
||||
# without it. Not sure what that's about.
|
||||
|
||||
# We run everything through the msys shell. We need to use
|
||||
# '-c' and pass all the arguments as one argument because that is
|
||||
# how sh works.
|
||||
cline = subprocess.list2cmdline([prog] + args[1:])
|
||||
args = [_current_shell, '-c', cline]
|
||||
args = self._normalize_command(args, require_unix_environment)
|
||||
|
||||
self.log(logging.INFO, 'process', {'args': args}, ' '.join(args))
|
||||
|
||||
|
@ -336,6 +330,32 @@ class MozbuildObject(object):
|
|||
if status != 0 and not ignore_errors:
|
||||
raise Exception('Process executed with non-0 exit code: %s' % args)
|
||||
|
||||
def _normalize_command(self, args, require_unix_environment):
|
||||
"""Adjust command arguments to run in the necessary environment.
|
||||
|
||||
This exists mainly to facilitate execution of programs requiring a *NIX
|
||||
shell when running on Windows. The caller specifies whether a shell
|
||||
environment is required. If it is and we are running on Windows but
|
||||
aren't running in the UNIX-like msys environment, then we rewrite the
|
||||
command to execute via a shell.
|
||||
"""
|
||||
assert isinstance(args, list) and len(args)
|
||||
|
||||
if not require_unix_environment or not _in_msys:
|
||||
return args
|
||||
|
||||
# Always munge Windows-style into Unix style for the command.
|
||||
prog = args[0].replace('\\', '/')
|
||||
|
||||
# PyMake removes the C: prefix. But, things seem to work here
|
||||
# without it. Not sure what that's about.
|
||||
|
||||
# We run everything through the msys shell. We need to use
|
||||
# '-c' and pass all the arguments as one argument because that is
|
||||
# how sh works.
|
||||
cline = subprocess.list2cmdline([prog] + args[1:])
|
||||
return [_current_shell, '-c', cline]
|
||||
|
||||
def _is_windows(self):
|
||||
return os.name in ('nt', 'ce')
|
||||
|
||||
|
|
|
@ -36,8 +36,9 @@ class Emulator(object):
|
|||
|
||||
deviceRe = re.compile(r"^emulator-(\d+)(\s*)(.*)$")
|
||||
|
||||
def __init__(self, homedir=None, noWindow=False, logcat_dir=None, arch="x86",
|
||||
emulatorBinary=None, res='480x800', sdcard=None, userdata=None):
|
||||
def __init__(self, homedir=None, noWindow=False, logcat_dir=None,
|
||||
arch="x86", emulatorBinary=None, res='480x800', sdcard=None,
|
||||
userdata=None, gecko_path=None):
|
||||
self.port = None
|
||||
self._emulator_launched = False
|
||||
self.proc = None
|
||||
|
@ -59,6 +60,7 @@ class Emulator(object):
|
|||
self.homedir = os.path.expanduser(homedir)
|
||||
self.dataImg = userdata
|
||||
self.copy_userdata = self.dataImg is None
|
||||
self.gecko_path = gecko_path
|
||||
|
||||
def _check_for_b2g(self):
|
||||
if self.homedir is None:
|
||||
|
@ -67,7 +69,7 @@ class Emulator(object):
|
|||
raise Exception('Must define B2G_HOME or pass the homedir parameter')
|
||||
self._check_file(self.homedir)
|
||||
|
||||
oldstyle_homedir = os.path.join(self.homedir, 'glue','gonk-ics')
|
||||
oldstyle_homedir = os.path.join(self.homedir, 'glue', 'gonk-ics')
|
||||
if os.access(oldstyle_homedir, os.F_OK):
|
||||
self.homedir = oldstyle_homedir
|
||||
|
||||
|
@ -79,7 +81,7 @@ class Emulator(object):
|
|||
if platform.system() == "Darwin":
|
||||
host_dir = "darwin-x86"
|
||||
|
||||
host_bin_dir = os.path.join("out","host", host_dir, "bin")
|
||||
host_bin_dir = os.path.join("out", "host", host_dir, "bin")
|
||||
|
||||
if self.arch == "x86":
|
||||
binary = os.path.join(host_bin_dir, "emulator-x86")
|
||||
|
@ -125,10 +127,10 @@ class Emulator(object):
|
|||
|
||||
@property
|
||||
def args(self):
|
||||
qemuArgs = [ self.binary,
|
||||
qemuArgs = [self.binary,
|
||||
'-kernel', self.kernelImg,
|
||||
'-sysdir', self.sysDir,
|
||||
'-data', self.dataImg ]
|
||||
'-data', self.dataImg]
|
||||
if self._tmp_sdcard:
|
||||
qemuArgs.extend(['-sdcard', self._tmp_sdcard])
|
||||
if self.noWindow:
|
||||
|
@ -150,12 +152,12 @@ class Emulator(object):
|
|||
|
||||
def create_sdcard(self, sdcard):
|
||||
self._tmp_sdcard = tempfile.mktemp(prefix='sdcard')
|
||||
sdargs = [self.mksdcard, "-l" , "mySdCard", sdcard, self._tmp_sdcard]
|
||||
sd = subprocess.Popen(sdargs, stdout= subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
sdargs = [self.mksdcard, "-l", "mySdCard", sdcard, self._tmp_sdcard]
|
||||
sd = subprocess.Popen(sdargs, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
retcode = sd.wait()
|
||||
if retcode:
|
||||
raise Exception('unable to create sdcard : exit code %d: %s'
|
||||
% (retcode, adb.stdout.read()))
|
||||
% (retcode, sd.stdout.read()))
|
||||
return None
|
||||
|
||||
def _check_for_adb(self):
|
||||
|
@ -168,10 +170,11 @@ class Emulator(object):
|
|||
if adb.wait() == 0:
|
||||
self.adb = adb.stdout.read().strip() # remove trailing newline
|
||||
return
|
||||
adb_paths = [os.path.join(self.homedir,'glue','gonk','out','host',
|
||||
host_dir ,'bin','adb'),os.path.join(self.homedir, 'out',
|
||||
'host', host_dir,'bin', 'adb'),os.path.join(self.homedir,
|
||||
'bin','adb')]
|
||||
adb_paths = [os.path.join(self.homedir, 'glue', 'gonk', 'out', 'host',
|
||||
host_dir, 'bin', 'adb'),
|
||||
os.path.join(self.homedir, 'out', 'host', host_dir,
|
||||
'bin', 'adb'),
|
||||
os.path.join(self.homedir, 'bin', 'adb')]
|
||||
for option in adb_paths:
|
||||
if os.path.exists(option):
|
||||
self.adb = option
|
||||
|
@ -180,6 +183,9 @@ class Emulator(object):
|
|||
|
||||
def _run_adb(self, args):
|
||||
args.insert(0, self.adb)
|
||||
if self.port:
|
||||
args.insert(1, '-s')
|
||||
args.insert(2, 'emulator-%d' % self.port)
|
||||
adb = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
retcode = adb.wait()
|
||||
if retcode:
|
||||
|
@ -198,7 +204,7 @@ class Emulator(object):
|
|||
if line.startswith('OK'):
|
||||
return output
|
||||
elif line.startswith('KO:'):
|
||||
raise Exception ('bad telnet response: %s' % line)
|
||||
raise Exception('bad telnet response: %s' % line)
|
||||
|
||||
def _run_telnet(self, command):
|
||||
if not self.telnet:
|
||||
|
@ -269,6 +275,8 @@ class Emulator(object):
|
|||
online, offline = self._get_adb_devices()
|
||||
self.port = int(list(online)[0])
|
||||
|
||||
self.install_gecko()
|
||||
|
||||
def start(self):
|
||||
self._check_for_b2g()
|
||||
self.start_adb()
|
||||
|
@ -301,8 +309,9 @@ class Emulator(object):
|
|||
self.save_logcat()
|
||||
|
||||
# setup DNS fix for networking
|
||||
self._run_adb(['-s', 'emulator-%d' % self.port,
|
||||
'shell', 'setprop', 'net.dns1', '10.0.2.3'])
|
||||
self._run_adb(['shell', 'setprop', 'net.dns1', '10.0.2.3'])
|
||||
|
||||
self.install_gecko()
|
||||
|
||||
def _save_logcat_proc(self, filename, cmd):
|
||||
self.logcat_proc = LogcatProc(filename, cmd)
|
||||
|
@ -311,6 +320,21 @@ class Emulator(object):
|
|||
self.logcat_proc.waitForFinish()
|
||||
self.logcat_proc = None
|
||||
|
||||
def install_gecko(self):
|
||||
"""
|
||||
Install gecko into the emulator using adb push. Restart b2g after the
|
||||
installation.
|
||||
"""
|
||||
if not self.gecko_path:
|
||||
return
|
||||
# need to remount so we can write to /system/b2g
|
||||
self._run_adb(['remount'])
|
||||
self._run_adb(['shell', 'stop', 'b2g'])
|
||||
print 'installing gecko binaries'
|
||||
self._run_adb(['push', self.gecko_path, '/system/b2g'])
|
||||
print 'restarting B2G'
|
||||
self._run_adb(['shell', 'start', 'b2g'])
|
||||
|
||||
def rotate_log(self, srclog, index=1):
|
||||
""" Rotate a logfile, by recursively rotating logs further in the sequence,
|
||||
deleting the last file if necessary.
|
||||
|
@ -349,8 +373,7 @@ class Emulator(object):
|
|||
local_port = s.getsockname()[1]
|
||||
s.close()
|
||||
|
||||
output = self._run_adb(['-s', 'emulator-%d' % self.port,
|
||||
'forward',
|
||||
output = self._run_adb(['forward',
|
||||
'tcp:%d' % local_port,
|
||||
'tcp:%d' % remote_port])
|
||||
|
||||
|
@ -374,4 +397,3 @@ class Emulator(object):
|
|||
print traceback.format_exc()
|
||||
time.sleep(1)
|
||||
return False
|
||||
|
||||
|
|
|
@ -85,9 +85,10 @@ class Marionette(object):
|
|||
CONTEXT_CONTENT = 'content'
|
||||
|
||||
def __init__(self, host='localhost', port=2828, bin=None, profile=None,
|
||||
emulator=None, sdcard= None, emulatorBinary=None, emulatorImg=None,
|
||||
emulator_res='480x800', connectToRunningEmulator=False,
|
||||
homedir=None, baseurl=None, noWindow=False, logcat_dir=None):
|
||||
emulator=None, sdcard=None, emulatorBinary=None,
|
||||
emulatorImg=None, emulator_res='480x800', gecko_path=None,
|
||||
connectToRunningEmulator=False, homedir=None, baseurl=None,
|
||||
noWindow=False, logcat_dir=None):
|
||||
self.host = host
|
||||
self.port = self.local_port = port
|
||||
self.bin = bin
|
||||
|
@ -101,6 +102,7 @@ class Marionette(object):
|
|||
self.baseurl = baseurl
|
||||
self.noWindow = noWindow
|
||||
self.logcat_dir = logcat_dir
|
||||
self.gecko_path = gecko_path
|
||||
|
||||
if bin:
|
||||
self.instance = GeckoInstance(host=self.host, port=self.port,
|
||||
|
@ -115,13 +117,16 @@ class Marionette(object):
|
|||
sdcard=sdcard,
|
||||
emulatorBinary=emulatorBinary,
|
||||
userdata=emulatorImg,
|
||||
res=emulator_res)
|
||||
res=emulator_res,
|
||||
gecko_path=self.gecko_path)
|
||||
self.emulator.start()
|
||||
self.port = self.emulator.setup_port_forwarding(self.port)
|
||||
assert(self.emulator.wait_for_port())
|
||||
|
||||
if connectToRunningEmulator:
|
||||
self.emulator = Emulator(homedir=homedir, logcat_dir=self.logcat_dir)
|
||||
self.emulator = Emulator(homedir=homedir,
|
||||
logcat_dir=self.logcat_dir,
|
||||
gecko_path=self.gecko_path)
|
||||
self.emulator.connect()
|
||||
self.port = self.emulator.setup_port_forwarding(self.port)
|
||||
assert(self.emulator.wait_for_port())
|
||||
|
|
|
@ -129,7 +129,8 @@ class MarionetteTestCase(CommonTestCase):
|
|||
emulatorBinary=self.marionette.emulator.binary,
|
||||
homedir=self.marionette.homedir,
|
||||
baseurl=self.marionette.baseurl,
|
||||
noWindow=self.marionette.noWindow)
|
||||
noWindow=self.marionette.noWindow,
|
||||
gecko_path=self.marionette.gecko_path)
|
||||
qemu.start_session()
|
||||
self.marionette.extra_emulators.append(qemu)
|
||||
else:
|
||||
|
|
|
@ -171,7 +171,8 @@ class MarionetteTestRunner(object):
|
|||
bin=None, profile=None, autolog=False, revision=None,
|
||||
es_server=None, rest_server=None, logger=None,
|
||||
testgroup="marionette", noWindow=False, logcat_dir=None,
|
||||
xml_output=None, repeat=0, perf=False, perfserv=None):
|
||||
xml_output=None, repeat=0, perf=False, perfserv=None,
|
||||
gecko_path=None):
|
||||
self.address = address
|
||||
self.emulator = emulator
|
||||
self.emulatorBinary = emulatorBinary
|
||||
|
@ -196,6 +197,7 @@ class MarionetteTestRunner(object):
|
|||
self.repeat = repeat
|
||||
self.perf = perf
|
||||
self.perfserv = perfserv
|
||||
self.gecko_path = gecko_path
|
||||
|
||||
# set up test handlers
|
||||
self.test_handlers = []
|
||||
|
@ -253,7 +255,8 @@ class MarionetteTestRunner(object):
|
|||
connectToRunningEmulator=True,
|
||||
homedir=self.homedir,
|
||||
baseurl=self.baseurl,
|
||||
logcat_dir=self.logcat_dir)
|
||||
logcat_dir=self.logcat_dir,
|
||||
gecko_path=self.gecko_path)
|
||||
else:
|
||||
self.marionette = Marionette(host=host,
|
||||
port=int(port),
|
||||
|
@ -266,7 +269,8 @@ class MarionetteTestRunner(object):
|
|||
homedir=self.homedir,
|
||||
baseurl=self.baseurl,
|
||||
noWindow=self.noWindow,
|
||||
logcat_dir=self.logcat_dir)
|
||||
logcat_dir=self.logcat_dir,
|
||||
gecko_path=self.gecko_path)
|
||||
else:
|
||||
raise Exception("must specify binary, address or emulator")
|
||||
|
||||
|
@ -604,6 +608,10 @@ def parse_options():
|
|||
default=0, help='number of times to repeat the test(s).')
|
||||
parser.add_option('-x', '--xml-output', action='store', dest='xml_output',
|
||||
help='XML output.')
|
||||
parser.add_option('--gecko-path', dest='gecko_path', action='store',
|
||||
default=None,
|
||||
help='path to B2G gecko binaries that should be '
|
||||
'installed on the device or emulator')
|
||||
|
||||
options, tests = parser.parse_args()
|
||||
|
||||
|
@ -653,7 +661,8 @@ def startTestRunner(runner_class, options, tests):
|
|||
xml_output=options.xml_output,
|
||||
repeat=options.repeat,
|
||||
perf=options.perf,
|
||||
perfserv=options.perfserv)
|
||||
perfserv=options.perfserv,
|
||||
gecko_path=options.gecko_path)
|
||||
runner.run_tests(tests, testtype=options.type)
|
||||
return runner
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ except (OSError, IOError):
|
|||
deps = ['manifestdestiny', 'mozhttpd >= 0.3',
|
||||
'mozprocess == 0.5', 'mozrunner == 5.10']
|
||||
|
||||
setup(name='marionette',
|
||||
setup(name='marionette_client',
|
||||
version=version,
|
||||
description="Marionette test automation client",
|
||||
long_description=description,
|
||||
|
|
|
@ -316,12 +316,10 @@ var DebuggerServer = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Remove the connection from the debugging server and shut down the server
|
||||
* if no other connections are open.
|
||||
* Remove the connection from the debugging server.
|
||||
*/
|
||||
_connectionClosed: function DH_connectionClosed(aConnection) {
|
||||
delete this._connections[aConnection.prefix];
|
||||
this.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -59,35 +59,10 @@ function test_socket_conn()
|
|||
|
||||
function test_socket_shutdown()
|
||||
{
|
||||
let count = 0;
|
||||
wait_for_server_shutdown(count);
|
||||
}
|
||||
|
||||
function wait_for_server_shutdown(aCount)
|
||||
{
|
||||
do_timeout(100, function() {
|
||||
dump("count: "+aCount+" ");
|
||||
if (++aCount > 20) {
|
||||
do_throw("Timed out waiting for the server to shut down.");
|
||||
return;
|
||||
}
|
||||
if (DebuggerServer.initialized) {
|
||||
wait_for_server_shutdown(aCount);
|
||||
return;
|
||||
}
|
||||
real_test_socket_shutdown(aCount);
|
||||
});
|
||||
}
|
||||
|
||||
function real_test_socket_shutdown()
|
||||
{
|
||||
// After the last conection was closed, the server must be initialized again.
|
||||
// Allow incoming connections.
|
||||
DebuggerServer.init(function () true);
|
||||
DebuggerServer.addActors("resource://test/testactors.js");
|
||||
|
||||
do_check_eq(DebuggerServer._socketConnections, 1);
|
||||
do_check_true(DebuggerServer.closeListener());
|
||||
do_check_eq(DebuggerServer._socketConnections, 0);
|
||||
// Make sure closing a non-started listener does nothing.
|
||||
// Make sure closing the listener twice does nothing.
|
||||
do_check_false(DebuggerServer.closeListener());
|
||||
do_check_eq(DebuggerServer._socketConnections, 0);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче