1
# This Source Code Form is subject to the terms of the Mozilla Public
2
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
# You can obtain one at http://mozilla.org/MPL/2.0/.
5
__all__ = ['Profile', 'FirefoxProfile', 'ThunderbirdProfile']
11
from addons import AddonManager
12
from permissions import Permissions
13
from shutil import rmtree
18
import json as simplejson
20
class Profile(object):
21
"""Handles all operations regarding profile. Created new profiles, installs extensions,
22
sets preferences and handles cleanup."""
25
profile=None, # Path to the profile
26
addons=None, # String of one or list of addons to install
27
addon_manifests=None, # Manifest for addons, see http://ahal.ca/blog/2011/bulk-installing-fx-addons/
28
preferences=None, # Dictionary or class of preferences
29
locations=None, # locations to proxy
30
proxy=None, # setup a proxy - dict of server-loc,server-port,ssl-port
31
restore=True # If true remove all installed addons preferences when cleaning up
34
# if true, remove installed addons/prefs afterwards
35
self.restore = restore
37
# prefs files written to
38
self.written_prefs = set()
41
nonce = '%s %s' % (str(time.time()), uuid.uuid4())
42
self.delimeters = ('#MozRunner Prefs Start %s' % nonce,'#MozRunner Prefs End %s' % nonce)
44
# Handle profile creation
45
self.create_new = not profile
47
# Ensure we have a full path to the profile
48
self.profile = os.path.abspath(os.path.expanduser(profile))
49
if not os.path.exists(self.profile):
50
os.makedirs(self.profile)
52
self.profile = self.create_new_profile()
55
if hasattr(self.__class__, 'preferences'):
57
self.set_preferences(self.__class__.preferences)
58
self._preferences = preferences
60
# supplied preferences
61
if isinstance(preferences, dict):
63
preferences = preferences.items()
65
assert not [i for i in preferences
69
self.set_preferences(preferences)
72
self._locations = locations # store this for reconstruction
74
self.permissions = Permissions(self.profile, locations)
75
prefs_js, user_js = self.permissions.network_prefs(proxy)
76
self.set_preferences(prefs_js, 'prefs.js')
77
self.set_preferences(user_js)
79
# handle addon installation
80
self.addon_manager = AddonManager(self.profile)
81
self.addon_manager.install_addons(addons, addon_manifests)
84
"""returns whether the profile exists or not"""
85
return os.path.exists(self.profile)
89
reset the profile to the beginning state
95
profile = self.profile
96
self.__init__(profile=profile,
97
addons=self.addon_manager.installed_addons,
98
addon_manifests=self.addon_manager.installed_manifests,
99
preferences=self._preferences,
100
locations=self._locations,
103
def create_new_profile(self):
104
"""Create a new clean profile in tmp which is a simple empty folder"""
105
profile = tempfile.mkdtemp(suffix='.mozrunner')
109
### methods for preferences
111
def set_preferences(self, preferences, filename='user.js'):
112
"""Adds preferences dict to profile preferences"""
116
prefs_file = os.path.join(self.profile, filename)
117
f = open(prefs_file, 'a')
121
# note what files we've touched
122
self.written_prefs.add(filename)
125
if isinstance(preferences, dict):
126
# order doesn't matter
127
preferences = preferences.items()
129
# write the preferences
130
f.write('\n%s\n' % self.delimeters[0])
131
_prefs = [(simplejson.dumps(k), simplejson.dumps(v) )
132
for k, v in preferences]
134
f.write('user_pref(%s, %s);\n' % _pref)
135
f.write('%s\n' % self.delimeters[1])
138
def pop_preferences(self, filename):
140
pop the last set of preferences added
141
returns True if popped
144
lines = file(os.path.join(self.profile, filename)).read().splitlines()
145
def last_index(_list, value):
147
returns the last index of an item;
148
this should actually be part of python code but it isn't
150
for index in reversed(range(len(_list))):
151
if _list[index] == value:
153
s = last_index(lines, self.delimeters[0])
154
e = last_index(lines, self.delimeters[1])
156
# ensure both markers are found
158
assert e is None, '%s found without %s' % (self.delimeters[1], self.delimeters[0])
159
return False # no preferences found
161
assert s is None, '%s found without %s' % (self.delimeters[0], self.delimeters[1])
163
# ensure the markers are in the proper order
164
assert e > s, '%s found at %s, while %s found at %s' % (self.delimeters[1], e, self.delimeters[0], s)
167
cleaned_prefs = '\n'.join(lines[:s] + lines[e+1:])
168
f = file(os.path.join(self.profile, 'user.js'), 'w')
169
f.write(cleaned_prefs)
173
def clean_preferences(self):
174
"""Removed preferences added by mozrunner."""
175
for filename in self.written_prefs:
176
if not os.path.exists(os.path.join(self.profile, filename)):
177
# file has been deleted
180
if not self.pop_preferences(filename):
185
def _cleanup_error(self, function, path, excinfo):
186
""" Specifically for windows we need to handle the case where the windows
187
process has not yet relinquished handles on files, so we do a wait/try
188
construct and timeout if we can't get a clear road to deletion
192
from exceptions import WindowsError
193
from time import sleep
194
def is_file_locked():
195
return excinfo[0] is WindowsError and excinfo[1].winerror == 32
197
if excinfo[0] is WindowsError and excinfo[1].winerror == 32:
198
# Then we're on windows, wait to see if the file gets unlocked
209
# We can't re-raise an error, so we'll hope the stuff above us will throw
213
"""Cleanup operations for the profile."""
216
if os.path.exists(self.profile):
217
rmtree(self.profile, onerror=self._cleanup_error)
219
self.clean_preferences()
220
self.addon_manager.clean_addons()
221
self.permissions.clean_db()
225
class FirefoxProfile(Profile):
226
"""Specialized Profile subclass for Firefox"""
227
preferences = {# Don't automatically update the application
228
'app.update.enabled' : False,
229
# Don't restore the last open set of tabs if the browser has crashed
230
'browser.sessionstore.resume_from_crash': False,
231
# Don't check for the default web browser
232
'browser.shell.checkDefaultBrowser' : False,
233
# Don't warn on exit when multiple tabs are open
234
'browser.tabs.warnOnClose' : False,
235
# Don't warn when exiting the browser
236
'browser.warnOnQuit': False,
237
# Only install add-ons from the profile and the application scope
238
# Also ensure that those are not getting disabled.
239
# see: https://developer.mozilla.org/en/Installing_extensions
240
'extensions.enabledScopes' : 5,
241
'extensions.autoDisableScopes' : 10,
242
# Don't install distribution add-ons from the app folder
243
'extensions.installDistroAddons' : False,
244
# Dont' run the add-on compatibility check during start-up
245
'extensions.showMismatchUI' : False,
246
# Don't automatically update add-ons
247
'extensions.update.enabled' : False,
248
# Don't open a dialog to show available add-on updates
249
'extensions.update.notifyUser' : False,
250
# Suppress automatic safe mode after crashes
251
'toolkit.startup.max_resumed_crashes' : -1,
252
# Enable test mode to run multiple tests in parallel
253
'focusmanager.testmode' : True,
256
class ThunderbirdProfile(Profile):
257
preferences = {'extensions.update.enabled' : False,
258
'extensions.update.notifyUser' : False,
259
'browser.shell.checkDefaultBrowser' : False,
260
'browser.tabs.warnOnClose' : False,
261
'browser.warnOnQuit': False,
262
'browser.sessionstore.resume_from_crash': False,
263
# prevents the 'new e-mail address' wizard on new profile
264
'mail.provider.enabled': False,