This commit is contained in:
Ryan VanderMeulen 2012-09-27 23:05:53 -04:00
Родитель e045c1720b 5330540831
Коммит ef0d8282c6
16 изменённых файлов: 442 добавлений и 94 удалений

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

@ -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,
'-kernel', self.kernelImg,
'-sysdir', self.sysDir,
'-data', self.dataImg ]
qemuArgs = [self.binary,
'-kernel', self.kernelImg,
'-sysdir', self.sysDir,
'-data', self.dataImg]
if self._tmp_sdcard:
qemuArgs.extend(['-sdcard', self._tmp_sdcard])
if self.noWindow:
@ -147,16 +149,16 @@ class Emulator(object):
return self.proc is not None and self.proc.poll() is None
else:
return self.port is not None
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)
retcode = sd.wait()
if retcode:
raise Exception('unable to create sdcard : exit code %d: %s'
% (retcode, adb.stdout.read()))
return None
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)
retcode = sd.wait()
if retcode:
raise Exception('unable to create sdcard : exit code %d: %s'
% (retcode, sd.stdout.read()))
return None
def _check_for_adb(self):
host_dir = "linux-x86"
@ -166,12 +168,13 @@ class Emulator(object):
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
if adb.wait() == 0:
self.adb = adb.stdout.read().strip() # remove trailing newline
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,10 +183,13 @@ 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:
raise Exception('adb terminated with exit code %d: %s'
raise Exception('adb terminated with exit code %d: %s'
% (retcode, adb.stdout.read()))
return adb.stdout.read()
@ -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:
@ -219,7 +225,7 @@ class Emulator(object):
if self._tmp_userdata:
os.remove(self._tmp_userdata)
self._tmp_userdata = None
if self._tmp_sdcard:
if self._tmp_sdcard:
os.remove(self._tmp_sdcard)
self._tmp_sdcard = None
return retcode
@ -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())
@ -262,7 +267,7 @@ class Marionette(object):
def current_window_handle(self):
self.window = self._send_message('getWindow', 'value')
return self.window
@property
def title(self):
response = self._send_message('getTitle', 'value')

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

@ -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,7 +608,11 @@ 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()
if not tests:
@ -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);