Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(113)

Side by Side Diff: tools/cc-frame-viewer/third_party/py-chrome-app/chromeapp.py

Issue 15736032: Remove old cc-frame-viewer now that it is upstreamed into trace_viewer (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4 import base64
5 import hashlib
6 import heapq
7 import json
8 import logging
9 import os
10 import re
11 import select
12 import socket
13 import subprocess
14 import sys
15 import time
16 import urlparse
17
18 import BaseHTTPServer
19
20 _unittests_running = False
21
22 class ChromeNotFoundException(Exception):
23 pass
24
25 class _PossibleDesktopBrowser(object):
26 def __init__(self, browser_type, executable):
27 self.browser_type = browser_type
28 self.local_executable = executable
29
30 def __repr__(self):
31 return '_PossibleDesktopBrowser(browser_type=%s)' % self.browser_type
32
33 def _samefile(a, b):
34 if sys.platform != 'win32':
35 return os.path.samefile(a, b)
36 return os.path.abspath(a) == os.path.abspath(b)
37
38 def _FindAllAvailableBrowsers():
39 """Finds all the desktop browsers available on this machine."""
40 browsers = []
41
42 has_display = True
43 if (sys.platform.startswith('linux') and
44 os.getenv('DISPLAY') == None):
45 has_display = False
46
47 def AddIfFound(browser_type, browser_dir, app_name):
48 app = os.path.join(browser_dir, app_name)
49 if os.path.exists(app):
50 browsers.append(_PossibleDesktopBrowser(browser_type,
51 app))
52 return True
53 return False
54
55 # Look for a browser in the standard chrome build locations.
56 if sys.platform == 'darwin':
57 chromium_app_name = 'Chromium.app/Contents/MacOS/Chromium'
58 elif sys.platform.startswith('linux'):
59 chromium_app_name = 'chrome'
60 elif sys.platform.startswith('win'):
61 chromium_app_name = 'chrome.exe'
62 else:
63 raise Exception('Platform not recognized')
64
65 # Mac-specific options.
66 if sys.platform == 'darwin':
67 mac_canary = ('/Applications/Google Chrome Canary.app/'
68 'Contents/MacOS/Google Chrome Canary')
69 mac_system = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
70 if os.path.exists(mac_canary):
71 browsers.append(_PossibleDesktopBrowser('canary',
72 mac_canary))
73
74 if os.path.exists(mac_system):
75 browsers.append(_PossibleDesktopBrowser('system',
76 mac_system))
77
78 # Linux specific options.
79 if sys.platform.startswith('linux'):
80 # Look for a google-chrome instance.
81 found = False
82 try:
83 with open(os.devnull, 'w') as devnull:
84 found = subprocess.call(['google-chrome', '--version'],
85 stdout=devnull, stderr=devnull) == 0
86 except OSError:
87 pass
88 if found:
89 browsers.append(
90 _PossibleDesktopBrowser('system',
91 'google-chrome'))
92
93 # Win32-specific options.
94 if sys.platform.startswith('win'):
95 system_path = os.path.join('Google', 'Chrome', 'Application')
96 canary_path = os.path.join('Google', 'Chrome SxS', 'Application')
97
98 win_search_paths = [os.getenv('PROGRAMFILES(X86)'),
99 os.getenv('PROGRAMFILES'),
100 os.getenv('LOCALAPPDATA')]
101
102 for path in win_search_paths:
103 if not path:
104 continue
105 if AddIfFound('canary', os.path.join(path, canary_path),
106 chromium_app_name):
107 break
108
109 for path in win_search_paths:
110 if not path:
111 continue
112 if AddIfFound('system', os.path.join(path, system_path),
113 chromium_app_name):
114 break
115
116 if len(browsers) and not has_display:
117 logging.warning(
118 'Found (%s), but you do not have a DISPLAY environment set.' %
119 ','.join([b.browser_type for b in browsers]))
120 return []
121
122 return browsers
123
124
125
126 _ExceptionNames = {
127 404: 'Not found',
128 500: 'Internal exception',
129 }
130
131 class _RequestException(Exception):
132 def __init__(self, number):
133 super(_RequestException, self).__init__('_RequestException')
134 self.number = number
135
136 class _RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
137 def __init__(self, request, client_address, server):
138 self._server = server
139 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request, client_address , server)
140
141 def _SendJSON(self, obj, resp_code=200, resp_code_str='OK'):
142 text = json.dumps(obj)
143 try:
144 self.send_response(resp_code, resp_code_str)
145 self.send_header('Cache-Control', 'no-cache')
146 self.send_header('Content-Type', 'application/json')
147 self.send_header('Content-Length', len(text))
148 self.end_headers()
149 self.wfile.write(text)
150 except IOError:
151 return
152
153 def log_message(self, format, *args):
154 pass
155
156 def do_POST(self):
157 self._HandleRequest('POST')
158
159 def do_GET(self):
160 self._HandleRequest('GET')
161
162 def _HandleRequest(self, method):
163 if 'Content-Length' in self.headers:
164 cl = int(self.headers['Content-Length'])
165 text = self.rfile.read(cl).encode('utf8')
166 try:
167 if text != '':
168 content = json.loads(text)
169 else:
170 content = ''
171 except ValueError:
172 raise Exception('Payload was unparseable: [%s]' % text)
173 else:
174 content = None
175
176 if self.path == '/ping':
177 self._SendJSON('pong')
178 return
179
180 try:
181 res = self._server.HandleRequest(method, self.path, content)
182 self._SendJSON(res)
183 except _RequestException, ex:
184 self.send_response(ex.number, _ExceptionNames[ex.number])
185 self.send_header('Content-Length', 0)
186 self.end_headers()
187 except:
188 import traceback
189 traceback.print_exc()
190 self.send_response(500, 'ServerError')
191 self.send_header('Content-Length', 0)
192 self.end_headers()
193
194 class _TimeoutTask(object):
195 def __init__(self, cb, deadline, args):
196 self.cb = cb
197 self.deadline = deadline
198 self.args = args
199
200 def __cmp__(self, that):
201 return cmp(self.deadline, that.deadline)
202
203 class _Daemon(BaseHTTPServer.HTTPServer):
204 def __init__(self, server_address):
205 BaseHTTPServer.HTTPServer.__init__(self, server_address, _RequestHandler)
206 self._port = server_address[1]
207 self._is_running = False
208 self._pending_timeout_heap = []
209
210 @property
211 def port(self):
212 return self._port
213
214 @property
215 def is_running(self):
216 return self._is_running
217
218 def AddDelayedTask(self, cb, delay, *args):
219 deadline = time.time() + delay
220 to = _TimeoutTask(cb, deadline, args)
221 heapq.heappush(self._pending_timeout_heap, to)
222
223 def serve_forever(self):
224 self._is_running = True
225 try:
226 while self._is_running:
227 now = time.time()
228 while True:
229 if len(self._pending_timeout_heap):
230 deadline = self._pending_timeout_heap[0].deadline
231 if now > deadline:
232 item = heapq.heappop(self._pending_timeout_heap)
233 item.cb(*item.args)
234 else:
235 next_deadline = deadline
236 break
237 else:
238 next_deadline = now + 0.2
239 break
240
241 now = time.time()
242 delay = max(0.0,next_deadline - now)
243 delay = min(0.25,delay)
244 r, w, e = select.select([self], [], [], delay)
245 if r:
246 self.handle_request()
247 finally:
248 self._is_running = False
249
250 def HandleRequest(self, method, path, content):
251 if self.handler:
252 return self.handler(method, path, content)
253 raise _RequestException('404')
254
255 def Stop(self):
256 assert self._is_running
257 self._is_running = False
258
259 def Run(self):
260 logging.debug('Starting chromeapp._Daemon on port %d', self._port)
261 self.serve_forever()
262 logging.debug('Shut down chromeapp._Daemon on port %d', self._port)
263
264 class _TimeoutException(Exception):
265 pass
266
267 def _WaitFor(condition,
268 timeout, poll_interval=0.1,
269 pass_time_left_to_func=False):
270 assert isinstance(condition, type(lambda: None)) # is function
271 start_time = time.time()
272 while True:
273 if pass_time_left_to_func:
274 res = condition(max((start_time + timeout) - time.time(), 0.0))
275 else:
276 res = condition()
277 if res:
278 break
279 if time.time() - start_time > timeout:
280 if condition.__name__ == '<lambda>':
281 try:
282 condition_string = inspect.getsource(condition).strip()
283 except IOError:
284 condition_string = condition.__name__
285 else:
286 condition_string = condition.__name__
287 raise _TimeoutException('Timed out while waiting %ds for %s.' %
288 (timeout, condition_string))
289 time.sleep(poll_interval)
290
291 def IsChromeInstalled():
292 """Returns whether chromeapp works on this system."""
293 browsers = _FindAllAvailableBrowsers()
294 return len(browsers) > 0
295
296 def _HexToMPDecimal(hex_chars):
297 """ Convert bytes to an MPDecimal string. Example \x00 -> "aa"
298 This gives us the AppID for a chrome extension.
299 """
300 result = ''
301 base = ord('a')
302 for i in xrange(len(hex_chars)):
303 value = ord(hex_chars[i])
304 dig1 = value / 16
305 dig2 = value % 16
306 result += chr(dig1 + base)
307 result += chr(dig2 + base)
308 return result
309
310 def _GetPublicKeyFromPath(filepath):
311 # Normalize the path for windows to have capital drive letters.
312 # We intentionally don't check if sys.platform == 'win32' and just
313 # check if this looks like drive letter so that we can test this
314 # even on posix systems.
315 if (len(filepath) >= 2 and
316 filepath[0].islower() and
317 filepath[1] == ':'):
318 return filepath[0].upper() + filepath[1:]
319 return filepath
320
321 def _GetPublicKeyUnpacked(filepath):
322 assert os.path.isdir(filepath)
323 f = open(os.path.join(filepath, 'manifest.json'), 'rb')
324 manifest = json.load(f)
325 if 'key' not in manifest:
326 # Use the path as the public key.
327 # See Extension::GenerateIdForPath in extension.cc
328 return _GetPublicKeyFromPath(os.path.abspath(filepath))
329 else:
330 return base64.standard_b64decode(manifest['key'])
331
332 def _GetCRXAppID(filepath):
333 pub_key = _GetPublicKeyUnpacked(filepath)
334 pub_key_hash = hashlib.sha256(pub_key).digest()
335 # AppID is the MPDecimal of only the first 128 bits of the hash.
336 return _HexToMPDecimal(pub_key_hash[:128/8])
337
338 class AppInstance(object):
339 def __init__(self, app, args=None):
340 self._app = app
341 self._proc = None
342 self._devnull = None
343 self._cur_chromeapp_js = None
344 self._event_listeners = {}
345 if args:
346 self._args = args
347 else:
348 self._args = []
349
350 self._exit_code = None
351 self._exiting_run_loop = False
352
353 def __enter__(self):
354 if not self.is_started:
355 self.Start()
356 return self
357
358 def __exit__(self, *args):
359 if self.is_started:
360 self._CloseBrowserProcess()
361
362 @property
363 def is_started(self):
364 return self._proc != None
365
366 def Start(self):
367 tmp = socket.socket()
368 tmp.bind(('', 0))
369 port = tmp.getsockname()[1]
370 tmp.close()
371
372 self._daemon = _Daemon(('localhost', port))
373 self._daemon.handler = self._HandleRequest
374
375 browsers = _FindAllAvailableBrowsers()
376 if len(browsers) == 0:
377 raise ChromeNotFoundException('Could not find Chrome. Cannot start app.')
378 browser = browsers[0]
379
380
381 if self._GetAppID() == None:
382 if not _unittests_running:
383 sys.stderr.write("""
384 Installing Chrome App for %s.
385
386 You will see chrome appear as this happens.
387
388 ***DO NOT CLOSE IT***
389 """ % self._app.stable_app_name)
390 sys.stderr.flush()
391 self._Install(browser)
392 if not _unittests_running:
393 sys.stderr.write("\nApp installed. Thanks for waiting.\n")
394
395 app_id = self._GetAppID()
396
397 # Temporary staging: we now know how to compute crx ids by hand.
398 # Verify that we are getting it right.
399 raw_id = _GetCRXAppID(self._app.manifest_dirname)
400 assert raw_id == app_id
401
402 if self._app.debug_mode:
403 print "chromeapp: app_id is %s" % app_id
404
405 browser_args = [browser.local_executable]
406 browser_args.extend(self._app._GetBrowserStartupArgs())
407 browser_args.append('--app-id=%s' % app_id)
408 logging.info('Launching %s as %s', self._app.stable_app_name, app_id)
409 self._CreateLaunchJS()
410 try:
411 self._Launch(browser_args)
412 except:
413 if self.is_started:
414 self._CloseBrowserProcess()
415
416 def _Install(self, browser):
417 logging.info('Installing %s for first time...', self._app.stable_app_name)
418 browser_args = [browser.local_executable]
419 browser_args.extend(self._app._GetBrowserStartupArgs())
420 browser_args.append(
421 '--load-extension=%s' % self._app.manifest_dirname
422 )
423 try:
424 self._Launch(browser_args)
425 def IsAppInstalled():
426 return self._GetAppID() != None
427 # We may have to a wait for a while, it seems like chrome takes a while
428 # to flush its preferences.
429 _WaitFor(IsAppInstalled, 60, poll_interval=0.5)
430 logging.info('Installed %s', self._app.stable_app_name)
431 finally:
432 if self.is_started:
433 self._CloseBrowserProcess()
434
435 def _GetAppID(self):
436 prefs = self._app._ReadPreferences()
437 if 'extensions' not in prefs:
438 return None
439 if 'settings' not in prefs['extensions']:
440 return None
441 settings = prefs['extensions']['settings']
442 for app_id, app_settings in settings.iteritems():
443 if 'path' not in app_settings:
444 continue
445 if not os.path.exists(app_settings['path']):
446 continue
447 if not _samefile(app_settings['path'],
448 self._app.manifest_dirname):
449 continue
450
451 if 'events' not in app_settings:
452 return None
453
454 return app_id
455
456 return None
457
458 def _CreateLaunchJS(self):
459 js_template = """
460 'use strict';
461
462 // DO NOT COMMIT! This template is automatically created and updated by
463 // py-chrome-ui just before launch, with the necessary
464 // parameters for communicating back to the hosting python code.
465 (function() {
466 var BASE_URL = "__CHROMEAPP_REPLY_URL__"; // Note: chromeapp will set this up during launch.
467 var DEBUG_MODE = __CHROMEAPP_DEBUG_MODE__; // Note: chromeapp will set this u p during launch.lj
468
469 function reqAsync(method, path, data, opt_response_cb, opt_err_cb) {
470 if (path[0] != '/')
471 throw new Error('Must start with /');
472 var req = new XMLHttpRequest();
473 req.open(method, BASE_URL + path, true);
474 req.addEventListener('load', function() {
475 if (req.status == 200) {
476 if (opt_response_cb)
477 opt_response_cb(JSON.parse(req.responseText));
478 return;
479 }
480 if (opt_err_cb)
481 opt_err_cb();
482 else
483 console.log('reqAsync ' + path, req);
484 });
485 req.addEventListener('error', function() {
486 if (opt_err_cb)
487 opt_err_cb();
488 else
489 console.log('reqAsync ' + path, req);
490 });
491 if (data)
492 req.send(JSON.stringify(data));
493 else
494 req.send(null);
495 }
496
497 function Event(type, opt_bubbles, opt_preventable) {
498 var e = document.createEvent('Event');
499 e.initEvent(type, !!opt_bubbles, !!opt_preventable);
500 e.__proto__ = window.Event.prototype;
501 return e;
502 };
503
504 function dispatchSimpleEvent(target, type, opt_bubbles, opt_cancelable) {
505 var e = new Event(type, opt_bubbles, opt_cancelable);
506 return target.dispatchEvent(e);
507 }
508
509 // chromeapp derives from a div in order to get basic dispatchEvent
510 // capabilities. Lame but effective.
511 var chromeapp = document.createElement('div');
512
513 // Ask the server for startup args.
514 // TODO(nduca): crbug.com/168085 causes breakpoints to be ineffective
515 // during the early stages of page load. In debug mode, we delay the launch
516 // event for a bit so that breakpoints work.
517 if (!DEBUG_MODE) {
518 reqAsync('GET', '/launch_args', null, gotLaunchArgs);
519 } else {
520 reqAsync('GET', '/launch_args', null, function(args) {
521 setTimeout(function() {
522 gotLaunchArgs(args);
523 }, 1000);
524 });
525 }
526 function gotLaunchArgs(args) {
527 var e = new Event('launch', false, false);
528 e.args = args;
529 chromeapp.launch_args = args;
530 chromeapp.dispatchEvent(e);
531 }
532
533 var oldOnError = window.onerror;
534 function onUncaughtError(error, url, line_number) {
535 reqAsync('POST', '/uncaught_error', {
536 error: error,
537 url: url,
538 line_number: line_number});
539 if (oldOnError) oldOnError(error, url, line_number);
540 }
541 window.onerror = onUncaughtError;
542
543 function print() {
544 var messages = [];
545 for (var i = 0; i < arguments.length; i++) {
546 try {
547 // See if it even stringifies.
548 JSON.stringify(arguments[i]);
549 messages.push(arguments[i]);
550 } catch(ex) {
551 messages.push('Argument ' + i + ' not convertible to JSON');
552 }
553 }
554 reqAsync('POST', '/print', messages);
555 }
556
557 function sendEvent(event_name, args, opt_callback, opt_err_callback) {
558 if (args === undefined)
559 throw new Error('args is required');
560 reqAsync('POST', '/send_event', {
561 event_name: event_name,
562 args: args},
563 opt_callback,
564 opt_err_callback);
565 }
566
567 var exiting = false;
568 function exit(opt_exitCode) {
569 var exitCode = opt_exitCode;
570 if (opt_exitCode === undefined)
571 exitCode = 0;
572 if (typeof(exitCode) != 'number')
573 throw new Error('exit code must be a number or undefined');
574 if (exiting)
575 throw new Error('chromeapp.exit() was a already called');
576 exiting = true;
577
578 reqAsync('POST', '/exit', {exitCode: exitCode}, function() { });
579
580 // Busy wait for a bit to try to give the xhr a chance to hit
581 // python.
582 var start = Date.now();
583 while(Date.now() < start + 150);
584 }
585
586 window.chromeapp = chromeapp;
587 window.chromeapp.launch_args = undefined;
588 window.chromeapp.sendEvent = sendEvent;
589 window.chromeapp.print = print;
590 window.chromeapp.exit = exit;
591 })();
592 """
593
594 assert self._daemon
595 self._cur_chromeapp_js = os.path.join(
596 self._app.manifest_dirname,
597 'chromeapp.js')
598 js = js_template
599 js = js.replace('__CHROMEAPP_REPLY_URL__',
600 'http://localhost:%i' % self._daemon.port)
601 js = js.replace('__CHROMEAPP_DEBUG_MODE__',
602 '%s' % json.dumps(self._app.debug_mode))
603 with open(self._cur_chromeapp_js, 'w') as f:
604 f.write(js)
605
606 def _CleanupLaunchJS(self):
607 if self._cur_chromeapp_js == None:
608 return
609 assert os.path.exists(self._cur_chromeapp_js)
610 os.unlink(self._cur_chromeapp_js)
611 self._cur_chromeapp_js = None
612
613 def _Launch(self, browser_args):
614 if not self._app.debug_mode:
615 self._devnull = open(os.devnull, 'w')
616 self._proc = subprocess.Popen(
617 browser_args, stdout=self._devnull, stderr=self._devnull)
618 else:
619 self._devnull = None
620 self._proc = subprocess.Popen(browser_args)
621
622 def _StartCheckingForBrowserAliveness(self):
623 self._daemon.AddDelayedTask(self._CheckForBrowserAliveness, 0.25)
624
625 def _CheckForBrowserAliveness(self):
626 if not self._proc:
627 return
628 def IsStopped():
629 return self._proc.poll() != None
630 if IsStopped():
631 if not self._exiting_run_loop:
632 sys.stderr.write("Browser closed without notifying us. Exiting...\n")
633 self.ExitRunLoop(1)
634 return
635 self._StartCheckingForBrowserAliveness()
636
637 def Run(self):
638 assert self._exit_code == None
639 assert self.is_started
640 self._StartCheckingForBrowserAliveness()
641 try:
642 self._daemon.Run()
643 finally:
644 self._exiting_run_loop = False
645 exit_code = self._exit_code
646 self._exit_code = None
647 return exit_code
648
649 def _OnUncaughtError(self, error):
650 m = re.match('chrome-extension:\/\/(.+)\/(.*)', error['url'], re.DOTALL)
651 assert m
652 sys.stderr.write("Uncaught error: %s:%i: %s\n" % (
653 m.group(2), error['line_number'],
654 error['error']))
655
656 def _OnPrint(self, content):
657 print "%s" % ' '.join([str(x) for x in content])
658
659 def AddListener(self, event_name, callback):
660 if event_name in self._event_listeners:
661 raise Exception('Event listener already registered')
662 self._event_listeners[event_name] = callback
663
664 def RemoveListener(self, event_name, callback):
665 if event_name not in self._event_listeners:
666 raise Exception("Not found")
667 del self._event_listeners[event_name]
668
669 def HasListener(self, event_name, callback):
670 return event_name in self._event_listeners
671
672 def _OnSendEvent(self, content):
673 event_name = content["event_name"]
674 args = content["args"]
675 listener = self._event_listeners.get(event_name, None)
676 if not listener:
677 sys.stderr.write('No listener for %s\n' % event_name)
678 raise _RequestException(500)
679
680 try:
681 return listener(args)
682 except:
683 import traceback
684 traceback.print_exc()
685 raise _RequestException(500)
686
687 def _HandleRequest(self, method, path, content):
688 parsed_result = urlparse.urlparse(path)
689 if path == '/launch_args':
690 return self._args
691
692 if path == '/uncaught_error':
693 self._OnUncaughtError(content)
694 return
695
696 if path == '/print':
697 self._OnPrint(content)
698 return
699
700 if path == '/send_event':
701 return self._OnSendEvent(content)
702
703 if path == '/exit':
704 self.ExitRunLoop(content['exitCode'])
705 return True
706
707 raise _RequestException(404)
708
709 def ExitRunLoop(self, exit_code):
710 """Forces the app out of its run loop."""
711 assert self._daemon.is_running
712 if self._exiting_run_loop:
713 logging.warning("Multiple calls to exit. First return value will be chosen .")
714 return
715 self._exiting_run_loop = True
716 self._exit_code = exit_code
717 self._daemon.Stop()
718
719 def _CloseBrowserProcess(self):
720 assert self.is_started
721 assert not self._daemon.is_running
722
723 if self._proc:
724
725 def IsStopped():
726 if not self._proc:
727 return True
728 return self._proc.poll() != None
729
730 # Try to politely shutdown, first.
731 if not IsStopped():
732 try:
733 self._proc.terminate()
734 try:
735 _WaitFor(IsStopped, timeout=5)
736 self._proc = None
737 except _TimeoutException:
738 pass
739 except:
740 pass
741
742 # Kill it.
743 if not IsStopped():
744 self._proc.kill()
745 try:
746 _WaitFor(IsStopped, timeout=5)
747 self._proc = None
748 except _TimeoutException:
749 self._proc = None
750 raise Exception('Could not shutdown the browser.')
751
752 if self._devnull:
753 self._devnull.close()
754 self._devnull = None
755
756 self._CleanupLaunchJS()
757
758 class ManifestError(Exception):
759 pass
760
761 class App(object):
762 def __init__(self, stable_app_name, manifest_filename,
763 debug_mode=False,
764 chromeapp_profiles_dir=False):
765 self._stable_app_name = stable_app_name
766 self._profile_dir = None
767
768 self._manifest_filename = manifest_filename
769 self._debug_mode = debug_mode
770
771 with open(self._manifest_filename, 'r') as f:
772 manifest_text = f.read()
773 self._manifest = json.loads(manifest_text)
774
775 if 'permissions' not in self._manifest:
776 raise ManifestError('You need to have permissions: "http://localhost:*/" i n your manifest.')
777 if 'http://localhost:*/' not in self._manifest['permissions']:
778 raise ManifestError('You need to have permissions: "http://localhost:*/" i n your manifest.')
779
780 if not chromeapp_profiles_dir:
781 chromeapp_profiles_dir = os.path.expanduser('~/.chromeapp')
782 if not os.path.exists(chromeapp_profiles_dir):
783 os.mkdir(chromeapp_profiles_dir)
784
785 self._profile_dir = os.path.join(chromeapp_profiles_dir,
786 stable_app_name)
787
788 def Run(self, args=None):
789 """Launches and runs instance of the application. Returns its exit code.
790
791 This is shorthand for creating an AppInstance against this app and running i t:
792 with AppInstance(app, args) as instance:
793 ret_val = instance.Run()
794 """
795 with AppInstance(self, args) as instance:
796 return instance.Run()
797
798 @property
799 def stable_app_name(self):
800 return self._stable_app_name
801
802 @property
803 def debug_mode(self):
804 return self._debug_mode
805
806 @property
807 def manifest_filename(self):
808 return self._manifest_filename
809
810 @property
811 def manifest_dirname(self):
812 return os.path.abspath(os.path.dirname(self._manifest_filename))
813
814 def _GetBrowserStartupArgs(self):
815 args = []
816 args.append('--user-data-dir=%s' % self._profile_dir)
817 args.append('--no-first-run')
818 args.append('--noerrdialogs')
819 args.append('--enable-experimental-extension-apis')
820 return args
821
822 def _ReadPreferences(self):
823 prefs_file = os.path.join(self._profile_dir,
824 'Default', 'Preferences')
825 try:
826 with open(prefs_file, 'r') as f:
827 contents = f.read()
828 except:
829 contents = """{
830 "extensions": {
831 "settings": {
832 }
833 }
834 }"""
835 return json.loads(contents)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698