Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | # file, # You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 4 | |
michael@0 | 5 | from __future__ import unicode_literals |
michael@0 | 6 | |
michael@0 | 7 | import sys |
michael@0 | 8 | import os |
michael@0 | 9 | import stat |
michael@0 | 10 | import platform |
michael@0 | 11 | import urllib2 |
michael@0 | 12 | import errno |
michael@0 | 13 | |
michael@0 | 14 | from mach.decorators import ( |
michael@0 | 15 | CommandArgument, |
michael@0 | 16 | CommandProvider, |
michael@0 | 17 | Command, |
michael@0 | 18 | ) |
michael@0 | 19 | |
michael@0 | 20 | from mozbuild.base import MachCommandBase |
michael@0 | 21 | |
michael@0 | 22 | |
michael@0 | 23 | @CommandProvider |
michael@0 | 24 | class SearchProvider(object): |
michael@0 | 25 | @Command('mxr', category='misc', |
michael@0 | 26 | description='Search for something in MXR.') |
michael@0 | 27 | @CommandArgument('term', nargs='+', help='Term(s) to search for.') |
michael@0 | 28 | def mxr(self, term): |
michael@0 | 29 | import webbrowser |
michael@0 | 30 | term = ' '.join(term) |
michael@0 | 31 | uri = 'https://mxr.mozilla.org/mozilla-central/search?string=%s' % term |
michael@0 | 32 | webbrowser.open_new_tab(uri) |
michael@0 | 33 | |
michael@0 | 34 | @Command('dxr', category='misc', |
michael@0 | 35 | description='Search for something in DXR.') |
michael@0 | 36 | @CommandArgument('term', nargs='+', help='Term(s) to search for.') |
michael@0 | 37 | def dxr(self, term): |
michael@0 | 38 | import webbrowser |
michael@0 | 39 | term = ' '.join(term) |
michael@0 | 40 | uri = 'http://dxr.mozilla.org/search?tree=mozilla-central&q=%s' % term |
michael@0 | 41 | webbrowser.open_new_tab(uri) |
michael@0 | 42 | |
michael@0 | 43 | @Command('mdn', category='misc', |
michael@0 | 44 | description='Search for something on MDN.') |
michael@0 | 45 | @CommandArgument('term', nargs='+', help='Term(s) to search for.') |
michael@0 | 46 | def mdn(self, term): |
michael@0 | 47 | import webbrowser |
michael@0 | 48 | term = ' '.join(term) |
michael@0 | 49 | uri = 'https://developer.mozilla.org/search?q=%s' % term |
michael@0 | 50 | webbrowser.open_new_tab(uri) |
michael@0 | 51 | |
michael@0 | 52 | @Command('google', category='misc', |
michael@0 | 53 | description='Search for something on Google.') |
michael@0 | 54 | @CommandArgument('term', nargs='+', help='Term(s) to search for.') |
michael@0 | 55 | def google(self, term): |
michael@0 | 56 | import webbrowser |
michael@0 | 57 | term = ' '.join(term) |
michael@0 | 58 | uri = 'https://www.google.com/search?q=%s' % term |
michael@0 | 59 | webbrowser.open_new_tab(uri) |
michael@0 | 60 | |
michael@0 | 61 | @Command('search', category='misc', |
michael@0 | 62 | description='Search for something on the Internets. ' |
michael@0 | 63 | 'This will open 3 new browser tabs and search for the term on Google, ' |
michael@0 | 64 | 'MDN, and MXR.') |
michael@0 | 65 | @CommandArgument('term', nargs='+', help='Term(s) to search for.') |
michael@0 | 66 | def search(self, term): |
michael@0 | 67 | self.google(term) |
michael@0 | 68 | self.mdn(term) |
michael@0 | 69 | self.mxr(term) |
michael@0 | 70 | |
michael@0 | 71 | |
michael@0 | 72 | class Interface(object): |
michael@0 | 73 | ''' |
michael@0 | 74 | Represents an XPIDL interface, in what file it is defined, what it derives |
michael@0 | 75 | from, what its uuid is, and where in the source file the uuid is. |
michael@0 | 76 | ''' |
michael@0 | 77 | def __init__(self, filename, production): |
michael@0 | 78 | import xpidl |
michael@0 | 79 | assert isinstance(production, xpidl.Interface) |
michael@0 | 80 | self.name = production.name |
michael@0 | 81 | self.base = production.base |
michael@0 | 82 | self.filename = filename |
michael@0 | 83 | self.uuid = production.attributes.uuid |
michael@0 | 84 | location = production.location |
michael@0 | 85 | data = location._lexdata |
michael@0 | 86 | attr_pos = data.rfind(b'[', 0, location._lexpos) |
michael@0 | 87 | # uuid is always lowercase, but actual file content may not be. |
michael@0 | 88 | self.uuid_pos = data[attr_pos:location._lexpos].lower() \ |
michael@0 | 89 | .rfind(self.uuid) + attr_pos |
michael@0 | 90 | |
michael@0 | 91 | |
michael@0 | 92 | class InterfaceRegistry(object): |
michael@0 | 93 | ''' |
michael@0 | 94 | Tracks XPIDL interfaces, and allow to search them by name and by the |
michael@0 | 95 | interface they derive from. |
michael@0 | 96 | ''' |
michael@0 | 97 | def __init__(self): |
michael@0 | 98 | self.by_name = {} |
michael@0 | 99 | self.by_base = {} |
michael@0 | 100 | |
michael@0 | 101 | def get_by_name(self, name): |
michael@0 | 102 | return self.by_name.get(name, []) |
michael@0 | 103 | |
michael@0 | 104 | def get_by_base(self, base): |
michael@0 | 105 | return self.by_base.get(base, []) |
michael@0 | 106 | |
michael@0 | 107 | def add(self, interface): |
michael@0 | 108 | l = self.by_name.setdefault(interface.name, []) |
michael@0 | 109 | l.append(interface) |
michael@0 | 110 | l = self.by_base.setdefault(interface.base, []) |
michael@0 | 111 | l.append(interface) |
michael@0 | 112 | |
michael@0 | 113 | |
michael@0 | 114 | class IDLUpdater(object): |
michael@0 | 115 | ''' |
michael@0 | 116 | Updates interfaces uuids in IDL files. |
michael@0 | 117 | ''' |
michael@0 | 118 | def __init__(self, interfaces): |
michael@0 | 119 | from mozpack.copier import FileRegistry |
michael@0 | 120 | self.interfaces = interfaces; |
michael@0 | 121 | self.registry = FileRegistry() |
michael@0 | 122 | |
michael@0 | 123 | def add(self, name): |
michael@0 | 124 | for interface in self.interfaces.get_by_name(name): |
michael@0 | 125 | self._add(interface) |
michael@0 | 126 | |
michael@0 | 127 | def _add(self, interface): |
michael@0 | 128 | from mozpack.files import GeneratedFile |
michael@0 | 129 | from uuid import uuid4 |
michael@0 | 130 | path = interface.filename |
michael@0 | 131 | if not self.registry.contains(path): |
michael@0 | 132 | self.registry.add(path, GeneratedFile(open(path).read())) |
michael@0 | 133 | content = self.registry[path].content |
michael@0 | 134 | content = content[:interface.uuid_pos] + str(uuid4()) + \ |
michael@0 | 135 | content[interface.uuid_pos + len(interface.uuid):] |
michael@0 | 136 | self.registry[path].content = content |
michael@0 | 137 | |
michael@0 | 138 | # Recurse through all the interfaces deriving from this one |
michael@0 | 139 | for derived in self.interfaces.get_by_base(interface.name): |
michael@0 | 140 | self._add(derived) |
michael@0 | 141 | |
michael@0 | 142 | def update(self): |
michael@0 | 143 | for p, f in self.registry: |
michael@0 | 144 | f.copy(p) |
michael@0 | 145 | |
michael@0 | 146 | |
michael@0 | 147 | @CommandProvider |
michael@0 | 148 | class UUIDProvider(object): |
michael@0 | 149 | @Command('uuid', category='misc', |
michael@0 | 150 | description='Generate a uuid.') |
michael@0 | 151 | @CommandArgument('--format', '-f', choices=['idl', 'cpp', 'c++'], |
michael@0 | 152 | help='Output format for the generated uuid.') |
michael@0 | 153 | def uuid(self, format=None): |
michael@0 | 154 | import uuid |
michael@0 | 155 | u = uuid.uuid4() |
michael@0 | 156 | if format in [None, 'idl']: |
michael@0 | 157 | print(u) |
michael@0 | 158 | if format is None: |
michael@0 | 159 | print('') |
michael@0 | 160 | if format in [None, 'cpp', 'c++']: |
michael@0 | 161 | u = u.hex |
michael@0 | 162 | print('{ 0x%s, 0x%s, 0x%s, \\' % (u[0:8], u[8:12], u[12:16])) |
michael@0 | 163 | pairs = tuple(map(lambda n: u[n:n+2], range(16, 32, 2))) |
michael@0 | 164 | print((' { ' + '0x%s, ' * 7 + '0x%s } }') % pairs) |
michael@0 | 165 | |
michael@0 | 166 | @Command('update-uuids', category='misc', |
michael@0 | 167 | description='Update IDL files with new UUIDs.') |
michael@0 | 168 | @CommandArgument('--path', default='.', |
michael@0 | 169 | help='Base path under which uuids will be searched.') |
michael@0 | 170 | @CommandArgument('interfaces', nargs='+', |
michael@0 | 171 | help='Changed interfaces whose UUIDs need to be updated. ' + |
michael@0 | 172 | 'Their descendants are updated as well.') |
michael@0 | 173 | def update_uuids(self, path, interfaces): |
michael@0 | 174 | import os |
michael@0 | 175 | import xpidl |
michael@0 | 176 | from mozpack.files import FileFinder |
michael@0 | 177 | import mozpack.path |
michael@0 | 178 | from tempfile import mkdtemp |
michael@0 | 179 | |
michael@0 | 180 | finder = FileFinder(path, find_executables=False) |
michael@0 | 181 | # Avoid creating xpidllex and xpidlyacc in the current directory. |
michael@0 | 182 | tmpdir = mkdtemp() |
michael@0 | 183 | try: |
michael@0 | 184 | parser = xpidl.IDLParser(outputdir=tmpdir) |
michael@0 | 185 | registry = InterfaceRegistry() |
michael@0 | 186 | for p, f in finder.find('**/*.idl'): |
michael@0 | 187 | p = mozpack.path.join(path, p) |
michael@0 | 188 | try: |
michael@0 | 189 | content = f.open().read() |
michael@0 | 190 | idl = parser.parse(content, filename=p) |
michael@0 | 191 | except Exception: |
michael@0 | 192 | continue |
michael@0 | 193 | for prod in idl.productions: |
michael@0 | 194 | if isinstance(prod, xpidl.Interface): |
michael@0 | 195 | registry.add(Interface(p, prod)) |
michael@0 | 196 | finally: |
michael@0 | 197 | import shutil |
michael@0 | 198 | shutil.rmtree(tmpdir) |
michael@0 | 199 | |
michael@0 | 200 | updates = IDLUpdater(registry) |
michael@0 | 201 | |
michael@0 | 202 | for interface in interfaces: |
michael@0 | 203 | updates.add(interface) |
michael@0 | 204 | |
michael@0 | 205 | updates.update() |
michael@0 | 206 | |
michael@0 | 207 | @CommandProvider |
michael@0 | 208 | class PastebinProvider(object): |
michael@0 | 209 | @Command('pastebin', category='misc', |
michael@0 | 210 | description='Command line interface to pastebin.mozilla.org.') |
michael@0 | 211 | @CommandArgument('--language', default=None, |
michael@0 | 212 | help='Language to use for syntax highlighting') |
michael@0 | 213 | @CommandArgument('--poster', default=None, |
michael@0 | 214 | help='Specify your name for use with pastebin.mozilla.org') |
michael@0 | 215 | @CommandArgument('--duration', default='day', |
michael@0 | 216 | choices=['d', 'day', 'm', 'month', 'f', 'forever'], |
michael@0 | 217 | help='Keep for specified duration (default: %(default)s)') |
michael@0 | 218 | @CommandArgument('file', nargs='?', default=None, |
michael@0 | 219 | help='Specify the file to upload to pastebin.mozilla.org') |
michael@0 | 220 | |
michael@0 | 221 | def pastebin(self, language, poster, duration, file): |
michael@0 | 222 | import sys |
michael@0 | 223 | import urllib |
michael@0 | 224 | |
michael@0 | 225 | URL = 'http://pastebin.mozilla.org/' |
michael@0 | 226 | |
michael@0 | 227 | FILE_TYPES = [{'value': 'text', 'name': 'None', 'extension': 'txt'}, |
michael@0 | 228 | {'value': 'bash', 'name': 'Bash', 'extension': 'sh'}, |
michael@0 | 229 | {'value': 'c', 'name': 'C', 'extension': 'c'}, |
michael@0 | 230 | {'value': 'cpp', 'name': 'C++', 'extension': 'cpp'}, |
michael@0 | 231 | {'value': 'html4strict', 'name': 'HTML', 'extension': 'html'}, |
michael@0 | 232 | {'value': 'javascript', 'name': 'Javascript', 'extension': 'js'}, |
michael@0 | 233 | {'value': 'javascript', 'name': 'Javascript', 'extension': 'jsm'}, |
michael@0 | 234 | {'value': 'lua', 'name': 'Lua', 'extension': 'lua'}, |
michael@0 | 235 | {'value': 'perl', 'name': 'Perl', 'extension': 'pl'}, |
michael@0 | 236 | {'value': 'php', 'name': 'PHP', 'extension': 'php'}, |
michael@0 | 237 | {'value': 'python', 'name': 'Python', 'extension': 'py'}, |
michael@0 | 238 | {'value': 'ruby', 'name': 'Ruby', 'extension': 'rb'}, |
michael@0 | 239 | {'value': 'css', 'name': 'CSS', 'extension': 'css'}, |
michael@0 | 240 | {'value': 'diff', 'name': 'Diff', 'extension': 'diff'}, |
michael@0 | 241 | {'value': 'ini', 'name': 'INI file', 'extension': 'ini'}, |
michael@0 | 242 | {'value': 'java', 'name': 'Java', 'extension': 'java'}, |
michael@0 | 243 | {'value': 'xml', 'name': 'XML', 'extension': 'xml'}, |
michael@0 | 244 | {'value': 'xml', 'name': 'XML', 'extension': 'xul'}] |
michael@0 | 245 | |
michael@0 | 246 | lang = '' |
michael@0 | 247 | |
michael@0 | 248 | if file: |
michael@0 | 249 | try: |
michael@0 | 250 | with open(file, 'r') as f: |
michael@0 | 251 | content = f.read() |
michael@0 | 252 | # TODO: Use mime-types instead of extensions; suprocess('file <f_name>') |
michael@0 | 253 | # Guess File-type based on file extension |
michael@0 | 254 | extension = file.split('.')[-1] |
michael@0 | 255 | for l in FILE_TYPES: |
michael@0 | 256 | if extension == l['extension']: |
michael@0 | 257 | print('Identified file as %s' % l['name']) |
michael@0 | 258 | lang = l['value'] |
michael@0 | 259 | except IOError: |
michael@0 | 260 | print('ERROR. No such file') |
michael@0 | 261 | return 1 |
michael@0 | 262 | else: |
michael@0 | 263 | content = sys.stdin.read() |
michael@0 | 264 | duration = duration[0] |
michael@0 | 265 | |
michael@0 | 266 | if language: |
michael@0 | 267 | lang = language |
michael@0 | 268 | |
michael@0 | 269 | |
michael@0 | 270 | params = [ |
michael@0 | 271 | ('parent_pid', ''), |
michael@0 | 272 | ('format', lang), |
michael@0 | 273 | ('code2', content), |
michael@0 | 274 | ('poster', poster), |
michael@0 | 275 | ('expiry', duration), |
michael@0 | 276 | ('paste', 'Send')] |
michael@0 | 277 | |
michael@0 | 278 | data = urllib.urlencode(params) |
michael@0 | 279 | print('Uploading ...') |
michael@0 | 280 | try: |
michael@0 | 281 | req = urllib2.Request(URL, data) |
michael@0 | 282 | response = urllib2.urlopen(req) |
michael@0 | 283 | http_response_code = response.getcode() |
michael@0 | 284 | if http_response_code == 200: |
michael@0 | 285 | print(response.geturl()) |
michael@0 | 286 | else: |
michael@0 | 287 | print('Could not upload the file, ' |
michael@0 | 288 | 'HTTP Response Code %s' %(http_response_code)) |
michael@0 | 289 | except urllib2.URLError: |
michael@0 | 290 | print('ERROR. Could not connect to pastebin.mozilla.org.') |
michael@0 | 291 | return 1 |
michael@0 | 292 | return 0 |
michael@0 | 293 | |
michael@0 | 294 | |
michael@0 | 295 | @CommandProvider |
michael@0 | 296 | class ReviewboardToolsProvider(MachCommandBase): |
michael@0 | 297 | @Command('rbt', category='devenv', allow_all_args=True, |
michael@0 | 298 | description='Run Reviewboard Tools') |
michael@0 | 299 | @CommandArgument('args', nargs='...', help='Arguments to rbt tool') |
michael@0 | 300 | def rbt(self, args): |
michael@0 | 301 | if not args: |
michael@0 | 302 | args = ['help'] |
michael@0 | 303 | |
michael@0 | 304 | self._activate_virtualenv() |
michael@0 | 305 | self.virtualenv_manager.install_pip_package('RBTools==0.6') |
michael@0 | 306 | |
michael@0 | 307 | from rbtools.commands.main import main |
michael@0 | 308 | |
michael@0 | 309 | # main() doesn't accept arguments and instead reads from sys.argv. So, |
michael@0 | 310 | # we fake it out. |
michael@0 | 311 | sys.argv = ['rbt'] + args |
michael@0 | 312 | return main() |
michael@0 | 313 | |
michael@0 | 314 | @CommandProvider |
michael@0 | 315 | class FormatProvider(MachCommandBase): |
michael@0 | 316 | @Command('clang-format', category='misc', |
michael@0 | 317 | description='Run clang-format on current changes') |
michael@0 | 318 | @CommandArgument('--show', '-s', action = 'store_true', |
michael@0 | 319 | help = 'Show diff output on instead of applying changes') |
michael@0 | 320 | def clang_format(self, show=False): |
michael@0 | 321 | plat = platform.system() |
michael@0 | 322 | fmt = plat.lower() + "/clang-format-3.5" |
michael@0 | 323 | fmt_diff = "clang-format-diff-3.5" |
michael@0 | 324 | |
michael@0 | 325 | # We are currently using a modified verion of clang-format hosted on people.mozilla.org. |
michael@0 | 326 | # This is a temporary work around until we upstream the necessary changes and we can use |
michael@0 | 327 | # a system version of clang-format. See bug 961541. |
michael@0 | 328 | if plat == "Windows": |
michael@0 | 329 | fmt += ".exe" |
michael@0 | 330 | else: |
michael@0 | 331 | arch = os.uname()[4] |
michael@0 | 332 | if (plat != "Linux" and plat != "Darwin") or arch != 'x86_64': |
michael@0 | 333 | print("Unsupported platform " + plat + "/" + arch + |
michael@0 | 334 | ". Supported platforms are Windows/*, Linux/x86_64 and Darwin/x86_64") |
michael@0 | 335 | return 1 |
michael@0 | 336 | |
michael@0 | 337 | os.chdir(self.topsrcdir) |
michael@0 | 338 | self.prompt = True |
michael@0 | 339 | |
michael@0 | 340 | try: |
michael@0 | 341 | if not self.locate_or_fetch(fmt): |
michael@0 | 342 | return 1 |
michael@0 | 343 | clang_format_diff = self.locate_or_fetch(fmt_diff) |
michael@0 | 344 | if not clang_format_diff: |
michael@0 | 345 | return 1 |
michael@0 | 346 | |
michael@0 | 347 | except urllib2.HTTPError as e: |
michael@0 | 348 | print("HTTP error {0}: {1}".format(e.code, e.reason)) |
michael@0 | 349 | return 1 |
michael@0 | 350 | |
michael@0 | 351 | from subprocess import Popen, PIPE |
michael@0 | 352 | |
michael@0 | 353 | if os.path.exists(".hg"): |
michael@0 | 354 | diff_process = Popen(["hg", "diff", "-U0", "-r", "tip^", |
michael@0 | 355 | "--include", "glob:**.c", "--include", "glob:**.cpp", "--include", "glob:**.h", |
michael@0 | 356 | "--exclude", "listfile:.clang-format-ignore"], stdout=PIPE) |
michael@0 | 357 | else: |
michael@0 | 358 | git_process = Popen(["git", "diff", "-U0", "HEAD^"], stdout=PIPE) |
michael@0 | 359 | try: |
michael@0 | 360 | diff_process = Popen(["filterdiff", "--include=*.h", "--include=*.cpp", |
michael@0 | 361 | "--exclude-from-file=.clang-format-ignore"], |
michael@0 | 362 | stdin=git_process.stdout, stdout=PIPE) |
michael@0 | 363 | except OSError as e: |
michael@0 | 364 | if e.errno == errno.ENOENT: |
michael@0 | 365 | print("Can't find filterdiff. Please install patchutils.") |
michael@0 | 366 | else: |
michael@0 | 367 | print("OSError {0}: {1}".format(e.code, e.reason)) |
michael@0 | 368 | return 1 |
michael@0 | 369 | |
michael@0 | 370 | |
michael@0 | 371 | args = [sys.executable, clang_format_diff, "-p1"] |
michael@0 | 372 | if not show: |
michael@0 | 373 | args.append("-i") |
michael@0 | 374 | cf_process = Popen(args, stdin=diff_process.stdout) |
michael@0 | 375 | return cf_process.communicate()[0] |
michael@0 | 376 | |
michael@0 | 377 | def locate_or_fetch(self, root): |
michael@0 | 378 | target = os.path.join(self._mach_context.state_dir, os.path.basename(root)) |
michael@0 | 379 | if not os.path.exists(target): |
michael@0 | 380 | site = "https://people.mozilla.org/~ajones/clang-format/" |
michael@0 | 381 | if self.prompt and raw_input("Download clang-format executables from {0} (yN)? ".format(site)).lower() != 'y': |
michael@0 | 382 | print("Download aborted.") |
michael@0 | 383 | return 1 |
michael@0 | 384 | self.prompt = False |
michael@0 | 385 | |
michael@0 | 386 | u = site + root |
michael@0 | 387 | print("Downloading {0} to {1}".format(u, target)) |
michael@0 | 388 | data = urllib2.urlopen(url=u).read() |
michael@0 | 389 | temp = target + ".tmp" |
michael@0 | 390 | with open(temp, "wb") as fh: |
michael@0 | 391 | fh.write(data) |
michael@0 | 392 | fh.close() |
michael@0 | 393 | os.chmod(temp, os.stat(temp).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) |
michael@0 | 394 | os.rename(temp, target) |
michael@0 | 395 | return target |
michael@0 | 396 |