From 30956334f2013fc66ee6ab4e70bb6e6e571aadcb Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Fri, 15 Feb 2013 08:32:43 +0100 Subject: [PATCH 001/140] Bug 638219 - Move command construction into Test class method; r=terrence --HG-- rename : toolkit/modules/Timer.jsm => browser/devtools/shared/Browser.jsm rename : toolkit/modules/tests/xpcshell/test_timer.js => browser/devtools/shared/test/browser_browser_basic.js extra : rebase_source : 4a02112413b31457cf855d68a6db6d816172652f --- js/src/jit-test/jit_test.py | 2 +- js/src/tests/lib/jittests.py | 33 ++++++++++++++++----------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/js/src/jit-test/jit_test.py b/js/src/jit-test/jit_test.py index c891eec78eb..5d582cc6268 100755 --- a/js/src/jit-test/jit_test.py +++ b/js/src/jit-test/jit_test.py @@ -189,7 +189,7 @@ def main(argv): sys.exit(1) tc = job_list[0] - cmd = [ 'gdb', '--args' ] + jittests.get_test_cmd(options.js_shell, tc.path, tc.jitflags, lib_dir, shell_args) + cmd = [ 'gdb', '--args' ] + tc.command(options.js_shell, lib_dir, shell_args) subprocess.call(cmd) sys.exit() diff --git a/js/src/tests/lib/jittests.py b/js/src/tests/lib/jittests.py index af7f69fbb98..c274e6f04f4 100755 --- a/js/src/tests/lib/jittests.py +++ b/js/src/tests/lib/jittests.py @@ -123,6 +123,20 @@ class Test: return test + def command(self, js, lib_dir, shell_args): + libdir_var = lib_dir + if not libdir_var.endswith('/'): + libdir_var += '/' + scriptdir_var = os.path.dirname(self.path); + if not scriptdir_var.endswith('/'): + scriptdir_var += '/' + expr = ("const platform=%r; const libdir=%r; const scriptdir=%r" + % (sys.platform, libdir_var, scriptdir_var)) + # We may have specified '-a' or '-d' twice: once via --jitflags, once + # via the "|jit-test|" line. Remove dups because they are toggles. + return ([js] + list(set(self.jitflags)) + shell_args + + ['-e', expr, '-f', os.path.join(lib_dir, 'prolog.js'), '-f', self.path]) + def find_tests(dir, substring = None): ans = [] for dirpath, dirnames, filenames in os.walk(dir): @@ -140,20 +154,6 @@ def find_tests(dir, substring = None): ans.append(test) return ans -def get_test_cmd(js, path, jitflags, lib_dir, shell_args): - libdir_var = lib_dir - if not libdir_var.endswith('/'): - libdir_var += '/' - scriptdir_var = os.path.dirname(path); - if not scriptdir_var.endswith('/'): - scriptdir_var += '/' - expr = ("const platform=%r; const libdir=%r; const scriptdir=%r" - % (sys.platform, libdir_var, scriptdir_var)) - # We may have specified '-a' or '-d' twice: once via --jitflags, once - # via the "|jit-test|" line. Remove dups because they are toggles. - return ([js] + list(set(jitflags)) + shell_args + - [ '-e', expr, '-f', os.path.join(lib_dir, 'prolog.js'), '-f', path ]) - def tmppath(token): fd, path = tempfile.mkstemp(prefix=token) os.close(fd) @@ -240,8 +240,7 @@ def run_cmd_avoid_stdio(cmdline, env, timeout): return read_and_unlink(stdoutPath), read_and_unlink(stderrPath), code def run_test(test, lib_dir, shell_args, options): - cmd = get_test_cmd(options.js_shell, test.path, test.jitflags, lib_dir, shell_args) - + cmd = test.command(options.js_shell, lib_dir, shell_args) if (test.valgrind and any([os.path.exists(os.path.join(d, 'valgrind')) for d in os.environ['PATH'].split(os.pathsep)])): @@ -442,7 +441,7 @@ def print_test_summary(failures, complete, doing, options, lib_dir, shell_args): def show_test(test): if options.show_failed: - print(' ' + subprocess.list2cmdline(get_test_cmd(options.js_shell, test.path, test.jitflags, lib_dir, shell_args))) + print(' ' + subprocess.list2cmdline(test.command(options.js_shell, lib_dir, shell_args))) else: print(' ' + ' '.join(test.jitflags + [test.path])) From c877e5877efac518ad7e8da3fea7436bbb37ee00 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Fri, 15 Feb 2013 08:33:25 +0100 Subject: [PATCH 002/140] Bug 638219 - Extract valgrind setup, prepend in Test.command(); r=terrence --HG-- extra : rebase_source : dc17ae04127892f4d5778e5d546d8fba4436766f --- js/src/tests/lib/jittests.py | 37 ++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/js/src/tests/lib/jittests.py b/js/src/tests/lib/jittests.py index c274e6f04f4..7f923464e94 100755 --- a/js/src/tests/lib/jittests.py +++ b/js/src/tests/lib/jittests.py @@ -45,6 +45,22 @@ def _relpath(path, start=None): os.path.relpath = _relpath class Test: + + VALGRIND_CMD = [] + paths = (d for d in os.environ['PATH'].split(os.pathsep)) + valgrinds = (os.path.join(d, 'valgrind') for d in paths) + if any(os.path.exists(p) for p in valgrinds): + VALGRIND_CMD = [ + 'valgrind', '-q', '--smc-check=all-non-file', + '--error-exitcode=1', '--gen-suppressions=all', + '--show-possibly-lost=no', '--leak-check=full', + ] + if os.uname()[0] == 'Darwin': + VALGRIND_CMD.append('--dsymutil=yes') + + del paths + del valgrinds + def __init__(self, path): self.path = path # path to test file @@ -134,8 +150,11 @@ class Test: % (sys.platform, libdir_var, scriptdir_var)) # We may have specified '-a' or '-d' twice: once via --jitflags, once # via the "|jit-test|" line. Remove dups because they are toggles. - return ([js] + list(set(self.jitflags)) + shell_args + - ['-e', expr, '-f', os.path.join(lib_dir, 'prolog.js'), '-f', self.path]) + cmd = [js] + list(set(self.jitflags)) + shell_args + ['-e', expr] + cmd += ['-f', os.path.join(lib_dir, 'prolog.js'), '-f', self.path] + if self.valgrind: + cmd = self.VALGRIND_CMD + cmd + return cmd def find_tests(dir, substring = None): ans = [] @@ -241,20 +260,6 @@ def run_cmd_avoid_stdio(cmdline, env, timeout): def run_test(test, lib_dir, shell_args, options): cmd = test.command(options.js_shell, lib_dir, shell_args) - if (test.valgrind and - any([os.path.exists(os.path.join(d, 'valgrind')) - for d in os.environ['PATH'].split(os.pathsep)])): - valgrind_prefix = [ 'valgrind', - '-q', - '--smc-check=all-non-file', - '--error-exitcode=1', - '--gen-suppressions=all', - '--show-possibly-lost=no', - '--leak-check=full'] - if os.uname()[0] == 'Darwin': - valgrind_prefix += ['--dsymutil=yes'] - cmd = valgrind_prefix + cmd - if options.show_cmd: print(subprocess.list2cmdline(cmd)) From b364cd92d0ad39ca18fc98e53839419782ac8f3d Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Fri, 15 Feb 2013 09:01:19 +0100 Subject: [PATCH 003/140] Bug 638219 - Save static paths in module-level constants; r=terrence These paths are a little far away from the script they're referenced in, so it's a little fragile. However, since (a) these aren't expected to change that often, and (b) the code should fail conspicuously if there is a change, I don't think it's a problem. --HG-- extra : rebase_source : 2e2d278826d4d30c813156553590c24d9056575f --- js/src/jit-test/jit_test.py | 19 +++++------- js/src/tests/lib/jittests.py | 58 +++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 40 deletions(-) diff --git a/js/src/jit-test/jit_test.py b/js/src/jit-test/jit_test.py index 5d582cc6268..de72df3ad44 100755 --- a/js/src/jit-test/jit_test.py +++ b/js/src/jit-test/jit_test.py @@ -18,11 +18,6 @@ import jittests def main(argv): - script_path = os.path.abspath(__file__) - script_dir = os.path.dirname(script_path) - test_dir = os.path.join(script_dir, 'tests') - lib_dir = os.path.join(script_dir, 'lib') - # If no multiprocessing is available, fallback to serial test execution max_jobs_default = 1 if jittests.HAVE_MULTIPROCESSING: @@ -106,14 +101,14 @@ def main(argv): if test_args: read_all = False for arg in test_args: - test_list += jittests.find_tests(test_dir, arg) + test_list += jittests.find_tests(arg) if options.read_tests: read_all = False try: f = open(options.read_tests) for line in f: - test_list.append(os.path.join(test_dir, line.strip('\n'))) + test_list.append(os.path.join(TEST_DIR, line.strip('\n'))) f.close() except IOError: if options.retest: @@ -125,12 +120,12 @@ def main(argv): sys.stderr.write('---\n') if read_all: - test_list = jittests.find_tests(test_dir) + test_list = jittests.find_tests() if options.exclude: exclude_list = [] for exclude in options.exclude: - exclude_list += jittests.find_tests(test_dir, exclude) + exclude_list += jittests.find_tests(exclude) test_list = [ test for test in test_list if test not in set(exclude_list) ] if not test_list: @@ -189,16 +184,16 @@ def main(argv): sys.exit(1) tc = job_list[0] - cmd = [ 'gdb', '--args' ] + tc.command(options.js_shell, lib_dir, shell_args) + cmd = [ 'gdb', '--args' ] + tc.command(options.js_shell, shell_args) subprocess.call(cmd) sys.exit() try: ok = None if options.max_jobs > 1 and jittests.HAVE_MULTIPROCESSING: - ok = jittests.run_tests_parallel(job_list, test_dir, lib_dir, shell_args, options) + ok = jittests.run_tests_parallel(job_list, shell_args, options) else: - ok = jittests.run_tests(job_list, test_dir, lib_dir, shell_args, options) + ok = jittests.run_tests(job_list, shell_args, options) if not ok: sys.exit(2) except OSError: diff --git a/js/src/tests/lib/jittests.py b/js/src/tests/lib/jittests.py index 7f923464e94..878225feb39 100755 --- a/js/src/tests/lib/jittests.py +++ b/js/src/tests/lib/jittests.py @@ -21,6 +21,11 @@ except ImportError: from progressbar import ProgressBar, NullProgressBar +TESTS_LIB_DIR = os.path.dirname(os.path.abspath(__file__)) +JS_DIR = os.path.dirname(os.path.dirname(TESTS_LIB_DIR)) +TEST_DIR = os.path.join(JS_DIR, 'jit-test', 'tests') +LIB_DIR = os.path.join(JS_DIR, 'jit-test', 'lib') + os.path.sep + # Backported from Python 3.1 posixpath.py def _relpath(path, start=None): """Return a relative version of a path""" @@ -139,26 +144,23 @@ class Test: return test - def command(self, js, lib_dir, shell_args): - libdir_var = lib_dir - if not libdir_var.endswith('/'): - libdir_var += '/' + def command(self, js, shell_args): scriptdir_var = os.path.dirname(self.path); if not scriptdir_var.endswith('/'): scriptdir_var += '/' expr = ("const platform=%r; const libdir=%r; const scriptdir=%r" - % (sys.platform, libdir_var, scriptdir_var)) + % (sys.platform, LIB_DIR, scriptdir_var)) # We may have specified '-a' or '-d' twice: once via --jitflags, once # via the "|jit-test|" line. Remove dups because they are toggles. cmd = [js] + list(set(self.jitflags)) + shell_args + ['-e', expr] - cmd += ['-f', os.path.join(lib_dir, 'prolog.js'), '-f', self.path] + cmd += ['-f', os.path.join(LIB_DIR, 'prolog.js'), '-f', self.path] if self.valgrind: cmd = self.VALGRIND_CMD + cmd return cmd -def find_tests(dir, substring = None): +def find_tests(substring=None): ans = [] - for dirpath, dirnames, filenames in os.walk(dir): + for dirpath, dirnames, filenames in os.walk(TEST_DIR): dirnames.sort() filenames.sort() if dirpath == '.': @@ -169,7 +171,7 @@ def find_tests(dir, substring = None): if filename in ('shell.js', 'browser.js', 'jsref.js'): continue test = os.path.join(dirpath, filename) - if substring is None or substring in os.path.relpath(test, dir): + if substring is None or substring in os.path.relpath(test, TEST_DIR): ans.append(test) return ans @@ -258,8 +260,8 @@ def run_cmd_avoid_stdio(cmdline, env, timeout): _, __, code = run_timeout_cmd(cmdline, { 'env': env }, timeout) return read_and_unlink(stdoutPath), read_and_unlink(stderrPath), code -def run_test(test, lib_dir, shell_args, options): - cmd = test.command(options.js_shell, lib_dir, shell_args) +def run_test(test, shell_args, options): + cmd = test.command(options.js_shell, shell_args) if options.show_cmd: print(subprocess.list2cmdline(cmd)) @@ -313,14 +315,14 @@ def print_tinderbox(label, test, message=None): result += ": " + message print(result) -def wrap_parallel_run_test(test, lib_dir, shell_args, resultQueue, options): +def wrap_parallel_run_test(test, shell_args, resultQueue, options): # Ignore SIGINT in the child signal.signal(signal.SIGINT, signal.SIG_IGN) - result = run_test(test, lib_dir, shell_args, options) + (test,) + result = run_test(test, shell_args, options) + (test,) resultQueue.put(result) return result -def run_tests_parallel(tests, test_dir, lib_dir, shell_args, options): +def run_tests_parallel(tests, shell_args, options): # This queue will contain the results of the various tests run. # We could make this queue a global variable instead of using # a manager to share, but this will not work on Windows. @@ -338,7 +340,7 @@ def run_tests_parallel(tests, test_dir, lib_dir, shell_args, options): result_process_return_queue = queue_manager.Queue() result_process = Process(target=process_test_results_parallel, args=(async_test_result_queue, result_process_return_queue, - notify_queue, len(tests), options, lib_dir, shell_args)) + notify_queue, len(tests), options, shell_args)) result_process.start() # Ensure that a SIGTERM is handled the same way as SIGINT @@ -368,7 +370,7 @@ def run_tests_parallel(tests, test_dir, lib_dir, shell_args, options): while notify_queue.get(): if (testcnt < len(tests)): # Start one new worker - worker_process = Process(target=wrap_parallel_run_test, args=(tests[testcnt], lib_dir, shell_args, async_test_result_queue, options)) + worker_process = Process(target=wrap_parallel_run_test, args=(tests[testcnt], shell_args, async_test_result_queue, options)) worker_processes.append(worker_process) worker_process.start() testcnt += 1 @@ -417,12 +419,12 @@ def get_parallel_results(async_test_result_queue, notify_queue): yield async_test_result -def process_test_results_parallel(async_test_result_queue, return_queue, notify_queue, num_tests, options, lib_dir, shell_args): +def process_test_results_parallel(async_test_result_queue, return_queue, notify_queue, num_tests, options, shell_args): gen = get_parallel_results(async_test_result_queue, notify_queue) - ok = process_test_results(gen, num_tests, options, lib_dir, shell_args) + ok = process_test_results(gen, num_tests, options, shell_args) return_queue.put(ok) -def print_test_summary(failures, complete, doing, options, lib_dir, shell_args): +def print_test_summary(failures, complete, doing, options, shell_args): if failures: if options.write_failures: try: @@ -431,7 +433,7 @@ def print_test_summary(failures, complete, doing, options, lib_dir, shell_args): written = set() for test, fout, ferr, fcode, _ in failures: if test.path not in written: - out.write(os.path.relpath(test.path, test_dir) + '\n') + out.write(os.path.relpath(test.path, TEST_DIR) + '\n') if options.write_failure_output: out.write(fout) out.write(ferr) @@ -446,7 +448,7 @@ def print_test_summary(failures, complete, doing, options, lib_dir, shell_args): def show_test(test): if options.show_failed: - print(' ' + subprocess.list2cmdline(test.command(options.js_shell, lib_dir, shell_args))) + print(' ' + subprocess.list2cmdline(test.command(options.js_shell, shell_args))) else: print(' ' + ' '.join(test.jitflags + [test.path])) @@ -465,7 +467,7 @@ def print_test_summary(failures, complete, doing, options, lib_dir, shell_args): print('PASSED ALL' + ('' if complete else ' (partial run -- interrupted by user %s)' % doing)) return True -def process_test_results(results, num_tests, options, lib_dir, shell_args): +def process_test_results(results, num_tests, options, shell_args): pb = NullProgressBar() if not options.hide_progress and not options.show_cmd and ProgressBar.conservative_isatty(): fmt = [ @@ -514,17 +516,17 @@ def process_test_results(results, num_tests, options, lib_dir, shell_args): print_tinderbox("TEST-UNEXPECTED-FAIL", None, "Test execution interrupted by user"); pb.finish(True) - return print_test_summary(failures, complete, doing, options, lib_dir, shell_args) + return print_test_summary(failures, complete, doing, options, shell_args) -def get_serial_results(tests, lib_dir, shell_args, options): +def get_serial_results(tests, shell_args, options): for test in tests: - result = run_test(test, lib_dir, shell_args, options) + result = run_test(test, shell_args, options) yield result + (test,) -def run_tests(tests, test_dir, lib_dir, shell_args, options): - gen = get_serial_results(tests, lib_dir, shell_args, options) - ok = process_test_results(gen, len(tests), options, lib_dir, shell_args) +def run_tests(tests, shell_args, options): + gen = get_serial_results(tests, shell_args, options) + ok = process_test_results(gen, len(tests), options, shell_args) return ok def parse_jitflags(options): From 4b6bc9c1099c6afd4016eebae1675f9942161744 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Fri, 15 Feb 2013 17:00:58 +0100 Subject: [PATCH 004/140] Bug 638219 - Use TestOutput class to wrap test results; r=terrence --HG-- extra : rebase_source : 446d5f86d11c4d41fc771dde2b6357f6fff63175 --- js/src/tests/lib/jittests.py | 51 ++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/js/src/tests/lib/jittests.py b/js/src/tests/lib/jittests.py index 878225feb39..a6f1911da52 100755 --- a/js/src/tests/lib/jittests.py +++ b/js/src/tests/lib/jittests.py @@ -20,6 +20,7 @@ except ImportError: HAVE_MULTIPROCESSING = False from progressbar import ProgressBar, NullProgressBar +from results import TestOutput TESTS_LIB_DIR = os.path.dirname(os.path.abspath(__file__)) JS_DIR = os.path.dirname(os.path.dirname(TESTS_LIB_DIR)) @@ -282,8 +283,8 @@ def run_test(test, shell_args, options): sys.stdout.write('Exit code: %s\n' % code) if test.valgrind: sys.stdout.write(err) - return (check_output(out, err, code, test), - out, err, code, timed_out) + + return TestOutput(test, cmd, out, err, code, None, timed_out) def check_output(out, err, rc, test): if test.expect_error: @@ -318,7 +319,7 @@ def print_tinderbox(label, test, message=None): def wrap_parallel_run_test(test, shell_args, resultQueue, options): # Ignore SIGINT in the child signal.signal(signal.SIGINT, signal.SIG_IGN) - result = run_test(test, shell_args, options) + (test,) + result = run_test(test, shell_args, options) resultQueue.put(result) return result @@ -431,14 +432,14 @@ def print_test_summary(failures, complete, doing, options, shell_args): out = open(options.write_failures, 'w') # Don't write duplicate entries when we are doing multiple failures per job. written = set() - for test, fout, ferr, fcode, _ in failures: - if test.path not in written: - out.write(os.path.relpath(test.path, TEST_DIR) + '\n') + for res in failures: + if res.test.path not in written: + out.write(os.path.relpath(res.test.path, TEST_DIR) + '\n') if options.write_failure_output: - out.write(fout) - out.write(ferr) - out.write('Exit code: ' + str(fcode) + "\n") - written.add(test.path) + out.write(res.out) + out.write(res.err) + out.write('Exit code: ' + str(res.rc) + "\n") + written.add(res.test.path) out.close() except IOError: sys.stderr.write("Exception thrown trying to write failure file '%s'\n"% @@ -453,14 +454,14 @@ def print_test_summary(failures, complete, doing, options, shell_args): print(' ' + ' '.join(test.jitflags + [test.path])) print('FAILURES:') - for test, _, __, ___, timed_out in failures: - if not timed_out: - show_test(test) + for res in failures: + if not res.timed_out: + show_test(res.test) print('TIMEOUTS:') - for test, _, __, ___, timed_out in failures: - if timed_out: - show_test(test) + for res in failures: + if res.timed_out: + show_test(res.test) return False else: @@ -483,18 +484,19 @@ def process_test_results(results, num_tests, options, shell_args): complete = False doing = 'before starting' try: - for i, (ok, out, err, code, timed_out, test) in enumerate(results): - doing = 'after %s'%test.path + for i, res in enumerate(results): + ok = check_output(res.out, res.err, res.rc, res.test) + doing = 'after %s' % res.test.path if not ok: - failures.append([ test, out, err, code, timed_out ]) - pb.message("FAIL - %s" % test.path) - if timed_out: + failures.append(res) + pb.message("FAIL - %s" % res.test.path) + if res.timed_out: timeouts += 1 if options.tinderbox: if ok: - print_tinderbox("TEST-PASS", test); + print_tinderbox("TEST-PASS", res.test); else: lines = [ _ for _ in out.split('\n') + err.split('\n') if _ != '' ] @@ -502,7 +504,7 @@ def process_test_results(results, num_tests, options, shell_args): msg = lines[-1] else: msg = '' - print_tinderbox("TEST-UNEXPECTED-FAIL", test, msg); + print_tinderbox("TEST-UNEXPECTED-FAIL", res.test, msg); n = i + 1 pb.update(n, { @@ -521,8 +523,7 @@ def process_test_results(results, num_tests, options, shell_args): def get_serial_results(tests, shell_args, options): for test in tests: - result = run_test(test, shell_args, options) - yield result + (test,) + yield run_test(test, shell_args, options) def run_tests(tests, shell_args, options): gen = get_serial_results(tests, shell_args, options) From d60cf242a46f4318db654ab1573e399b2321f284 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Fri, 15 Feb 2013 17:44:24 +0100 Subject: [PATCH 005/140] Bug 638219 - Reuse cmd saved in results object rather than rebuilding it; r=terrence --HG-- extra : rebase_source : 4b955e7793c966db184f67cc1a90ab8c24bbfcd7 --- js/src/tests/lib/jittests.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/js/src/tests/lib/jittests.py b/js/src/tests/lib/jittests.py index a6f1911da52..5c64d296be7 100755 --- a/js/src/tests/lib/jittests.py +++ b/js/src/tests/lib/jittests.py @@ -341,7 +341,7 @@ def run_tests_parallel(tests, shell_args, options): result_process_return_queue = queue_manager.Queue() result_process = Process(target=process_test_results_parallel, args=(async_test_result_queue, result_process_return_queue, - notify_queue, len(tests), options, shell_args)) + notify_queue, len(tests), options)) result_process.start() # Ensure that a SIGTERM is handled the same way as SIGINT @@ -420,12 +420,12 @@ def get_parallel_results(async_test_result_queue, notify_queue): yield async_test_result -def process_test_results_parallel(async_test_result_queue, return_queue, notify_queue, num_tests, options, shell_args): +def process_test_results_parallel(async_test_result_queue, return_queue, notify_queue, num_tests, options): gen = get_parallel_results(async_test_result_queue, notify_queue) - ok = process_test_results(gen, num_tests, options, shell_args) + ok = process_test_results(gen, num_tests, options) return_queue.put(ok) -def print_test_summary(failures, complete, doing, options, shell_args): +def print_test_summary(failures, complete, doing, options): if failures: if options.write_failures: try: @@ -447,28 +447,28 @@ def print_test_summary(failures, complete, doing, options, shell_args): traceback.print_exc() sys.stderr.write('---\n') - def show_test(test): + def show_test(res): if options.show_failed: - print(' ' + subprocess.list2cmdline(test.command(options.js_shell, shell_args))) + print(' ' + subprocess.list2cmdline(res.cmd)) else: - print(' ' + ' '.join(test.jitflags + [test.path])) + print(' ' + ' '.join(res.test.jitflags + [res.test.path])) print('FAILURES:') for res in failures: if not res.timed_out: - show_test(res.test) + show_test(res) print('TIMEOUTS:') for res in failures: if res.timed_out: - show_test(res.test) + show_test(res) return False else: print('PASSED ALL' + ('' if complete else ' (partial run -- interrupted by user %s)' % doing)) return True -def process_test_results(results, num_tests, options, shell_args): +def process_test_results(results, num_tests, options): pb = NullProgressBar() if not options.hide_progress and not options.show_cmd and ProgressBar.conservative_isatty(): fmt = [ @@ -518,7 +518,7 @@ def process_test_results(results, num_tests, options, shell_args): print_tinderbox("TEST-UNEXPECTED-FAIL", None, "Test execution interrupted by user"); pb.finish(True) - return print_test_summary(failures, complete, doing, options, shell_args) + return print_test_summary(failures, complete, doing, options) def get_serial_results(tests, shell_args, options): @@ -527,7 +527,7 @@ def get_serial_results(tests, shell_args, options): def run_tests(tests, shell_args, options): gen = get_serial_results(tests, shell_args, options) - ok = process_test_results(gen, len(tests), options, shell_args) + ok = process_test_results(gen, len(tests), options) return ok def parse_jitflags(options): From ad98786482fb3ea66690ef44f72af260d31b3b1b Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Fri, 15 Feb 2013 17:09:33 +0100 Subject: [PATCH 006/140] Bug 638219 - Move test result output into result processing function; r=terrence --HG-- extra : rebase_source : c72b28460c2e4b52aa3e4bfa903d5ee1563c1e39 --- js/src/tests/lib/jittests.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/js/src/tests/lib/jittests.py b/js/src/tests/lib/jittests.py index 5c64d296be7..74185cee087 100755 --- a/js/src/tests/lib/jittests.py +++ b/js/src/tests/lib/jittests.py @@ -276,14 +276,6 @@ def run_test(test, shell_args, options): env['TZ'] = 'PST8PDT' out, err, code, timed_out = run(cmd, env, options.timeout) - - if options.show_output: - sys.stdout.write(out) - sys.stdout.write(err) - sys.stdout.write('Exit code: %s\n' % code) - if test.valgrind: - sys.stdout.write(err) - return TestOutput(test, cmd, out, err, code, None, timed_out) def check_output(out, err, rc, test): @@ -486,6 +478,13 @@ def process_test_results(results, num_tests, options): try: for i, res in enumerate(results): + if options.show_output: + sys.stdout.write(res.out) + sys.stdout.write(res.err) + sys.stdout.write('Exit code: %s\n' % res.rc) + if res.test.valgrind: + sys.stdout.write(res.err) + ok = check_output(res.out, res.err, res.rc, res.test) doing = 'after %s' % res.test.path if not ok: From 301f3895ff68bc318ad002600a57a48f6de6df1b Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Fri, 15 Feb 2013 17:58:45 +0100 Subject: [PATCH 007/140] Bug 638219 - Set all test-independent command-line bits up once, pass in; r=terrence This will help integration with the jstests framework, which also uses a single prefix argument to its Test command construction method. Note that the order of js arguments is changed, from: cmd = [js] + list(set(self.jitflags)) + shell_args + ['-e', expr] cmd += ['-f', os.path.join(LIB_DIR, 'prolog.js'), '-f', self.path] to: prefix = [os.path.abspath(args[0])] + shlex.split(options.shell_args) prefix += ['-f', os.path.join(jittests.LIB_DIR, 'prolog.js')] cmd = prefix + list(set(self.jitflags)) + ['-e', expr, '-f', self.path] The assumption here is that only the order of -f options matters. --HG-- extra : rebase_source : 9655d6efc62006aa840d72f6b31d764bd870cc7d --- js/src/jit-test/jit_test.py | 16 ++++++++-------- js/src/tests/lib/jittests.py | 26 ++++++++++++-------------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/js/src/jit-test/jit_test.py b/js/src/jit-test/jit_test.py index de72df3ad44..0e226811fd1 100755 --- a/js/src/jit-test/jit_test.py +++ b/js/src/jit-test/jit_test.py @@ -79,7 +79,7 @@ def main(argv): if len(args) < 1: op.error('missing JS_SHELL argument') # We need to make sure we are using backslashes on Windows. - options.js_shell, test_args = os.path.abspath(args[0]), args[1:] + test_args = args[:1] if jittests.stdio_might_be_broken(): # Prefer erring on the side of caution and not using stdio if @@ -174,8 +174,8 @@ def main(argv): new_test.jitflags.extend(jitflags) job_list.append(new_test) - shell_args = shlex.split(options.shell_args) - + prefix = [os.path.abspath(args[0])] + shlex.split(options.shell_args) + prefix += ['-f', os.path.join(jittests.LIB_DIR, 'prolog.js')] if options.debug: if len(job_list) > 1: print 'Multiple tests match command line arguments, debugger can only run one' @@ -184,21 +184,21 @@ def main(argv): sys.exit(1) tc = job_list[0] - cmd = [ 'gdb', '--args' ] + tc.command(options.js_shell, shell_args) + cmd = ['gdb', '--args'] + tc.command(prefix) subprocess.call(cmd) sys.exit() try: ok = None if options.max_jobs > 1 and jittests.HAVE_MULTIPROCESSING: - ok = jittests.run_tests_parallel(job_list, shell_args, options) + ok = jittests.run_tests_parallel(job_list, prefix, options) else: - ok = jittests.run_tests(job_list, shell_args, options) + ok = jittests.run_tests(job_list, prefix, options) if not ok: sys.exit(2) except OSError: - if not os.path.exists(options.js_shell): - print >> sys.stderr, "JS shell argument: file does not exist: '%s'" % options.js_shell + if not os.path.exists(prefix[0]): + print >> sys.stderr, "JS shell argument: file does not exist: '%s'" % prefix[0] sys.exit(1) else: raise diff --git a/js/src/tests/lib/jittests.py b/js/src/tests/lib/jittests.py index 74185cee087..267a29e891c 100755 --- a/js/src/tests/lib/jittests.py +++ b/js/src/tests/lib/jittests.py @@ -145,7 +145,7 @@ class Test: return test - def command(self, js, shell_args): + def command(self, prefix): scriptdir_var = os.path.dirname(self.path); if not scriptdir_var.endswith('/'): scriptdir_var += '/' @@ -153,8 +153,7 @@ class Test: % (sys.platform, LIB_DIR, scriptdir_var)) # We may have specified '-a' or '-d' twice: once via --jitflags, once # via the "|jit-test|" line. Remove dups because they are toggles. - cmd = [js] + list(set(self.jitflags)) + shell_args + ['-e', expr] - cmd += ['-f', os.path.join(LIB_DIR, 'prolog.js'), '-f', self.path] + cmd = prefix + list(set(self.jitflags)) + ['-e', expr, '-f', self.path] if self.valgrind: cmd = self.VALGRIND_CMD + cmd return cmd @@ -261,8 +260,8 @@ def run_cmd_avoid_stdio(cmdline, env, timeout): _, __, code = run_timeout_cmd(cmdline, { 'env': env }, timeout) return read_and_unlink(stdoutPath), read_and_unlink(stderrPath), code -def run_test(test, shell_args, options): - cmd = test.command(options.js_shell, shell_args) +def run_test(test, prefix, options): + cmd = test.command(prefix) if options.show_cmd: print(subprocess.list2cmdline(cmd)) @@ -308,14 +307,14 @@ def print_tinderbox(label, test, message=None): result += ": " + message print(result) -def wrap_parallel_run_test(test, shell_args, resultQueue, options): +def wrap_parallel_run_test(test, prefix, resultQueue, options): # Ignore SIGINT in the child signal.signal(signal.SIGINT, signal.SIG_IGN) - result = run_test(test, shell_args, options) + result = run_test(test, prefix, options) resultQueue.put(result) return result -def run_tests_parallel(tests, shell_args, options): +def run_tests_parallel(tests, prefix, options): # This queue will contain the results of the various tests run. # We could make this queue a global variable instead of using # a manager to share, but this will not work on Windows. @@ -363,7 +362,7 @@ def run_tests_parallel(tests, shell_args, options): while notify_queue.get(): if (testcnt < len(tests)): # Start one new worker - worker_process = Process(target=wrap_parallel_run_test, args=(tests[testcnt], shell_args, async_test_result_queue, options)) + worker_process = Process(target=wrap_parallel_run_test, args=(tests[testcnt], prefix, async_test_result_queue, options)) worker_processes.append(worker_process) worker_process.start() testcnt += 1 @@ -519,13 +518,12 @@ def process_test_results(results, num_tests, options): pb.finish(True) return print_test_summary(failures, complete, doing, options) - -def get_serial_results(tests, shell_args, options): +def get_serial_results(tests, prefix, options): for test in tests: - yield run_test(test, shell_args, options) + yield run_test(test, prefix, options) -def run_tests(tests, shell_args, options): - gen = get_serial_results(tests, shell_args, options) +def run_tests(tests, prefix, options): + gen = get_serial_results(tests, prefix, options) ok = process_test_results(gen, len(tests), options) return ok From 9fbf61c89143220ff9a939547a9f1d3658a94303 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Fri, 1 Mar 2013 16:16:13 -0600 Subject: [PATCH 008/140] Bug 846679 - Export WinUtils so we can use its handy dandy routines in other places besides widget. r=masayuki --- widget/windows/Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/windows/Makefile.in b/widget/windows/Makefile.in index a5770701a33..70ca933ba61 100644 --- a/widget/windows/Makefile.in +++ b/widget/windows/Makefile.in @@ -93,7 +93,7 @@ SHARED_LIBRARY_LIBS = \ ../xpwidgets/$(LIB_PREFIX)xpwidgets_s.$(LIB_SUFFIX) \ $(NULL) -EXPORTS = nsdefs.h WindowHook.h +EXPORTS = nsdefs.h WindowHook.h WinUtils.h EXPORTS_NAMESPACES = mozilla/widget EXPORTS_mozilla/widget = AudioSession.h From 24581defe93eb6848b570fba60b5e15805d90806 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Fri, 1 Mar 2013 16:16:11 -0600 Subject: [PATCH 009/140] Bug 843014 - 'Search for' context menu item should quote selected text. r=fryn --- browser/metro/base/content/ContextCommands.js | 19 ++++++++++++++++--- browser/metro/base/content/browser.xul | 6 ++++-- browser/metro/base/content/helperui/MenuUI.js | 4 ++++ .../locales/en-US/chrome/browser.properties | 4 ++-- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/browser/metro/base/content/ContextCommands.js b/browser/metro/base/content/ContextCommands.js index a18fcd81c36..e18de84c4c5 100644 --- a/browser/metro/base/content/ContextCommands.js +++ b/browser/metro/base/content/ContextCommands.js @@ -10,6 +10,15 @@ var ContextCommands = { _picker: null, + get _ellipsis() { + delete this._ellipsis; + this._ellipsis = "\u2026"; + try { + this._ellipsis = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data; + } catch (ex) { } + return this._ellipsis; + }, + get clipboard() { return Cc["@mozilla.org/widget/clipboardhelper;1"] .getService(Ci.nsIClipboardHelper); @@ -98,6 +107,7 @@ var ContextCommands = { searchTextSetup: function cc_searchTextSetup(aRichListItem, aSearchString) { let defaultURI; let defaultName; + aSearchString = aSearchString.trim(); try { let defaultPB = Services.prefs.getDefaultBranch(null); const nsIPLS = Ci.nsIPrefLocalizedString; @@ -108,11 +118,15 @@ var ContextCommands = { Cu.reportError(ex); return false; } + let displayString = aSearchString; + if (displayString.length > 15) { + displayString = displayString.substring(0, 15) + this._ellipsis; + } // label child node let label = Services.strings .createBundle("chrome://browser/locale/browser.properties") - .formatStringFromName("browser.search.contextTextSearchLabel", - [defaultName], 1); + .formatStringFromName("browser.search.contextTextSearchLabel2", + [defaultName, displayString], 2); aRichListItem.childNodes[0].setAttribute("value", label); aRichListItem.setAttribute("searchString", defaultURI); return true; @@ -355,7 +369,6 @@ var ContextCommands = { var newDir = file.parent.QueryInterface(Ci.nsILocalFile); Services.prefs.setComplexValue("browser.download.lastDir", Ci.nsILocalFile, newDir); }, - }; function AutoChosen(aFileAutoChosen, aUriAutoChosen) { diff --git a/browser/metro/base/content/browser.xul b/browser/metro/base/content/browser.xul index 593f6f58e64..e8db2dbbd92 100644 --- a/browser/metro/base/content/browser.xul +++ b/browser/metro/base/content/browser.xul @@ -572,6 +572,8 @@ + @@ -585,8 +587,8 @@ - - + + diff --git a/browser/metro/base/content/helperui/MenuUI.js b/browser/metro/base/content/helperui/MenuUI.js index 3e439edfabd..e6e4de19064 100644 --- a/browser/metro/base/content/helperui/MenuUI.js +++ b/browser/metro/base/content/helperui/MenuUI.js @@ -161,6 +161,10 @@ var ContextMenuUI = { continue; for (let i = 0; i < types.length; i++) { + // If one of the item's types has '!' before it, treat it as an exclusion rule. + if (types[i].charAt(0) == '!' && contentTypes.indexOf(types[i].substring(1)) != -1) { + break; + } if (contentTypes.indexOf(types[i]) != -1) { // If this is the special search text item, we need to set its label dynamically. if (searchTextItem && !ContextCommands.searchTextSetup(command, this._popupState.string)) { diff --git a/browser/metro/locales/en-US/chrome/browser.properties b/browser/metro/locales/en-US/chrome/browser.properties index e971a0468ca..e856861de74 100644 --- a/browser/metro/locales/en-US/chrome/browser.properties +++ b/browser/metro/locales/en-US/chrome/browser.properties @@ -10,8 +10,8 @@ browser.search.order.1=Bing browser.search.order.2=Google browser.search.order.3=Yahoo -# l10n: search context menu item text will be: |Search (browser.search.defaultenginename) for ".." -browser.search.contextTextSearchLabel=Search %S for ".." +# l10n: search context menu item text will be: |Search (browser.search.defaultenginename) for "(string).." +browser.search.contextTextSearchLabel2=Search %S for "%S" # Settings Charms aboutCharm1=About From ede5196c398a5121c6d59e5f86e7940e0f5251ca Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Fri, 1 Mar 2013 15:50:53 -0800 Subject: [PATCH 010/140] Bug 840588 - add topsrcdir and mozconfig to mozinfo.json [r=ted] --HG-- extra : rebase_source : 281c7272576af2f23be7becd7845a5f78da3eb17 --- config/writemozinfo.py | 7 +++++++ configure.in | 1 + 2 files changed, 8 insertions(+) diff --git a/config/writemozinfo.py b/config/writemozinfo.py index e207a48c32b..f4feb979bd9 100755 --- a/config/writemozinfo.py +++ b/config/writemozinfo.py @@ -28,6 +28,13 @@ def build_dict(env=os.environ): if missing: raise Exception("Missing required environment variables: %s" % ', '.join(missing)) + + if 'MOZCONFIG' in env: + d["mozconfig"] = env["MOZCONFIG"] + + if 'TOPSRCDIR' in env: + d["topsrcdir"] = env["TOPSRCDIR"] + # os o = env["OS_TARGET"] known_os = {"Linux": "linux", diff --git a/configure.in b/configure.in index 5d412f11325..5a7e9b806d2 100644 --- a/configure.in +++ b/configure.in @@ -9118,6 +9118,7 @@ MOZ_WIDGET_TOOLKIT=${MOZ_WIDGET_TOOLKIT} \ UNIVERSAL_BINARY=${UNIVERSAL_BINARY} \ MOZ_CRASHREPORTER=${MOZ_CRASHREPORTER} \ MOZ_APP_NAME=${MOZ_APP_NAME} \ +TOPSRCDIR=${_topsrcdir} \ $PYTHON ${_topsrcdir}/config/writemozinfo.py ./mozinfo.json.tmp if cmp -s ./mozinfo.json.tmp ./mozinfo.json; then rm ./mozinfo.json.tmp From 247d600febff62be12d77c2a138aec433e66fed6 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Fri, 1 Mar 2013 15:51:11 -0800 Subject: [PATCH 011/140] Bug 840588 - Create a mach wrapper that searches up from $CWD for a topsrcdir [r=gps] --HG-- rename : mach => build/mach_bootstrap.py extra : rebase_source : df914d42e46aac1326e50735a5d8e66cb4243a25 --- build/mach_bootstrap.py | 60 ++++++++++++++++++++++++++++ mach | 87 +++++++++++++++++------------------------ 2 files changed, 96 insertions(+), 51 deletions(-) create mode 100755 build/mach_bootstrap.py diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py new file mode 100755 index 00000000000..5cd52e24ea4 --- /dev/null +++ b/build/mach_bootstrap.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from __future__ import print_function, unicode_literals + +import os +import platform +import sys + +# TODO Bug 794506 Integrate with the in-tree virtualenv configuration. +SEARCH_PATHS = [ + 'python/mach', + 'python/mozboot', + 'python/mozbuild', + 'build/pymake', + 'python/blessings', + 'python/psutil', + 'python/which', + 'other-licenses/ply', + 'xpcom/idl-parser', + 'testing', + 'testing/xpcshell', + 'testing/mozbase/mozprocess', + 'testing/mozbase/mozfile', + 'testing/mozbase/mozinfo', +] + +# Individual files providing mach commands. +MACH_MODULES = [ + 'addon-sdk/mach_commands.py', + 'layout/tools/reftest/mach_commands.py', + 'python/mozboot/mozboot/mach_commands.py', + 'python/mozbuild/mozbuild/config.py', + 'python/mozbuild/mozbuild/mach_commands.py', + 'python/mozbuild/mozbuild/frontend/mach_commands.py', + 'testing/mochitest/mach_commands.py', + 'testing/xpcshell/mach_commands.py', +] + +def bootstrap(topsrcdir): + # Ensure we are running Python 2.7+. We put this check here so we generate a + # user-friendly error message rather than a cryptic stack trace on module + # import. + if sys.version_info[0] != 2 or sys.version_info[1] < 7: + print('Python 2.7 or above (but not Python 3) is required to run mach.') + print('You are running Python', platform.python_version()) + sys.exit(1) + + try: + import mach.main + except ImportError: + sys.path[0:0] = [os.path.join(topsrcdir, path) for path in SEARCH_PATHS] + import mach.main + + mach = mach.main.Mach(topsrcdir) + for path in MACH_MODULES: + mach.load_commands_from_file(os.path.join(topsrcdir, path)) + return mach diff --git a/mach b/mach index a4242f09745..9aba2825387 100755 --- a/mach +++ b/mach @@ -6,61 +6,46 @@ from __future__ import print_function, unicode_literals import os -import platform import sys -# Ensure we are running Python 2.7+. We put this check here so we generate a -# user-friendly error message rather than a cryptic stack trace on module -# import. -if sys.version_info[0] != 2 or sys.version_info[1] < 7: - print('Python 2.7 or above (but not Python 3) is required to run mach.') - print('You are running Python', platform.python_version()) - sys.exit(1) +def ancestors(path): + while path: + yield path + (path, child) = os.path.split(path) + if child == "": + break -# TODO Bug 794506 Integrate with the in-tree virtualenv configuration. -SEARCH_PATHS = [ - 'python/mach', - 'python/mozboot', - 'python/mozbuild', - 'build', - 'build/pymake', - 'python/blessings', - 'python/psutil', - 'python/which', - 'other-licenses/ply', - 'xpcom/idl-parser', - 'testing', - 'testing/xpcshell', - 'testing/mozbase/mozprocess', - 'testing/mozbase/mozfile', - 'testing/mozbase/mozinfo', -] +def load_mach(topsrcdir): + sys.path[0:0] = [os.path.join(topsrcdir, "build")] + import mach_bootstrap + return mach_bootstrap.bootstrap(topsrcdir) -# Individual files providing mach commands. -MACH_MODULES = [ - 'addon-sdk/mach_commands.py', - 'layout/tools/reftest/mach_commands.py', - 'python/mozboot/mozboot/mach_commands.py', - 'python/mozbuild/mozbuild/config.py', - 'python/mozbuild/mozbuild/mach_commands.py', - 'python/mozbuild/mozbuild/frontend/mach_commands.py', - 'testing/mochitest/mach_commands.py', - 'testing/xpcshell/mach_commands.py', -] +# Check whether the current directory is within a mach src or obj dir. +for dir_path in ancestors(os.getcwd()): + # If we find a "mozinfo.json" file, we are in the objdir. + mozinfo_path = os.path.join(dir_path, "mozinfo.json") + if os.path.isfile(mozinfo_path): + import json + info = json.load(open(mozinfo_path)) + if "mozconfig" in info and "MOZCONFIG" not in os.environ: + # If the MOZCONFIG environment variable is not already set, set it + # to the value from mozinfo.json. This will tell the build system + # to look for a config file at the path in $MOZCONFIG rather than + # its default locations. + # + # Note: subprocess requires native strings in os.environ Python + # 2.7.2 and earlier on Windows. + os.environ[b"MOZCONFIG"] = str(info["mozconfig"]) -our_dir = os.path.dirname(os.path.abspath(__file__)) + if "topsrcdir" in info: + # Continue searching for mach_bootstrap in the source directory. + dir_path = info["topsrcdir"] -try: - import mach.main -except ImportError: - sys.path[0:0] = [os.path.join(our_dir, path) for path in SEARCH_PATHS] + # If we find the mach bootstrap module, we are in the srcdir. + mach_path = os.path.join(dir_path, "build/mach_bootstrap.py") + if os.path.isfile(mach_path): + mach = load_mach(dir_path) + sys.exit(mach.run(sys.argv[1:])) - import mach.main - -# All of the code is in a module because EVERYTHING IS A LIBRARY. -mach = mach.main.Mach(our_dir) - -for path in MACH_MODULES: - mach.load_commands_from_file(os.path.join(our_dir, path)) - -sys.exit(mach.run(sys.argv[1:])) +print("Could not run mach: No mach source directory found") +sys.exit(1) From f76b987b207645803a4e3af8819fe71b2b2ec78d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Fri, 1 Mar 2013 11:42:49 -0800 Subject: [PATCH 012/140] Backout e0be1d5500f2 (bug 833795) for causing bug 846816 --- dom/ipc/TabParent.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 1804d98f68e..e5a81a75ba1 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -619,18 +619,6 @@ bool TabParent::SendRealTouchEvent(nsTouchEvent& event) } MaybeForwardEventToRenderFrame(event, &e); - - // Adjust the widget coordinates to be relative to our frame. - nsRefPtr frameLoader = GetFrameLoader(); - - if (!frameLoader) { - // No frame anymore? - sEventCapturer = nullptr; - return false; - } - - nsEventStateManager::MapEventCoordinatesForChildProcess(frameLoader, &event); - return (e.message == NS_TOUCH_MOVE) ? PBrowserParent::SendRealTouchMoveEvent(e) : PBrowserParent::SendRealTouchEvent(e); @@ -667,6 +655,17 @@ TabParent::TryCapture(const nsGUIEvent& aEvent) return false; } + // Adjust the widget coordinates to be relative to our frame. + nsRefPtr frameLoader = GetFrameLoader(); + + if (!frameLoader) { + // No frame anymore? + sEventCapturer = nullptr; + return false; + } + + nsEventStateManager::MapEventCoordinatesForChildProcess(frameLoader, &event); + SendRealTouchEvent(event); return true; } From db65a38a152cacd067894b255f7bd258bdcdaebd Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Fri, 1 Mar 2013 15:57:36 -0800 Subject: [PATCH 013/140] Bug 828400 - Set an explicit minHeight on context menu items. r=sriram --- mobile/android/base/PromptService.java | 3 +++ mobile/android/base/resources/values/dimens.xml | 1 + 2 files changed, 4 insertions(+) diff --git a/mobile/android/base/PromptService.java b/mobile/android/base/PromptService.java index bdc64d8a9c4..5916381739c 100644 --- a/mobile/android/base/PromptService.java +++ b/mobile/android/base/PromptService.java @@ -67,6 +67,7 @@ public class PromptService implements OnClickListener, OnCancelListener, OnItemC private final int mIconTextPadding; private final int mIconSize; private final int mInputPaddingSize; + private final int mMinRowSize; PromptService() { sInflater = LayoutInflater.from(GeckoApp.mAppContext); @@ -78,6 +79,7 @@ public class PromptService implements OnClickListener, OnCancelListener, OnItemC mIconTextPadding = (int) (res.getDimension(R.dimen.prompt_service_icon_text_padding)); mIconSize = (int) (res.getDimension(R.dimen.prompt_service_icon_size)); mInputPaddingSize = (int) (res.getDimension(R.dimen.prompt_service_inputs_padding)); + mMinRowSize = (int) (res.getDimension(R.dimen.prompt_service_min_list_item_height)); GeckoAppShell.getEventDispatcher().registerEventListener("Prompt:Show", this); } @@ -692,6 +694,7 @@ public class PromptService implements OnClickListener, OnCancelListener, OnItemC } convertView = sInflater.inflate(resourceId, null); + convertView.setMinimumHeight(mMinRowSize); TextView tv = (TextView) convertView.findViewById(android.R.id.text1); viewHolder = new ViewHolder(tv, tv.getPaddingLeft(), tv.getPaddingRight(), diff --git a/mobile/android/base/resources/values/dimens.xml b/mobile/android/base/resources/values/dimens.xml index 3af15f3552d..d0bc7b88d4a 100644 --- a/mobile/android/base/resources/values/dimens.xml +++ b/mobile/android/base/resources/values/dimens.xml @@ -44,6 +44,7 @@ 16dp 10dp 8dp + 48dp 64dp 26dp 400dp From b96ab263878e47415886e45bfe718059a189ee90 Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Fri, 1 Mar 2013 15:57:38 -0800 Subject: [PATCH 014/140] Bug 844834 - Use system colors for dialog titles. r=sriram --- mobile/android/base/Makefile.in | 6 +++++- .../base/resources/layout/site_setting_title.xml | 11 ++++------- mobile/android/base/resources/values-v14/styles.xml | 13 +++++++++++++ mobile/android/base/resources/values/styles.xml | 6 ++++++ 4 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 mobile/android/base/resources/values-v14/styles.xml diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 33322a4ebee..771a5835b74 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -505,6 +505,10 @@ RES_VALUES_LAND_V14 = \ res/values-land-v14/dimens.xml \ $(NULL) +RES_VALUES_V14 = \ + res/values-v14/styles.xml \ + $(NULL) + RES_XML = \ res/xml/gecko_appwidget_info.xml \ $(SYNC_RES_XML) \ @@ -1003,7 +1007,7 @@ MOZ_BRANDING_DRAWABLE_MDPI = $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRE MOZ_BRANDING_DRAWABLE_HDPI = $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources-hdpi.mn; then cat $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources-hdpi.mn | tr '\n' ' '; fi) MOZ_BRANDING_DRAWABLE_XHDPI = $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources-xhdpi.mn; then cat $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources-xhdpi.mn | tr '\n' ' '; fi) -RESOURCES=$(RES_LAYOUT) $(RES_LAYOUT_LARGE_LAND_V11) $(RES_LAYOUT_LARGE_V11) $(RES_LAYOUT_XLARGE_V11) $(RES_VALUES) $(RES_VALUES_LAND) $(RES_VALUES_V11) $(RES_VALUES_LARGE_V11) $(RES_VALUES_LARGE_LAND_V11) $(RES_VALUES_XLARGE_V11) $(RES_VALUES_LAND_V14) $(RES_XML) $(RES_ANIM) $(RES_DRAWABLE_MDPI) $(RES_DRAWABLE_LDPI) $(RES_DRAWABLE_HDPI) $(RES_DRAWABLE_XHDPI) $(RES_DRAWABLE_MDPI_V11) $(RES_DRAWABLE_HDPI_V11) $(RES_DRAWABLE_XHDPI_V11) $(RES_DRAWABLE_LAND_MDPI_V14) $(RES_DRAWABLE_LAND_HDPI_V14) $(RES_DRAWABLE_LAND_XHDPI_V14) $(RES_DRAWABLE_LARGE_LAND_V11) $(RES_DRAWABLE_LARGE_MDPI_V11) $(RES_DRAWABLE_LARGE_HDPI_V11) $(RES_DRAWABLE_LARGE_XHDPI_V11) $(RES_DRAWABLE_XLARGE_MDPI_V11) $(RES_DRAWABLE_XLARGE_HDPI_V11) $(RES_DRAWABLE_XLARGE_XHDPI_V11) $(RES_COLOR) $(RES_MENU) +RESOURCES=$(RES_LAYOUT) $(RES_LAYOUT_LARGE_LAND_V11) $(RES_LAYOUT_LARGE_V11) $(RES_LAYOUT_XLARGE_V11) $(RES_VALUES) $(RES_VALUES_LAND) $(RES_VALUES_V11) $(RES_VALUES_LARGE_V11) $(RES_VALUES_LARGE_LAND_V11) $(RES_VALUES_XLARGE_V11) $(RES_VALUES_LAND_V14) $(RES_VALUES_V14) $(RES_XML) $(RES_ANIM) $(RES_DRAWABLE_MDPI) $(RES_DRAWABLE_LDPI) $(RES_DRAWABLE_HDPI) $(RES_DRAWABLE_XHDPI) $(RES_DRAWABLE_MDPI_V11) $(RES_DRAWABLE_HDPI_V11) $(RES_DRAWABLE_XHDPI_V11) $(RES_DRAWABLE_LAND_MDPI_V14) $(RES_DRAWABLE_LAND_HDPI_V14) $(RES_DRAWABLE_LAND_XHDPI_V14) $(RES_DRAWABLE_LARGE_LAND_V11) $(RES_DRAWABLE_LARGE_MDPI_V11) $(RES_DRAWABLE_LARGE_HDPI_V11) $(RES_DRAWABLE_LARGE_XHDPI_V11) $(RES_DRAWABLE_XLARGE_MDPI_V11) $(RES_DRAWABLE_XLARGE_HDPI_V11) $(RES_DRAWABLE_XLARGE_XHDPI_V11) $(RES_COLOR) $(RES_MENU) RES_DIRS= \ res/layout \ diff --git a/mobile/android/base/resources/layout/site_setting_title.xml b/mobile/android/base/resources/layout/site_setting_title.xml index 41dec76fdb5..4fdfd6d61af 100644 --- a/mobile/android/base/resources/layout/site_setting_title.xml +++ b/mobile/android/base/resources/layout/site_setting_title.xml @@ -10,8 +10,7 @@ android:gravity="center_vertical"> + android:textSize="14sp"/> diff --git a/mobile/android/base/resources/values-v14/styles.xml b/mobile/android/base/resources/values-v14/styles.xml new file mode 100644 index 00000000000..39e18421752 --- /dev/null +++ b/mobile/android/base/resources/values-v14/styles.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/mobile/android/base/resources/values/styles.xml b/mobile/android/base/resources/values/styles.xml index ffd7e6ac30c..14a83d06edf 100644 --- a/mobile/android/base/resources/values/styles.xml +++ b/mobile/android/base/resources/values/styles.xml @@ -284,4 +284,10 @@ @color/abouthome_section_subtitle + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+

+
+
+
+
+
+
+
+
+
+ + diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 953530f23c6..13bc643ac7a 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -425,6 +425,7 @@ load 683712.html load text-overflow-bug713610.html load 700031.xhtml load 718516.html +load 724978.xhtml load first-letter-638937.html load first-letter-638937-2.html load 734777.html diff --git a/layout/generic/nsColumnSetFrame.cpp b/layout/generic/nsColumnSetFrame.cpp index 3993cd22710..f4c4c09a470 100644 --- a/layout/generic/nsColumnSetFrame.cpp +++ b/layout/generic/nsColumnSetFrame.cpp @@ -64,6 +64,12 @@ public: return nsContainerFrame::StealFrame(aPresContext, aChild, true); } + virtual bool IsFrameOfType(uint32_t aFlags) const + { + return nsContainerFrame::IsFrameOfType(aFlags & + ~(nsIFrame::eCanContainOverflowContainers)); + } + virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) MOZ_OVERRIDE; @@ -132,7 +138,8 @@ protected: * style. This function will also be responsible for implementing * the state machine that controls column balancing. */ - ReflowConfig ChooseColumnStrategy(const nsHTMLReflowState& aReflowState); + ReflowConfig ChooseColumnStrategy(const nsHTMLReflowState& aReflowState, + bool aForceAuto); /** * Reflow column children. Returns true iff the content that was reflowed @@ -259,10 +266,14 @@ NS_IMETHODIMP nsColumnSetFrame::SetInitialChildList(ChildListID aListID, nsFrameList& aChildList) { + if (aListID == kAbsoluteList) { + return nsContainerFrame::SetInitialChildList(aListID, aChildList); + } + NS_ASSERTION(aListID == kPrincipalList, "Only default child list supported"); NS_ASSERTION(aChildList.OnlyChild(), - "initial child list must have exactly one child"); + "initial child list must have exaisRevertingctly one child"); // Queue up the frames for the content frame return nsContainerFrame::SetInitialChildList(kPrincipalList, aChildList); } @@ -308,7 +319,8 @@ GetColumnGap(nsColumnSetFrame* aFrame, } nsColumnSetFrame::ReflowConfig -nsColumnSetFrame::ChooseColumnStrategy(const nsHTMLReflowState& aReflowState) +nsColumnSetFrame::ChooseColumnStrategy(const nsHTMLReflowState& aReflowState, + bool aForceAuto = false) { const nsStyleColumn* colStyle = StyleColumn(); nscoord availContentWidth = GetAvailableContentWidth(aReflowState); @@ -326,7 +338,8 @@ nsColumnSetFrame::ChooseColumnStrategy(const nsHTMLReflowState& aReflowState) int32_t numColumns = colStyle->mColumnCount; // If column-fill is set to 'balance', then we want to balance the columns. - const bool isBalancing = colStyle->mColumnFill == NS_STYLE_COLUMN_FILL_BALANCE; + const bool isBalancing = colStyle->mColumnFill == NS_STYLE_COLUMN_FILL_BALANCE + && !aForceAuto; if (isBalancing) { const uint32_t MAX_NESTED_COLUMN_BALANCING = 2; uint32_t cnt = 0; @@ -404,6 +417,16 @@ nsColumnSetFrame::ChooseColumnStrategy(const nsHTMLReflowState& aReflowState) // This is the case when the column-fill property is set to 'auto'. // No balancing, so don't limit the column count numColumns = INT32_MAX; + + // XXX_jwir3: If a page's height is set to 0, we could continually + // create continuations, resulting in an infinite loop, since + // no progress is ever made. This is an issue with the spec + // (css3-multicol, css3-page, and css3-break) that is + // unresolved as of 27 Feb 2013. For the time being, we set this + // to have a minimum of 1 css px. Once a resolution is made + // on what minimum to have for a page height, we may need to + // change this value to match the appropriate spec(s). + colHeight = std::max(colHeight, nsPresContext::CSSPixelsToAppUnits(1)); } #ifdef DEBUG_roc @@ -611,7 +634,7 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize, if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) { availSize.height = GetAvailableContentHeight(aReflowState); } - + if (reflowNext) child->AddStateBits(NS_FRAME_IS_DIRTY); @@ -919,9 +942,8 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext, // children, we were balancing and we overflowed in the block direction. do { if (colData.mShouldRevertToAuto) { - config = ChooseColumnStrategy(aReflowState); + config = ChooseColumnStrategy(aReflowState, true); isBalancing = false; - config.mBalanceColCount = INT32_MAX; } bool feasible = ReflowChildren(aDesiredSize, aReflowState, @@ -1067,10 +1089,7 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext, aStatus = NS_FRAME_COMPLETE; } - // XXXjwir3: This call should be replaced with FinishWithAbsoluteFrames - // when bug 724978 is fixed and nsColumnSetFrame is a full absolute - // container. - FinishAndStoreOverflow(&aDesiredSize); + FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus, false); aDesiredSize.mCarriedOutBottomMargin = carriedOutBottomMargin; @@ -1103,8 +1122,12 @@ NS_IMETHODIMP nsColumnSetFrame::AppendFrames(ChildListID aListID, nsFrameList& aFrameList) { - NS_NOTREACHED("AppendFrames not supported"); - return NS_ERROR_NOT_IMPLEMENTED; + if (aListID == kAbsoluteList) { + return nsContainerFrame::AppendFrames(aListID, aFrameList); + } + + NS_ERROR("unexpected child list"); + return NS_ERROR_INVALID_ARG; } NS_IMETHODIMP @@ -1112,14 +1135,22 @@ nsColumnSetFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, nsFrameList& aFrameList) { - NS_NOTREACHED("InsertFrames not supported"); - return NS_ERROR_NOT_IMPLEMENTED; + if (aListID == kAbsoluteList) { + return nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList); + } + + NS_ERROR("unexpected child list"); + return NS_ERROR_INVALID_ARG; } NS_IMETHODIMP nsColumnSetFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) { - NS_NOTREACHED("RemoveFrame not supported"); - return NS_ERROR_NOT_IMPLEMENTED; + if (aListID == kAbsoluteList) { + return nsContainerFrame::RemoveFrame(aListID, aOldFrame); + } + + NS_ERROR("unexpected child list"); + return NS_ERROR_INVALID_ARG; } diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 5ee2a390cf3..58c5f07755b 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -4196,9 +4196,10 @@ void nsFrame::FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus) + nsReflowStatus& aStatus, + bool aConstrainHeight) { - ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); + ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus, aConstrainHeight); FinishAndStoreOverflow(&aDesiredSize); } @@ -4207,7 +4208,8 @@ void nsFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus) + nsReflowStatus& aStatus, + bool aConstrainHeight) { if (HasAbsolutelyPositionedChildren()) { nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock(); @@ -4228,7 +4230,7 @@ nsFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext, absoluteContainer->Reflow(container, aPresContext, aReflowState, aStatus, containingBlockWidth, containingBlockHeight, - true, true, true, // XXX could be optimized + aConstrainHeight, true, true, // XXX could be optimized &aDesiredSize.mOverflowAreas); } } diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h index c00f81239ca..434536d9c59 100644 --- a/layout/generic/nsFrame.h +++ b/layout/generic/nsFrame.h @@ -336,11 +336,13 @@ public: void ReflowAbsoluteFrames(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus); + nsReflowStatus& aStatus, + bool aConstrainHeight = true); void FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus); + nsReflowStatus& aStatus, + bool aConstrainHeight = true); virtual bool CanContinueTextRun() const; virtual bool UpdateOverflow(); diff --git a/layout/reftests/abs-pos/reftest.list b/layout/reftests/abs-pos/reftest.list index 7e896ef4c6b..f37b1bc13f7 100644 --- a/layout/reftests/abs-pos/reftest.list +++ b/layout/reftests/abs-pos/reftest.list @@ -13,8 +13,6 @@ fuzzy-if(gtk2Widget,1,8) == select-1.html select-1-ref.html fuzzy-if(gtk2Widget,1,8) == select-1-dynamic.html select-1-ref.html == select-2.html select-2-ref.html fuzzy-if(gtk2Widget,1,19) fuzzy-if(Android||B2G,17,726) == select-3.html select-3-ref.html - -# Fails due to bug 724978. Should be re-enabled once this is fixed. -fails == multi-column-1.html multi-column-1-ref.html +== multi-column-1.html multi-column-1-ref.html == button-1.html button-1-ref.html == button-2.html button-2-ref.html diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 879a9636814..0e93fc202fc 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -683,13 +683,11 @@ skip-if(B2G) == 378937-1.html 378937-1-ref.html == 379316-1.html 379316-1-ref.html skip-if(B2G) fails-if(Android) random-if(cocoaWidget) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fuzzy-if(gtk2Widget,1,191) == 379316-2.html 379316-2-ref.html # bug 379786 == 379328-1.html 379328-1-ref.html -# The next set of reftests all fail until bug 724978 has been fixed, and the -# overflow container ability of nsColumnSetFrame is restored. -fails == 379349-1a.xhtml 379349-1-ref.xhtml -fails == 379349-1b.xhtml 379349-1-ref.xhtml -fails == 379349-1c.xhtml 379349-1-ref.xhtml -fails == 379349-2a.xhtml 379349-2-ref.xhtml -fails == 379349-2b.xhtml 379349-2-ref.xhtml +== 379349-1a.xhtml 379349-1-ref.xhtml +== 379349-1b.xhtml 379349-1-ref.xhtml +== 379349-1c.xhtml 379349-1-ref.xhtml +== 379349-2a.xhtml 379349-2-ref.xhtml +== 379349-2b.xhtml 379349-2-ref.xhtml skip-if(B2G) == 379349-3a.xhtml 379349-3-ref.xhtml skip-if(B2G) == 379349-3b.xhtml 379349-3-ref.xhtml == 379361-1.html 379361-1-ref.html From 1266bbd1d41bd0112e2aafb4522aac14815b2e1b Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Fri, 1 Mar 2013 13:06:16 +0900 Subject: [PATCH 031/140] Bug 396166 - xul:label@value accessible should implement nsIAccessibleText, r=tbsaunde, roc --- accessible/src/base/AccTypes.h | 1 + accessible/src/base/Logging.cpp | 2 +- .../src/base/nsAccessibilityService.cpp | 18 ++ accessible/src/base/nsAccessibilityService.h | 6 + accessible/src/generic/Accessible.h | 6 +- accessible/src/xul/XULElementAccessibles.cpp | 85 ++++++++- accessible/src/xul/XULElementAccessibles.h | 37 +++- accessible/tests/mochitest/common.js | 4 +- accessible/tests/mochitest/events.js | 13 +- accessible/tests/mochitest/events/Makefile.in | 1 + .../tests/mochitest/events/test_label.xul | 177 ++++++++++++++++++ accessible/tests/mochitest/text/Makefile.in | 1 + .../tests/mochitest/text/test_label.xul | 64 +++++++ .../tests/mochitest/tree/test_groupbox.xul | 21 +-- layout/xul/base/src/nsTextBoxFrame.cpp | 22 ++- layout/xul/base/src/nsTextBoxFrame.h | 4 + 16 files changed, 436 insertions(+), 26 deletions(-) create mode 100644 accessible/tests/mochitest/events/test_label.xul create mode 100644 accessible/tests/mochitest/text/test_label.xul diff --git a/accessible/src/base/AccTypes.h b/accessible/src/base/AccTypes.h index 2f5886fbe44..44b7c7fa0a5 100644 --- a/accessible/src/base/AccTypes.h +++ b/accessible/src/base/AccTypes.h @@ -52,6 +52,7 @@ enum AccType { eMenuPopupType, eProgressType, eRootType, + eXULLabelType, eXULTabpanelsType, eXULTreeType, diff --git a/accessible/src/base/Logging.cpp b/accessible/src/base/Logging.cpp index d19cb39bf1e..2086d82936c 100644 --- a/accessible/src/base/Logging.cpp +++ b/accessible/src/base/Logging.cpp @@ -65,7 +65,7 @@ EnableLogging(const char* aModulesStr) size_t tokenLen = strcspn(token, ","); for (unsigned int idx = 0; idx < ArrayLength(sModuleMap); idx++) { if (strncmp(token, sModuleMap[idx].mStr, tokenLen) == 0) { -#if !defined(MOZ_PROFILING) && (!defined(MOZ_DEBUG) || defined(MOZ_OPTIMIZE)) +#if !defined(MOZ_PROFILING) && (!defined(DEBUG) || defined(MOZ_OPTIMIZE)) // Stack tracing on profiling enabled or debug not optimized builds. if (strncmp(token, "stack", tokenLen) == 0) break; diff --git a/accessible/src/base/nsAccessibilityService.cpp b/accessible/src/base/nsAccessibilityService.cpp index 418f20457ce..49f31b346a9 100644 --- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -402,6 +402,24 @@ nsAccessibilityService::UpdateImageMap(nsImageFrame* aImageFrame) } } +void +nsAccessibilityService::UpdateLabelValue(nsIPresShell* aPresShell, + nsIContent* aLabelElm, + const nsString& aNewValue) +{ + DocAccessible* document = GetDocAccessible(aPresShell); + if (document) { + Accessible* accessible = document->GetAccessible(aLabelElm); + if (accessible) { + XULLabelAccessible* xulLabel = accessible->AsXULLabel(); + NS_ASSERTION(xulLabel, + "UpdateLabelValue was called for wrong accessible!"); + if (xulLabel) + xulLabel->UpdateLabelValue(aNewValue); + } + } +} + void nsAccessibilityService::PresShellActivated(nsIPresShell* aPresShell) { diff --git a/accessible/src/base/nsAccessibilityService.h b/accessible/src/base/nsAccessibilityService.h index adf36156665..f7b8bb5f7b9 100644 --- a/accessible/src/base/nsAccessibilityService.h +++ b/accessible/src/base/nsAccessibilityService.h @@ -104,6 +104,12 @@ public: */ void UpdateImageMap(nsImageFrame* aImageFrame); + /** + * Update the label accessible tree when rendered @value is changed. + */ + void UpdateLabelValue(nsIPresShell* aPresShell, nsIContent* aLabelElm, + const nsString& aNewValue); + /** * Notify accessibility that anchor jump has been accomplished to the given * target. Used by layout. diff --git a/accessible/src/generic/Accessible.h b/accessible/src/generic/Accessible.h index ed3e14dba1e..098293509db 100644 --- a/accessible/src/generic/Accessible.h +++ b/accessible/src/generic/Accessible.h @@ -46,6 +46,7 @@ class Relation; class TableAccessible; class TableCellAccessible; class TextLeafAccessible; +class XULLabelAccessible; class XULTreeAccessible; /** @@ -523,6 +524,9 @@ public: bool IsTextLeaf() const { return mType == eTextLeafType; } TextLeafAccessible* AsTextLeaf(); + bool IsXULLabel() const { return mType == eXULLabelType; } + XULLabelAccessible* AsXULLabel(); + bool IsXULTabpanels() const { return mType == eXULTabpanelsType; } bool IsXULTree() const { return mType == eXULTreeType; } @@ -892,7 +896,7 @@ protected: static const uint8_t kChildrenFlagsBits = 2; static const uint8_t kStateFlagsBits = 5; - static const uint8_t kTypeBits = 5; + static const uint8_t kTypeBits = 6; static const uint8_t kGenericTypesBits = 12; /** diff --git a/accessible/src/xul/XULElementAccessibles.cpp b/accessible/src/xul/XULElementAccessibles.cpp index 93bf28c384e..289fda2dfb1 100644 --- a/accessible/src/xul/XULElementAccessibles.cpp +++ b/accessible/src/xul/XULElementAccessibles.cpp @@ -7,18 +7,25 @@ #include "Accessible-inl.h" #include "BaseAccessibles.h" +#include "DocAccessible-inl.h" #include "nsAccUtils.h" #include "nsCoreUtils.h" #include "nsTextEquivUtils.h" #include "Relation.h" #include "Role.h" #include "States.h" +#include "TextUpdater.h" + +#ifdef A11Y_LOG +#include "Logging.h" +#endif #include "nsIAccessibleRelation.h" #include "nsIDOMXULDescriptionElement.h" #include "nsINameSpaceManager.h" -#include "nsString.h" #include "nsNetUtil.h" +#include "nsString.h" +#include "nsTextBoxFrame.h" using namespace mozilla::a11y; @@ -30,6 +37,31 @@ XULLabelAccessible:: XULLabelAccessible(nsIContent* aContent, DocAccessible* aDoc) : HyperTextAccessibleWrap(aContent, aDoc) { + mType = eXULLabelType; + + // If @value attribute is given then it's rendered instead text content. In + // this case we need to create a text leaf accessible to make @value attribute + // accessible. + // XXX: text interface doesn't let you get the text by words. + nsTextBoxFrame* textBoxFrame = do_QueryFrame(mContent->GetPrimaryFrame()); + if (textBoxFrame) { + mValueTextLeaf = new XULLabelTextLeafAccessible(mContent, mDoc); + if (mDoc->BindToDocument(mValueTextLeaf, nullptr)) { + nsAutoString text; + textBoxFrame->GetCroppedTitle(text); + mValueTextLeaf->SetText(text); + return; + } + + mValueTextLeaf = nullptr; + } +} + +void +XULLabelAccessible::Shutdown() +{ + mValueTextLeaf = nullptr; + HyperTextAccessibleWrap::Shutdown(); } ENameValueFlag @@ -37,7 +69,9 @@ XULLabelAccessible::NativeName(nsString& aName) { // if the value attr doesn't exist, the screen reader must get the accessible text // from the accessible text interface or from the children - mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName); + if (mValueTextLeaf) + return mValueTextLeaf->Name(aName); + return eNameOK; } @@ -72,6 +106,53 @@ XULLabelAccessible::RelationByType(uint32_t aType) return rel; } +void +XULLabelAccessible::UpdateLabelValue(const nsString& aValue) +{ +#ifdef A11Y_LOG + if (logging::IsEnabled(logging::eText)) { + logging::MsgBegin("TEXT", "text may be changed (xul:label @value update)"); + logging::Node("container", mContent); + logging::MsgEntry("old text '%s'", + NS_ConvertUTF16toUTF8(mValueTextLeaf->Text()).get()); + logging::MsgEntry("new text: '%s'", + NS_ConvertUTF16toUTF8(aValue).get()); + logging::MsgEnd(); + } +#endif + + TextUpdater::Run(mDoc, mValueTextLeaf, aValue); +} + +void +XULLabelAccessible::CacheChildren() +{ + if (mValueTextLeaf) { + AppendChild(mValueTextLeaf); + return; + } + + // Cache children from subtree. + AccessibleWrap::CacheChildren(); +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULLabelTextLeafAccessible +//////////////////////////////////////////////////////////////////////////////// + +role +XULLabelTextLeafAccessible::NativeRole() +{ + return roles::TEXT_LEAF; +} + +uint64_t +XULLabelTextLeafAccessible::NativeState() +{ + return TextLeafAccessibleWrap::NativeState() | states::READONLY; +} + //////////////////////////////////////////////////////////////////////////////// // XULTooltipAccessible diff --git a/accessible/src/xul/XULElementAccessibles.h b/accessible/src/xul/XULElementAccessibles.h index ee5ea233b48..9fbdc97305f 100644 --- a/accessible/src/xul/XULElementAccessibles.h +++ b/accessible/src/xul/XULElementAccessibles.h @@ -6,12 +6,14 @@ #ifndef mozilla_a11y_XULElementAccessibles_h__ #define mozilla_a11y_XULElementAccessibles_h__ -#include "BaseAccessibles.h" #include "HyperTextAccessibleWrap.h" +#include "TextLeafAccessibleWrap.h" namespace mozilla { namespace a11y { +class XULLabelTextLeafAccessible; + /** * Used for XUL description and label elements. */ @@ -21,15 +23,48 @@ public: XULLabelAccessible(nsIContent* aContent, DocAccessible* aDoc); // Accessible + virtual void Shutdown(); virtual a11y::role NativeRole(); virtual uint64_t NativeState(); virtual Relation RelationByType(uint32_t aRelationType); + void UpdateLabelValue(const nsString& aValue); + protected: // Accessible virtual ENameValueFlag NativeName(nsString& aName) MOZ_OVERRIDE; + virtual void CacheChildren() MOZ_OVERRIDE; + +private: + nsRefPtr mValueTextLeaf; }; +inline XULLabelAccessible* +Accessible::AsXULLabel() +{ + return IsXULLabel() ? static_cast(this) : nullptr; +} + + +/** + * Used to implement text interface on XUL label accessible in case when text + * is provided by @value attribute (no underlying text frame). + */ +class XULLabelTextLeafAccessible MOZ_FINAL : public TextLeafAccessibleWrap +{ +public: + XULLabelTextLeafAccessible(nsIContent* aContent, DocAccessible* aDoc) : + TextLeafAccessibleWrap(aContent, aDoc) + { mStateFlags |= eSharedNode; } + + virtual ~XULLabelTextLeafAccessible() { } + + // Accessible + virtual a11y::role NativeRole() MOZ_OVERRIDE; + virtual uint64_t NativeState() MOZ_OVERRIDE; +}; + + /** * Used for XUL tooltip element. */ diff --git a/accessible/tests/mochitest/common.js b/accessible/tests/mochitest/common.js index 74de033d9e2..01ea3b0a036 100644 --- a/accessible/tests/mochitest/common.js +++ b/accessible/tests/mochitest/common.js @@ -582,13 +582,13 @@ function getTextFromClipboard() var clip = Components.classes["@mozilla.org/widget/clipboard;1"]. getService(Components.interfaces.nsIClipboard); if (!clip) - return; + return ""; var trans = Components.classes["@mozilla.org/widget/transferable;1"]. createInstance(Components.interfaces.nsITransferable); trans.init(getLoadContext()); if (!trans) - return; + return ""; trans.addDataFlavor("text/unicode"); clip.getData(trans, clip.kGlobalClipboard); diff --git a/accessible/tests/mochitest/events.js b/accessible/tests/mochitest/events.js index cfe1733ecec..5d4e3e24011 100644 --- a/accessible/tests/mochitest/events.js +++ b/accessible/tests/mochitest/events.js @@ -1548,16 +1548,21 @@ function textChangeChecker(aID, aStart, aEnd, aTextOrFunc, aIsInserted, aFromUse { this.target = getNode(aID); this.type = aIsInserted ? EVENT_TEXT_INSERTED : EVENT_TEXT_REMOVED; + this.startOffset = aStart; + this.endOffset = aEnd; + this.textOrFunc = aTextOrFunc; this.check = function textChangeChecker_check(aEvent) { aEvent.QueryInterface(nsIAccessibleTextChangeEvent); - var modifiedText = (typeof aTextOrFunc == "function") ? - aTextOrFunc() : aTextOrFunc; - var modifiedTextLen = (aEnd == -1) ? modifiedText.length : aEnd - aStart; + var modifiedText = (typeof this.textOrFunc == "function") ? + this.textOrFunc() : this.textOrFunc; + var modifiedTextLen = + (this.endOffset == -1) ? modifiedText.length : aEnd - aStart; - is(aEvent.start, aStart, "Wrong start offset for " + prettyName(aID)); + is(aEvent.start, this.startOffset, + "Wrong start offset for " + prettyName(aID)); is(aEvent.length, modifiedTextLen, "Wrong length for " + prettyName(aID)); var changeInfo = (aIsInserted ? "inserted" : "removed"); is(aEvent.isInserted(), aIsInserted, diff --git a/accessible/tests/mochitest/events/Makefile.in b/accessible/tests/mochitest/events/Makefile.in index 06b3e408c85..3bf9d64cb3d 100644 --- a/accessible/tests/mochitest/events/Makefile.in +++ b/accessible/tests/mochitest/events/Makefile.in @@ -46,6 +46,7 @@ MOCHITEST_A11Y_FILES =\ test_focus_tabbox.xul \ test_focus_tree.xul \ test_fromUserInput.html \ + test_label.xul \ test_menu.xul \ test_mutation.html \ test_mutation.xhtml \ diff --git a/accessible/tests/mochitest/events/test_label.xul b/accessible/tests/mochitest/events/test_label.xul new file mode 100644 index 00000000000..e82763a7cbd --- /dev/null +++ b/accessible/tests/mochitest/events/test_label.xul @@ -0,0 +1,177 @@ + + + + + + + + + + + + Bug 396166 + +
+

+ +
+      
+ + + + + hello + + + + +
+ +
+ diff --git a/accessible/tests/mochitest/text/Makefile.in b/accessible/tests/mochitest/text/Makefile.in index f130fc3d3b8..355f6afe934 100644 --- a/accessible/tests/mochitest/text/Makefile.in +++ b/accessible/tests/mochitest/text/Makefile.in @@ -15,6 +15,7 @@ MOCHITEST_A11Y_FILES = \ doc.html \ test_doc.html \ test_hypertext.html \ + test_label.xul \ test_passwords.html \ test_selection.html \ test_singleline.html \ diff --git a/accessible/tests/mochitest/text/test_label.xul b/accessible/tests/mochitest/text/test_label.xul new file mode 100644 index 00000000000..459928b86e2 --- /dev/null +++ b/accessible/tests/mochitest/text/test_label.xul @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + Bug 396166 + +

+ +
+    
+ +
+
diff --git a/accessible/tests/mochitest/tree/test_groupbox.xul b/accessible/tests/mochitest/tree/test_groupbox.xul index cf471bab34e..0b68e601803 100644 --- a/accessible/tests/mochitest/tree/test_groupbox.xul +++ b/accessible/tests/mochitest/tree/test_groupbox.xul @@ -21,20 +21,13 @@ function doTest() { - var accTree = { - role: ROLE_GROUPING, - children: [ - { - role: ROLE_LABEL, - children: [ ] - }, - { - role: ROLE_CHECKBUTTON, - children: [ ] - } - ] - }; - + var accTree = + { GROUPING: [ + { LABEL: [ + { TEXT_LEAF: [ ] } + ] }, + { CHECKBUTTON: [ ] } + ] }; testAccessibleTree("groupbox", accTree); SimpleTest.finish() diff --git a/layout/xul/base/src/nsTextBoxFrame.cpp b/layout/xul/base/src/nsTextBoxFrame.cpp index 69b3cc67f6e..ad9e26a3cbc 100644 --- a/layout/xul/base/src/nsTextBoxFrame.cpp +++ b/layout/xul/base/src/nsTextBoxFrame.cpp @@ -31,6 +31,10 @@ #include "nsLayoutUtils.h" #include "mozilla/Attributes.h" +#ifdef ACCESSIBILITY +#include "nsAccessibilityService.h" +#endif + #ifdef IBMBIDI #include "nsBidiUtils.h" #include "nsBidiPresUtils.h" @@ -59,6 +63,9 @@ NS_NewTextBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) NS_IMPL_FRAMEARENA_HELPERS(nsTextBoxFrame) +NS_QUERYFRAME_HEAD(nsTextBoxFrame) + NS_QUERYFRAME_ENTRY(nsTextBoxFrame) +NS_QUERYFRAME_TAIL_INHERITING(nsTextBoxFrameSuper) NS_IMETHODIMP nsTextBoxFrame::AttributeChanged(int32_t aNameSpaceID, @@ -596,8 +603,10 @@ nsTextBoxFrame::CalculateTitleForWidth(nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, nscoord aWidth) { - if (mTitle.IsEmpty()) + if (mTitle.IsEmpty()) { + mCroppedTitle.Truncate(); return 0; + } nsRefPtr fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); @@ -993,6 +1002,17 @@ nsTextBoxFrame::CalcDrawRect(nsRenderingContext &aRenderingContext) // determine (cropped) title which fits in aRect.width and its width nscoord titleWidth = CalculateTitleForWidth(presContext, aRenderingContext, textRect.width); + +#ifdef ACCESSIBILITY + // Make sure to update the accessible tree in case when cropped title is + // changed. + nsAccessibilityService* accService = GetAccService(); + if (accService) { + accService->UpdateLabelValue(PresContext()->PresShell(), mContent, + mCroppedTitle); + } +#endif + // determine if and at which position to put the underline UpdateAccessIndex(); diff --git a/layout/xul/base/src/nsTextBoxFrame.h b/layout/xul/base/src/nsTextBoxFrame.h index 7df6cd1d3bc..d0bd1f72a5f 100644 --- a/layout/xul/base/src/nsTextBoxFrame.h +++ b/layout/xul/base/src/nsTextBoxFrame.h @@ -14,6 +14,8 @@ typedef nsLeafBoxFrame nsTextBoxFrameSuper; class nsTextBoxFrame : public nsTextBoxFrameSuper { public: + NS_DECL_QUERYFRAME_TARGET(nsTextBoxFrame) + NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS virtual nsSize GetPrefSize(nsBoxLayoutState& aBoxLayoutState); @@ -59,6 +61,8 @@ public: virtual bool ComputesOwnOverflowArea(); + void GetCroppedTitle(nsString& aTitle) const { aTitle = mCroppedTitle; } + protected: friend class nsAsyncAccesskeyUpdate; friend class nsDisplayXULTextBox; From 943b4338f1203b272f679dbd18eba933e387a70e Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Thu, 28 Feb 2013 21:35:26 -0800 Subject: [PATCH 032/140] Bug 683159: Remove SimpleTest.expectAssertions() calls no longer needed due to SpecialPowers.gc() added in changeset 67060725ec8d. --- toolkit/content/tests/chrome/test_bug471776.xul | 5 ----- toolkit/content/tests/chrome/test_bug509732.xul | 3 --- toolkit/content/tests/chrome/test_bug554279.xul | 8 -------- toolkit/content/tests/chrome/test_bug557987.xul | 4 ---- toolkit/content/tests/chrome/test_bug562554.xul | 6 ------ toolkit/content/tests/chrome/test_bug570192.xul | 8 -------- toolkit/content/tests/chrome/test_bug585946.xul | 4 ---- toolkit/content/tests/chrome/test_bug624329.xul | 2 -- toolkit/content/tests/chrome/test_bug792324.xul | 2 +- toolkit/content/tests/chrome/test_button.xul | 2 -- toolkit/content/tests/chrome/test_chromemargin.xul | 4 ---- toolkit/content/tests/chrome/test_closemenu_attribute.xul | 2 +- toolkit/content/tests/chrome/test_colorpicker_popup.xul | 2 +- toolkit/content/tests/chrome/test_contextmenu_list.xul | 6 ++---- toolkit/content/tests/chrome/test_datepicker.xul | 2 -- toolkit/content/tests/chrome/test_deck.xul | 2 -- toolkit/content/tests/chrome/test_dialogfocus.xul | 2 -- toolkit/content/tests/chrome/test_findbar.xul | 2 -- toolkit/content/tests/chrome/test_findbar_events.xul | 6 ------ toolkit/content/tests/chrome/test_focus_anons.xul | 2 -- toolkit/content/tests/chrome/test_hiddenitems.xul | 2 -- toolkit/content/tests/chrome/test_hiddenpaging.xul | 2 -- toolkit/content/tests/chrome/test_keys.xul | 1 - toolkit/content/tests/chrome/test_largemenu.xul | 6 ------ toolkit/content/tests/chrome/test_menu.xul | 4 ---- toolkit/content/tests/chrome/test_menuchecks.xul | 4 ---- toolkit/content/tests/chrome/test_menuitem_blink.xul | 4 ---- toolkit/content/tests/chrome/test_menuitem_commands.xul | 4 ---- toolkit/content/tests/chrome/test_menulist.xul | 4 ---- toolkit/content/tests/chrome/test_menulist_keynav.xul | 4 ---- toolkit/content/tests/chrome/test_mousecapture.xul | 6 +----- toolkit/content/tests/chrome/test_notificationbox.xul | 2 -- toolkit/content/tests/chrome/test_panel.xul | 4 ---- 33 files changed, 6 insertions(+), 115 deletions(-) diff --git a/toolkit/content/tests/chrome/test_bug471776.xul b/toolkit/content/tests/chrome/test_bug471776.xul index da418256fc9..6002c691ac1 100644 --- a/toolkit/content/tests/chrome/test_bug471776.xul +++ b/toolkit/content/tests/chrome/test_bug471776.xul @@ -30,11 +30,6 @@ diff --git a/layout/base/tests/chrome/test_transformed_scrolling_repaints.html b/layout/base/tests/chrome/test_transformed_scrolling_repaints.html index 3e754fd0ffe..29e23ce06f4 100644 --- a/layout/base/tests/chrome/test_transformed_scrolling_repaints.html +++ b/layout/base/tests/chrome/test_transformed_scrolling_repaints.html @@ -15,11 +15,6 @@
 
+
+
diff --git a/dom/bindings/test/test_InstanceOf.html b/dom/bindings/test/test_InstanceOf.html
index 3a5a76b1b21..514ec1b2aa4 100644
--- a/dom/bindings/test/test_InstanceOf.html
+++ b/dom/bindings/test/test_InstanceOf.html
@@ -13,16 +13,42 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=748983
 Mozilla Bug 748983
 

 
 
+ From e54e250210a9cdc1975e217f3b7666040d6f884b Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Fri, 1 Mar 2013 17:05:58 -0800 Subject: [PATCH 053/140] Bug 748469 - (Part 1) Move FormAssistPopup viewport math to Java. r=kats --- mobile/android/base/FormAssistPopup.java | 34 +++++++++++++----------- mobile/android/chrome/content/browser.js | 20 ++------------ 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/mobile/android/base/FormAssistPopup.java b/mobile/android/base/FormAssistPopup.java index e593c943093..b58e7a13070 100644 --- a/mobile/android/base/FormAssistPopup.java +++ b/mobile/android/base/FormAssistPopup.java @@ -6,6 +6,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.gfx.FloatSize; +import org.mozilla.gecko.gfx.ImmutableViewportMetrics; import org.mozilla.gecko.util.GeckoEventListener; import org.json.JSONArray; @@ -99,24 +100,22 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene private void handleAutoCompleteMessage(JSONObject message) throws JSONException { final JSONArray suggestions = message.getJSONArray("suggestions"); - final JSONArray rect = message.getJSONArray("rect"); - final double zoom = message.getDouble("zoom"); + final JSONObject rect = message.getJSONObject("rect"); GeckoApp.mAppContext.mMainHandler.post(new Runnable() { @Override public void run() { - showAutoCompleteSuggestions(suggestions, rect, zoom); + showAutoCompleteSuggestions(suggestions, rect); } }); } private void handleValidationMessage(JSONObject message) throws JSONException { final String validationMessage = message.getString("validationMessage"); - final JSONArray rect = message.getJSONArray("rect"); - final double zoom = message.getDouble("zoom"); + final JSONObject rect = message.getJSONObject("rect"); GeckoApp.mAppContext.mMainHandler.post(new Runnable() { @Override public void run() { - showValidationMessage(validationMessage, rect, zoom); + showValidationMessage(validationMessage, rect); } }); } @@ -130,7 +129,7 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene }); } - private void showAutoCompleteSuggestions(JSONArray suggestions, JSONArray rect, double zoom) { + private void showAutoCompleteSuggestions(JSONArray suggestions, JSONObject rect) { if (mAutoCompleteList == null) { LayoutInflater inflater = LayoutInflater.from(mContext); mAutoCompleteList = (ListView) inflater.inflate(R.layout.autocomplete_list, null); @@ -154,10 +153,10 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene adapter.populateSuggestionsList(suggestions); mAutoCompleteList.setAdapter(adapter); - positionAndShowPopup(rect, zoom, true); + positionAndShowPopup(rect, true); } - private void showValidationMessage(String validationMessage, JSONArray rect, double zoom) { + private void showValidationMessage(String validationMessage, JSONObject rect) { if (mValidationMessage == null) { LayoutInflater inflater = LayoutInflater.from(mContext); mValidationMessage = (RelativeLayout) inflater.inflate(R.layout.validation_message, null); @@ -182,11 +181,11 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene // We need to set the text as selected for the marquee text to work. mValidationMessageText.setSelected(true); - positionAndShowPopup(rect, zoom, false); + positionAndShowPopup(rect, false); } // Returns true if the popup is successfully shown, false otherwise - private boolean positionAndShowPopup(JSONArray rect, double zoom, boolean isAutoComplete) { + private boolean positionAndShowPopup(JSONObject rect, boolean isAutoComplete) { // Don't show the form assist popup when using fullscreen VKB InputMethodManager imm = (InputMethodManager) GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE); @@ -211,6 +210,9 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene sValidationMessageHeight = (int) (res.getDimension(R.dimen.validation_message_height)); } + ImmutableViewportMetrics viewportMetrics = GeckoApp.mAppContext.getLayerView().getViewportMetrics(); + float zoom = viewportMetrics.zoomFactor; + // These values correspond to the input box for which we want to // display the FormAssistPopup. int left = 0; @@ -219,16 +221,16 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene int height = 0; try { - left = (int) (rect.getDouble(0) * zoom); - top = (int) (rect.getDouble(1) * zoom); - width = (int) (rect.getDouble(2) * zoom); - height = (int) (rect.getDouble(3) * zoom); + left = (int) (rect.getDouble("x") * zoom - viewportMetrics.viewportRectLeft); + top = (int) (rect.getDouble("y") * zoom - viewportMetrics.viewportRectTop); + width = (int) (rect.getDouble("w") * zoom); + height = (int) (rect.getDouble("h") * zoom); } catch (JSONException e) { } int popupWidth = RelativeLayout.LayoutParams.FILL_PARENT; int popupLeft = left < 0 ? 0 : left; - FloatSize viewport = GeckoApp.mAppContext.getLayerView().getViewportMetrics().getSize(); + FloatSize viewport = viewportMetrics.getSize(); // For autocomplete suggestions, if the input is smaller than the screen-width, // shrink the popup's width. Otherwise, keep it as FILL_PARENT. diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index e99c3470cb8..3f8c1dfc266 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -5178,18 +5178,6 @@ var FormAssistant = { return suggestions; }, - // Gets the element position data necessary for the Java UI to position - // the form assist popup. - _getElementPositionData: function _getElementPositionData(aElement) { - let rect = ElementTouchHelper.getBoundingContentRect(aElement); - let viewport = BrowserApp.selectedTab.getViewport(); - - return { rect: [rect.x - (viewport.x / viewport.zoom), - rect.y - (viewport.y / viewport.zoom), - rect.w, rect.h], - zoom: viewport.zoom } - }, - // Retrieves autocomplete suggestions for an element from the form autocomplete service // and sends the suggestions to the Java UI, along with element position data. // Returns true if there are suggestions to show, false otherwise. @@ -5214,12 +5202,10 @@ var FormAssistant = { if (!suggestions.length) return false; - let positionData = this._getElementPositionData(aElement); sendMessageToJava({ type: "FormAssist:AutoComplete", suggestions: suggestions, - rect: positionData.rect, - zoom: positionData.zoom + rect: ElementTouchHelper.getBoundingContentRect(aElement) }); // Keep track of input element so we can fill it in if the user @@ -5249,12 +5235,10 @@ var FormAssistant = { if (!this._isValidateable(aElement)) return false; - let positionData = this._getElementPositionData(aElement); sendMessageToJava({ type: "FormAssist:ValidationMessage", validationMessage: aElement.validationMessage, - rect: positionData.rect, - zoom: positionData.zoom + rect: ElementTouchHelper.getBoundingContentRect(aElement) }); return true; From 1b5209a5f3db6476e326c94321314a63a3d19a11 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Fri, 1 Mar 2013 17:06:00 -0800 Subject: [PATCH 054/140] Bug 748469 - (Part 2) Clean up FormAssist.positionAndShowPopup. r=wesj --- mobile/android/base/FormAssistPopup.java | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/mobile/android/base/FormAssistPopup.java b/mobile/android/base/FormAssistPopup.java index b58e7a13070..7ff85ce082d 100644 --- a/mobile/android/base/FormAssistPopup.java +++ b/mobile/android/base/FormAssistPopup.java @@ -52,7 +52,7 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene private static RelativeLayout.LayoutParams sValidationTextLayoutNormal; private static RelativeLayout.LayoutParams sValidationTextLayoutInverted; - private static final String LOGTAG = "FormAssistPopup"; + private static final String LOGTAG = "GeckoFormAssistPopup"; // The blocklist is so short that ArrayList is probably cheaper than HashSet. private static final Collection sInputMethodBlocklist = Arrays.asList(new String[] { @@ -184,18 +184,12 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene positionAndShowPopup(rect, false); } - // Returns true if the popup is successfully shown, false otherwise - private boolean positionAndShowPopup(JSONObject rect, boolean isAutoComplete) { + private void positionAndShowPopup(JSONObject rect, boolean isAutoComplete) { // Don't show the form assist popup when using fullscreen VKB InputMethodManager imm = (InputMethodManager) GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE); if (imm.isFullscreenMode()) - return false; - - if (!isShown()) { - setVisibility(VISIBLE); - startAnimation(mAnimation); - } + return; // Hide/show the appropriate popup contents if (mAutoCompleteList != null) @@ -225,7 +219,11 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene top = (int) (rect.getDouble("y") * zoom - viewportMetrics.viewportRectTop); width = (int) (rect.getDouble("w") * zoom); height = (int) (rect.getDouble("h") * zoom); - } catch (JSONException e) { } + } catch (JSONException e) { + // Bail if we can't get the correct dimensions for the popup. + Log.e(LOGTAG, "Error getting FormAssistPopup dimensions", e); + return; + } int popupWidth = RelativeLayout.LayoutParams.FILL_PARENT; int popupLeft = left < 0 ? 0 : left; @@ -292,7 +290,10 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene setLayoutParams(layoutParams); requestLayout(); - return true; + if (!isShown()) { + setVisibility(VISIBLE); + startAnimation(mAnimation); + } } public void hide() { From d28353ab8afcae2217150dd3a61f5f2d85cd9484 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Fri, 1 Mar 2013 17:06:03 -0800 Subject: [PATCH 055/140] Bug 846102 - Remove sub-frame offset from cursor handle position calculation. r=bnicholson --- mobile/android/chrome/content/browser.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 3f8c1dfc266..dccfa33a66e 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2396,9 +2396,6 @@ var SelectionHandler = { }, positionHandles: function sh_positionHandles() { - // Translate coordinates to account for selections in sub-frames. We can't cache - // this because the top-level page may have scrolled since selection started. - let offset = this._getViewOffset(); let scrollX = {}, scrollY = {}; this._view.top.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).getScrollXY(false, scrollX, scrollY); @@ -2418,26 +2415,33 @@ var SelectionHandler = { let positions = null; if (this._activeType == this.TYPE_CURSOR) { + // The left and top properties returned are relative to the client area + // of the window, so we don't need to account for a sub-frame offset. let cursor = this._cwu.sendQueryContentEvent(this._cwu.QUERY_CARET_RECT, this._target.selectionEnd, 0, 0, 0); let x = cursor.left; let y = cursor.top + cursor.height; - positions = [ { handle: this.HANDLE_TYPE_MIDDLE, - left: x + offset.x + scrollX.value, - top: y + offset.y + scrollY.value, - hidden: checkHidden(x, y) } ]; + positions = [{ handle: this.HANDLE_TYPE_MIDDLE, + left: x + scrollX.value, + top: y + scrollY.value, + hidden: checkHidden(x, y) }]; } else { let sx = this.cache.start.x; let sy = this.cache.start.y; let ex = this.cache.end.x; let ey = this.cache.end.y; - positions = [ { handle: this.HANDLE_TYPE_START, + + // Translate coordinates to account for selections in sub-frames. We can't cache + // this because the top-level page may have scrolled since selection started. + let offset = this._getViewOffset(); + + positions = [{ handle: this.HANDLE_TYPE_START, left: sx + offset.x + scrollX.value, top: sy + offset.y + scrollY.value, hidden: checkHidden(sx, sy) }, { handle: this.HANDLE_TYPE_END, left: ex + offset.x + scrollX.value, top: ey + offset.y + scrollY.value, - hidden: checkHidden(ex, ey) } ]; + hidden: checkHidden(ex, ey) }]; } sendMessageToJava({ From 1745d82807e29aaed6ae1b3cef12aab66933f38f Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Fri, 1 Mar 2013 17:08:35 -0800 Subject: [PATCH 056/140] Bug 638219 - Fix test selection after prior landings for bug 638219; rs=Waldo --HG-- extra : rebase_source : eb107700ec8c3e136323ef8173d3d3ec13418de0 --- js/src/jit-test/jit_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jit-test/jit_test.py b/js/src/jit-test/jit_test.py index 0e226811fd1..0b20a4ec7c8 100755 --- a/js/src/jit-test/jit_test.py +++ b/js/src/jit-test/jit_test.py @@ -79,7 +79,7 @@ def main(argv): if len(args) < 1: op.error('missing JS_SHELL argument') # We need to make sure we are using backslashes on Windows. - test_args = args[:1] + test_args = args[1:] if jittests.stdio_might_be_broken(): # Prefer erring on the side of caution and not using stdio if From 71d8d1dff01c4ff33eb6e4f955bce720e733178f Mon Sep 17 00:00:00 2001 From: Geoff Brown Date: Fri, 1 Mar 2013 18:41:05 -0700 Subject: [PATCH 057/140] Bug 846353: Fix typos in robocop testBookmarksTab, testDoorHanger and testShareLink; r=jmaher DONTBUILD --- mobile/android/base/tests/testBookmarksTab.java.in | 4 ++-- mobile/android/base/tests/testDoorHanger.java.in | 8 ++++---- mobile/android/base/tests/testShareLink.java.in | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mobile/android/base/tests/testBookmarksTab.java.in b/mobile/android/base/tests/testBookmarksTab.java.in index bf725dd9153..17ee99c1a90 100644 --- a/mobile/android/base/tests/testBookmarksTab.java.in +++ b/mobile/android/base/tests/testBookmarksTab.java.in @@ -165,7 +165,7 @@ public class testBookmarksTab extends BaseTest { mSolo.clickOnText("Remove"); // Wait for the toaster notification - waitForText("Bookmark Removed"); + waitForText("Bookmark removed"); // Verify Bookmark is removed child = list.getChildAt(1); @@ -197,7 +197,7 @@ public class testBookmarksTab extends BaseTest { mSolo.clickOnEditText(fieldIndex); mActions.sendKeys(addedText); mSolo.clickOnText("OK"); - waitForText("Bookmark Updated"); + waitForText("Bookmark updated"); mActions.sendSpecialKey(Actions.SpecialKey.BACK); // close the VKB } diff --git a/mobile/android/base/tests/testDoorHanger.java.in b/mobile/android/base/tests/testDoorHanger.java.in index 24fd3fc84e5..9ef92a224a0 100644 --- a/mobile/android/base/tests/testDoorHanger.java.in +++ b/mobile/android/base/tests/testDoorHanger.java.in @@ -76,11 +76,11 @@ public class testDoorHanger extends BaseTest { // Load login page loadUrl(LOGIN_URL); - waitForText("to remeber password for"); + waitForText("to remember password for"); // Test doorhanger is dismissed when tapping "Not Now" mSolo.clickOnText("Not Now"); - mAsserter.is(mSolo.searchText("to remeber password for"), false, "Doorhanger notification is hidden"); + mAsserter.is(mSolo.searchText("to remember password for"), false, "Doorhanger notification is hidden"); // Load login page loadUrl(LOGIN_URL); @@ -88,10 +88,10 @@ public class testDoorHanger extends BaseTest { // Test doorhanger is dismissed when tapping "Never show" and is no longer triggered mSolo.clickOnText("Remember"); - mAsserter.is(mSolo.searchText("to remeber password for"), false, "Doorhanger notification is hidden"); + mAsserter.is(mSolo.searchText("to remember password for"), false, "Doorhanger notification is hidden"); // Reload the page and check that there is no doorhanger displayed loadUrl(LOGIN_URL); - mAsserter.is(mSolo.searchText("to remeber password for"), false, "Doorhanger notification is hidden"); + mAsserter.is(mSolo.searchText("to remember password for"), false, "Doorhanger notification is hidden"); } } diff --git a/mobile/android/base/tests/testShareLink.java.in b/mobile/android/base/tests/testShareLink.java.in index d8b6a86a277..e81065acccc 100644 --- a/mobile/android/base/tests/testShareLink.java.in +++ b/mobile/android/base/tests/testShareLink.java.in @@ -35,7 +35,7 @@ public class testShareLink extends BaseTest { loadUrl(url); waitForText("Big Link"); // Waiting for page title to ensure the page is loaded selectMenuItem("Share"); - waitForText("Share Via"); + waitForText("Share via"); // Get list of current avaliable share activities and verify them shareOptions = getShareOptions(); @@ -94,7 +94,7 @@ public class testShareLink extends BaseTest { public void verifySharePopup(ArrayList shareOptions, String openedFrom) { waitForText("Share"); mSolo.clickOnText("Share"); - waitForText("Share Via"); + waitForText("Share via"); ArrayList displayedOptions = getSharePopupOption(); for (String option:shareOptions) { // Verify if the option is present in the list of displayed share options From cd5b12be6b8dcb9cdcffd2d6884c66d6e2cc3a04 Mon Sep 17 00:00:00 2001 From: "Nicholas D. Matsakis" Date: Fri, 1 Mar 2013 13:48:13 -0500 Subject: [PATCH 058/140] Bug 829602 - Switch to self-hosted version of Parallel Array r=dvander,till --- js/src/Makefile.in | 1 + js/src/builtin/ParallelArray-inl.h | 211 -- js/src/builtin/ParallelArray.cpp | 2203 ++--------------- js/src/builtin/ParallelArray.h | 415 +--- js/src/builtin/ParallelArray.js | 1283 ++++++++++ js/src/builtin/Utilities.js | 1 + js/src/builtin/js2c.py | 5 +- js/src/builtin/macros.py | 20 + js/src/ion/CodeGenerator.cpp | 70 + js/src/ion/CodeGenerator.h | 2 + js/src/ion/IonBuilder.h | 7 + js/src/ion/MCallOptimize.cpp | 150 ++ js/src/ion/MIR.h | 4 + js/src/ion/VMFunctions.cpp | 15 + js/src/ion/VMFunctions.h | 3 +- js/src/jit-test/lib/parallelarray-helpers.js | 213 +- .../jit-test/tests/auto-regress/bug755564.js | 2 - .../jit-test/tests/auto-regress/bug784011.js | 2 - .../jit-test/tests/auto-regress/bug789107.js | 2 - .../tests/parallelarray/alloc-array.js | 32 + .../parallelarray/alloc-different-objs.js | 21 + .../tests/parallelarray/alloc-many-objs.js | 23 + .../jit-test/tests/parallelarray/alloc-obj.js | 13 + .../parallelarray/alloc-too-many-objs.js | 35 + .../tests/parallelarray/bailout-executed.js | 22 + .../parallelarray/bailout-never-executed.js | 16 + .../jit-test/tests/parallelarray/closure-1.js | 12 + .../jit-test/tests/parallelarray/closure-2.js | 11 + .../jit-test/tests/parallelarray/closure-3.js | 12 + .../jit-test/tests/parallelarray/closure-4.js | 12 + .../jit-test/tests/parallelarray/closure-5.js | 37 + .../jit-test/tests/parallelarray/closure-6.js | 48 + .../jit-test/tests/parallelarray/closure-7.js | 48 + .../jit-test/tests/parallelarray/closure-8.js | 53 + .../tests/parallelarray/compare-strings-eq.js | 6 + .../tests/parallelarray/compare-strings-ne.js | 6 + .../parallelarray/comprehension-in-loop.js | 19 + .../parallelarray/comprehension-nested.js | 16 + .../parallelarray/comprehension-scale.js | 2 +- .../parallelarray/comprehension-throws.js | 3 +- .../tests/parallelarray/constructor-1.js | 6 +- .../tests/parallelarray/constructor-2.js | 3 +- .../tests/parallelarray/constructor-3.js | 2 +- .../tests/parallelarray/constructor-4.js | 4 +- .../tests/parallelarray/constructor-5.js | 4 + .../jit-test/tests/parallelarray/element-1.js | 6 +- .../jit-test/tests/parallelarray/element-2.js | 16 +- .../jit-test/tests/parallelarray/element-3.js | 3 +- .../tests/parallelarray/enumerate-1.js | 3 +- .../jit-test/tests/parallelarray/filter-1.js | 14 - .../jit-test/tests/parallelarray/filter-2.js | 14 - .../jit-test/tests/parallelarray/filter-3.js | 14 - .../jit-test/tests/parallelarray/filter-4.js | 16 - .../tests/parallelarray/filter-all.js | 2 + .../filter-every-third-element.js | 5 + .../parallelarray/filter-non-divisible.js | 6 + .../tests/parallelarray/filter-none.js | 2 + .../tests/parallelarray/filter-throws.js | 11 - .../tests/parallelarray/filter-truthy.js | 18 + .../tests/parallelarray/filter-very-few.js | 2 + .../jit-test/tests/parallelarray/flatten-3.js | 24 + js/src/jit-test/tests/parallelarray/get-1.js | 2 +- js/src/jit-test/tests/parallelarray/get-2.js | 8 +- js/src/jit-test/tests/parallelarray/get-3.js | 2 +- js/src/jit-test/tests/parallelarray/get-4.js | 2 +- js/src/jit-test/tests/parallelarray/get-5.js | 9 - js/src/jit-test/tests/parallelarray/get-6.js | 13 + .../tests/parallelarray/get-throws.js | 14 - .../jit-test/tests/parallelarray/holes-1.js | 20 +- .../jit-test/tests/parallelarray/holes-2.js | 3 +- .../jit-test/tests/parallelarray/index-1.js | 8 + .../jit-test/tests/parallelarray/index-2.js | 17 + .../jit-test/tests/parallelarray/index-3.js | 24 + .../jit-test/tests/parallelarray/index-4.js | 26 + .../jit-test/tests/parallelarray/length-3.js | 3 +- .../tests/parallelarray/mandelbrot.js | 49 + js/src/jit-test/tests/parallelarray/map-1.js | 10 - js/src/jit-test/tests/parallelarray/map-2.js | 2 +- js/src/jit-test/tests/parallelarray/map-3.js | 2 +- .../parallelarray/map-add-from-upvar-field.js | 12 + .../tests/parallelarray/map-factorial.js | 9 + .../jit-test/tests/parallelarray/map-inc.js | 3 + .../map-invalid-mode-specifier.js | 16 + .../tests/parallelarray/map-nested.js | 17 + .../map-parallel-assign-to-def-prop.js | 9 + .../jit-test/tests/parallelarray/map-short.js | 11 + .../parallelarray/nested-sum-each-row.js | 20 + .../tests/parallelarray/partition-1.js | 4 +- .../jit-test/tests/parallelarray/reduce-1.js | 8 - .../jit-test/tests/parallelarray/reduce-2.js | 8 - .../tests/parallelarray/reduce-bail.js | 31 + .../tests/parallelarray/reduce-fn-args.js | 10 +- .../{reduce-3.js => reduce-higher-dim.js} | 0 .../tests/parallelarray/reduce-length-one.js | 9 + .../tests/parallelarray/reduce-mul-short.js | 26 + .../tests/parallelarray/reduce-mul.js | 22 + .../tests/parallelarray/reduce-sum.js | 8 + .../tests/parallelarray/reduce-throws.js | 3 +- js/src/jit-test/tests/parallelarray/scan-1.js | 16 +- js/src/jit-test/tests/parallelarray/scan-2.js | 6 +- js/src/jit-test/tests/parallelarray/scan-3.js | 3 +- .../tests/parallelarray/scan-fn-args.js | 1 - .../tests/parallelarray/scan-sorted.js | 37 + .../tests/parallelarray/scan-throws.js | 4 +- .../tests/parallelarray/scatter-10.js | 28 + .../tests/parallelarray/scatter-11.js | 28 + .../tests/parallelarray/scatter-12.js | 27 + .../tests/parallelarray/scatter-13.js | 34 + .../jit-test/tests/parallelarray/scatter-7.js | 4 +- .../jit-test/tests/parallelarray/scatter-8.js | 4 +- .../jit-test/tests/parallelarray/scatter-9.js | 2 + .../parallelarray/scatter-regression-1.js | 17 + .../tests/parallelarray/scatter-throws.js | 3 +- .../jit-test/tests/parallelarray/shape-3.js | 16 + .../jit-test/tests/parallelarray/shape-4.js | 3 +- .../jit-test/tests/parallelarray/shape-5.js | 13 + .../tests/parallelarray/stack-overflow.js | 17 + .../tests/parallelarray/strict-equals-1.js | 13 + .../tests/parallelarray/surfaces-1.js | 73 +- .../tests/parallelarray/surfaces-2.js | 11 +- .../tests/parallelarray/surfaces-3.js | 3 +- .../tests/parallelarray/throw-executed.js | 17 + .../parallelarray/throw-never-executed.js | 9 + .../tests/parallelarray/timeout-gc.js | 23 + .../jit-test/tests/parallelarray/timeout.js | 14 + .../tests/parallelarray/write-array.js | 17 + .../parallelarray/write-illegal-array-elt.js | 16 + .../tests/parallelarray/write-illegal-obj.js | 33 + .../jit-test/tests/parallelarray/write-obj.js | 17 + js/src/js.msg | 6 +- js/src/jscntxt.h | 1 + js/src/jsinferinlines.h | 4 + js/src/jsiter.cpp | 10 - js/src/parjs-benchmarks/README.txt | 18 + js/src/parjs-benchmarks/allocator.js | 30 + js/src/parjs-benchmarks/mandelbrot.js | 61 + js/src/parjs-benchmarks/run.sh | 27 + js/src/parjs-benchmarks/util.js | 155 ++ js/src/vm/SelfHosting.cpp | 22 + 139 files changed, 3622 insertions(+), 2892 deletions(-) delete mode 100644 js/src/builtin/ParallelArray-inl.h create mode 100644 js/src/builtin/ParallelArray.js create mode 100644 js/src/jit-test/tests/parallelarray/alloc-array.js create mode 100644 js/src/jit-test/tests/parallelarray/alloc-different-objs.js create mode 100644 js/src/jit-test/tests/parallelarray/alloc-many-objs.js create mode 100644 js/src/jit-test/tests/parallelarray/alloc-obj.js create mode 100644 js/src/jit-test/tests/parallelarray/alloc-too-many-objs.js create mode 100644 js/src/jit-test/tests/parallelarray/bailout-executed.js create mode 100644 js/src/jit-test/tests/parallelarray/bailout-never-executed.js create mode 100644 js/src/jit-test/tests/parallelarray/closure-1.js create mode 100644 js/src/jit-test/tests/parallelarray/closure-2.js create mode 100644 js/src/jit-test/tests/parallelarray/closure-3.js create mode 100644 js/src/jit-test/tests/parallelarray/closure-4.js create mode 100644 js/src/jit-test/tests/parallelarray/closure-5.js create mode 100644 js/src/jit-test/tests/parallelarray/closure-6.js create mode 100644 js/src/jit-test/tests/parallelarray/closure-7.js create mode 100644 js/src/jit-test/tests/parallelarray/closure-8.js create mode 100644 js/src/jit-test/tests/parallelarray/compare-strings-eq.js create mode 100644 js/src/jit-test/tests/parallelarray/compare-strings-ne.js create mode 100644 js/src/jit-test/tests/parallelarray/comprehension-in-loop.js create mode 100644 js/src/jit-test/tests/parallelarray/comprehension-nested.js delete mode 100644 js/src/jit-test/tests/parallelarray/filter-1.js delete mode 100644 js/src/jit-test/tests/parallelarray/filter-2.js delete mode 100644 js/src/jit-test/tests/parallelarray/filter-3.js delete mode 100644 js/src/jit-test/tests/parallelarray/filter-4.js create mode 100644 js/src/jit-test/tests/parallelarray/filter-all.js create mode 100644 js/src/jit-test/tests/parallelarray/filter-every-third-element.js create mode 100644 js/src/jit-test/tests/parallelarray/filter-non-divisible.js create mode 100644 js/src/jit-test/tests/parallelarray/filter-none.js delete mode 100644 js/src/jit-test/tests/parallelarray/filter-throws.js create mode 100644 js/src/jit-test/tests/parallelarray/filter-truthy.js create mode 100644 js/src/jit-test/tests/parallelarray/filter-very-few.js create mode 100644 js/src/jit-test/tests/parallelarray/flatten-3.js delete mode 100644 js/src/jit-test/tests/parallelarray/get-5.js create mode 100644 js/src/jit-test/tests/parallelarray/get-6.js delete mode 100644 js/src/jit-test/tests/parallelarray/get-throws.js create mode 100644 js/src/jit-test/tests/parallelarray/index-1.js create mode 100644 js/src/jit-test/tests/parallelarray/index-2.js create mode 100644 js/src/jit-test/tests/parallelarray/index-3.js create mode 100644 js/src/jit-test/tests/parallelarray/index-4.js create mode 100644 js/src/jit-test/tests/parallelarray/mandelbrot.js delete mode 100644 js/src/jit-test/tests/parallelarray/map-1.js create mode 100644 js/src/jit-test/tests/parallelarray/map-add-from-upvar-field.js create mode 100644 js/src/jit-test/tests/parallelarray/map-factorial.js create mode 100644 js/src/jit-test/tests/parallelarray/map-inc.js create mode 100644 js/src/jit-test/tests/parallelarray/map-invalid-mode-specifier.js create mode 100644 js/src/jit-test/tests/parallelarray/map-nested.js create mode 100644 js/src/jit-test/tests/parallelarray/map-parallel-assign-to-def-prop.js create mode 100644 js/src/jit-test/tests/parallelarray/map-short.js create mode 100644 js/src/jit-test/tests/parallelarray/nested-sum-each-row.js delete mode 100644 js/src/jit-test/tests/parallelarray/reduce-1.js delete mode 100644 js/src/jit-test/tests/parallelarray/reduce-2.js create mode 100644 js/src/jit-test/tests/parallelarray/reduce-bail.js rename js/src/jit-test/tests/parallelarray/{reduce-3.js => reduce-higher-dim.js} (100%) create mode 100644 js/src/jit-test/tests/parallelarray/reduce-length-one.js create mode 100644 js/src/jit-test/tests/parallelarray/reduce-mul-short.js create mode 100644 js/src/jit-test/tests/parallelarray/reduce-mul.js create mode 100644 js/src/jit-test/tests/parallelarray/reduce-sum.js create mode 100644 js/src/jit-test/tests/parallelarray/scan-sorted.js create mode 100644 js/src/jit-test/tests/parallelarray/scatter-10.js create mode 100644 js/src/jit-test/tests/parallelarray/scatter-11.js create mode 100644 js/src/jit-test/tests/parallelarray/scatter-12.js create mode 100644 js/src/jit-test/tests/parallelarray/scatter-13.js create mode 100644 js/src/jit-test/tests/parallelarray/scatter-regression-1.js create mode 100644 js/src/jit-test/tests/parallelarray/shape-3.js create mode 100644 js/src/jit-test/tests/parallelarray/shape-5.js create mode 100644 js/src/jit-test/tests/parallelarray/stack-overflow.js create mode 100644 js/src/jit-test/tests/parallelarray/strict-equals-1.js create mode 100644 js/src/jit-test/tests/parallelarray/throw-executed.js create mode 100644 js/src/jit-test/tests/parallelarray/throw-never-executed.js create mode 100644 js/src/jit-test/tests/parallelarray/timeout-gc.js create mode 100644 js/src/jit-test/tests/parallelarray/timeout.js create mode 100644 js/src/jit-test/tests/parallelarray/write-array.js create mode 100644 js/src/jit-test/tests/parallelarray/write-illegal-array-elt.js create mode 100644 js/src/jit-test/tests/parallelarray/write-illegal-obj.js create mode 100644 js/src/jit-test/tests/parallelarray/write-obj.js create mode 100644 js/src/parjs-benchmarks/README.txt create mode 100644 js/src/parjs-benchmarks/allocator.js create mode 100644 js/src/parjs-benchmarks/mandelbrot.js create mode 100755 js/src/parjs-benchmarks/run.sh create mode 100644 js/src/parjs-benchmarks/util.js diff --git a/js/src/Makefile.in b/js/src/Makefile.in index e4ec55c595c..62fce59b06c 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -958,6 +958,7 @@ selfhosting_srcs := \ $(srcdir)/builtin/Array.js \ $(srcdir)/builtin/Intl.js \ $(srcdir)/builtin/IntlData.js \ + $(srcdir)/builtin/ParallelArray.js \ $(NULL) selfhosted_out_h_deps := \ diff --git a/js/src/builtin/ParallelArray-inl.h b/js/src/builtin/ParallelArray-inl.h deleted file mode 100644 index 1e93166d11d..00000000000 --- a/js/src/builtin/ParallelArray-inl.h +++ /dev/null @@ -1,211 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef ParallelArray_inl_h__ -#define ParallelArray_inl_h__ - -#include "builtin/ParallelArray.h" - -#include "jsobjinlines.h" - -namespace js { - -inline bool -ParallelArrayObject::IndexInfo::inBounds() const -{ - JS_ASSERT(isInitialized()); - JS_ASSERT(indices.length() <= dimensions.length()); - - for (uint32_t d = 0; d < indices.length(); d++) { - if (indices[d] >= dimensions[d]) - return false; - } - - return true; -} - -inline bool -ParallelArrayObject::IndexInfo::bump() -{ - JS_ASSERT(isInitialized()); - JS_ASSERT(indices.length() > 0); - - uint32_t d = indices.length() - 1; - while (++indices[d] == dimensions[d]) { - if (d == 0) - return false; - indices[d--] = 0; - } - - return true; -} - -inline uint32_t -ParallelArrayObject::IndexInfo::scalarLengthOfDimensions() -{ - JS_ASSERT(isInitialized()); - return dimensions[0] * partialProducts[0]; -} - -inline uint32_t -ParallelArrayObject::IndexInfo::toScalar() -{ - JS_ASSERT(isInitialized()); - JS_ASSERT(indices.length() <= partialProducts.length()); - - if (indices.length() == 0) - return 0; - if (dimensions.length() == 1) - return indices[0]; - - uint32_t index = indices[0] * partialProducts[0]; - for (uint32_t i = 1; i < indices.length(); i++) - index += indices[i] * partialProducts[i]; - return index; -} - -inline bool -ParallelArrayObject::IndexInfo::fromScalar(uint32_t index) -{ - JS_ASSERT(isInitialized()); - if (!indices.resize(partialProducts.length())) - return false; - - if (dimensions.length() == 1) { - indices[0] = index; - return true; - } - - uint32_t prev = index; - uint32_t d; - for (d = 0; d < partialProducts.length() - 1; d++) { - indices[d] = prev / partialProducts[d]; - prev = prev % partialProducts[d]; - } - indices[d] = prev; - - return true; -} - -inline bool -ParallelArrayObject::IndexInfo::initialize(uint32_t space) -{ - // Initialize using a manually set dimension vector. - JS_ASSERT(dimensions.length() > 0); - JS_ASSERT(space <= dimensions.length()); - - // Compute the partial products of the dimensions. - // - // NB: partialProducts[i] is the scalar length of dimension i. The scalar - // length of the entire space is thus dimensions[0] * partialProducts[0]. - uint32_t ndims = dimensions.length(); - if (!partialProducts.resize(ndims)) - return false; - partialProducts[ndims - 1] = 1; - for (uint32_t i = ndims - 1; i > 0; i--) - partialProducts[i - 1] = dimensions[i] * partialProducts[i]; - - // Reserve indices. - return indices.reserve(ndims) && indices.resize(space); -} - -inline bool -ParallelArrayObject::IndexInfo::initialize(JSContext *cx, HandleParallelArrayObject source, - uint32_t space) -{ - // Initialize using a dimension vector gotten from a parallel array - // source. - if (!source->getDimensions(cx, dimensions)) - return false; - - return initialize(space); -} - -inline bool -ParallelArrayObject::DenseArrayToIndexVector(JSContext *cx, HandleObject obj, - IndexVector &indices) -{ - uint32_t length = obj->getDenseInitializedLength(); - if (!indices.resize(length)) - return false; - - // Read the index vector out of the dense array into an actual Vector for - // ease of access. We're guaranteed that the elements of the dense array - // are uint32s, so just cast. - const Value *src = obj->getDenseElements(); - const Value *end = src + length; - for (uint32_t *dst = indices.begin(); src < end; dst++, src++) - *dst = static_cast(src->toInt32()); - - return true; -} - -inline bool -ParallelArrayObject::is(const Value &v) -{ - return v.isObject() && is(&v.toObject()); -} - -inline bool -ParallelArrayObject::is(JSObject *obj) -{ - return obj->hasClass(&class_); -} - -inline ParallelArrayObject * -ParallelArrayObject::as(JSObject *obj) -{ - JS_ASSERT(is(obj)); - return static_cast(obj); -} - -inline JSObject * -ParallelArrayObject::dimensionArray() -{ - JSObject &dimObj = getSlot(SLOT_DIMENSIONS).toObject(); - JS_ASSERT(dimObj.isArray()); - return &dimObj; -} - -inline JSObject * -ParallelArrayObject::buffer() -{ - JSObject &buf = getSlot(SLOT_BUFFER).toObject(); - JS_ASSERT(buf.isArray()); - return &buf; -} - -inline uint32_t -ParallelArrayObject::bufferOffset() -{ - return static_cast(getSlot(SLOT_BUFFER_OFFSET).toInt32()); -} - -inline uint32_t -ParallelArrayObject::outermostDimension() -{ - return static_cast(dimensionArray()->getDenseElement(0).toInt32()); -} - -inline bool -ParallelArrayObject::isOneDimensional() -{ - return dimensionArray()->getDenseInitializedLength() == 1; -} - -inline bool -ParallelArrayObject::getDimensions(JSContext *cx, IndexVector &dims) -{ - RootedObject obj(cx, dimensionArray()); - if (!obj) - return false; - return DenseArrayToIndexVector(cx, obj, dims); -} - -} // namespace js - -#endif // ParallelArray_inl_h__ diff --git a/js/src/builtin/ParallelArray.cpp b/js/src/builtin/ParallelArray.cpp index 410e215baf2..daa1656609e 100644 --- a/js/src/builtin/ParallelArray.cpp +++ b/js/src/builtin/ParallelArray.cpp @@ -5,911 +5,47 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "builtin/ParallelArray.h" -#include "builtin/ParallelArray-inl.h" - #include "jsapi.h" #include "jsobj.h" #include "jsarray.h" -#include "jsprf.h" -#include "gc/Marking.h" +#include "builtin/ParallelArray.h" + +#include "vm/ForkJoin.h" #include "vm/GlobalObject.h" -#include "vm/Stack.h" -#include "vm/StringBuffer.h" +#include "vm/String.h" +#include "vm/ThreadPool.h" +#include "jsinterpinlines.h" #include "jsobjinlines.h" using namespace js; -using namespace js::types; - -// -// Utilities -// - -typedef ParallelArrayObject::IndexVector IndexVector; -typedef ParallelArrayObject::IndexInfo IndexInfo; - -static bool -ReportMoreArgsNeeded(JSContext *cx, const char *name, const char *num, const char *p) -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED, name, num, p); - return false; -} - -static bool -ReportBadArg(JSContext *cx, const char *s = "") -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_BAD_ARG, s); - return false; -} - -static bool -ReportBadLength(JSContext *cx) -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); - return false; -} - -static bool -ReportBadLengthOrArg(JSContext *cx, HandleValue v, const char *s = "") -{ - return v.isNumber() ? ReportBadLength(cx) : ReportBadArg(cx, s); -} - -static bool -ReportBadPartition(JSContext *cx) -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_BAD_PARTITION); - return false; -} - -bool -ParallelArrayObject::IndexInfo::isInitialized() const -{ - return (dimensions.length() > 0 && - indices.capacity() >= dimensions.length() && - partialProducts.length() == dimensions.length()); -} - -static inline bool -OpenDelimiters(const IndexInfo &iv, StringBuffer &sb) -{ - JS_ASSERT(iv.isInitialized()); - - uint32_t d = iv.dimensions.length() - 1; - do { - if (iv.indices[d] != 0) - break; - if (!sb.append('<')) - return false; - } while (d-- > 0); - - return true; -} - -static inline bool -CloseDelimiters(const IndexInfo &iv, StringBuffer &sb) -{ - JS_ASSERT(iv.isInitialized()); - - uint32_t d = iv.dimensions.length() - 1; - do { - if (iv.indices[d] != iv.dimensions[d] - 1) { - if (!sb.append(',')) - return false; - break; - } - - if (!sb.append('>')) - return false; - } while (d-- > 0); - - return true; -} - -// A version of ToUint32 that reports if the input value is malformed: either -// it is given to us as a negative integer or it overflows. -static bool -ToUint32(JSContext *cx, const Value &v, uint32_t *out, bool *malformed) -{ - AssertArgumentsAreSane(cx, v); - { - js::SkipRoot skip(cx, &v); - js::MaybeCheckStackRoots(cx); - } - - *malformed = false; - - if (v.isInt32()) { - int32_t i = v.toInt32(); - if (i < 0) { - *malformed = true; - return true; - } - *out = static_cast(i); - return true; - } - - double d; - if (v.isDouble()) { - d = v.toDouble(); - } else { - if (!ToNumberSlow(cx, v, &d)) - return false; - } - - *out = ToUint32(d); - - if (!MOZ_DOUBLE_IS_FINITE(d) || d != static_cast(*out)) { - *malformed = true; - return true; - } - - return true; -} - -static bool -GetLength(JSContext *cx, HandleObject obj, uint32_t *length) -{ - // If obj's length cannot overflow, just use GetLengthProperty. - if (obj->isArray() || obj->isArguments()) - return GetLengthProperty(cx, obj, length); - - // Otherwise check that we don't overflow uint32_t. - RootedValue value(cx); - if (!JSObject::getProperty(cx, obj, obj, cx->names().length, &value)) - return false; - - bool malformed; - if (!ToUint32(cx, value, length, &malformed)) - return false; - if (malformed) - return ReportBadLengthOrArg(cx, value); - - return true; -} - -// Check if obj is a parallel array, and if so, cast to pa and initialize -// the IndexInfo accordingly. -// -// This function is designed to be used in conjunction with -// GetElementFromArrayLikeObject; see below. -static bool -MaybeGetParallelArrayObjectAndLength(JSContext *cx, HandleObject obj, - MutableHandle pa, - IndexInfo *iv, uint32_t *length) -{ - if (ParallelArrayObject::is(obj)) { - pa.set(ParallelArrayObject::as(obj)); - if (!pa->isOneDimensional() && !iv->initialize(cx, pa, 1)) - return false; - *length = pa->outermostDimension(); - } else if (!GetLength(cx, obj, length)) { - return false; - } - - return true; -} - -// Store the i-th element of the array-like object obj into vp. -// -// If pa is not null, then pa is obj casted to a ParallelArrayObject -// and iv is initialized according to the dimensions of pa. In this case, -// we get the element using the ParallelArrayObject. -// -// Otherwise we do what is done in GetElement in jsarray.cpp. -static bool -GetElementFromArrayLikeObject(JSContext *cx, HandleObject obj, HandleParallelArrayObject pa, - IndexInfo &iv, uint32_t i, MutableHandleValue vp) -{ - // Fast path getting an element from parallel and dense arrays. For dense - // arrays, we only do this if the prototype doesn't have indexed - // properties. In this case holes = undefined. - if (pa && pa->getParallelArrayElement(cx, i, &iv, vp)) - return true; - - if (obj->isArray() && i < obj->getDenseInitializedLength() && - !ObjectMayHaveExtraIndexedProperties(obj)) - { - vp.set(obj->getDenseElement(i)); - if (vp.isMagic(JS_ELEMENTS_HOLE)) - vp.setUndefined(); - return true; - } - - if (obj->isArguments()) { - if (obj->asArguments().maybeGetElement(static_cast(i), vp)) - return true; - } - - // Slow path everything else: objects with indexed properties on the - // prototype, non-parallel and dense arrays. - return JSObject::getElement(cx, obj, obj, i, vp); -} - -static inline bool -SetArrayNewType(JSContext *cx, HandleObject obj) -{ - RootedTypeObject newtype(cx, GetTypeCallerInitObject(cx, JSProto_Array)); - if (!newtype) - return false; - obj->setType(newtype); - return true; -} - -static JSObject * -NewDenseCopiedArrayWithType(JSContext *cx, uint32_t length, HandleObject source) -{ - JS_ASSERT(source); - - RootedObject buffer(cx, NewDenseAllocatedArray(cx, length)); - if (!buffer) - return NULL; - JS_ASSERT(buffer->getDenseCapacity() >= length); - buffer->setDenseInitializedLength(length); - - uint32_t srclen; - uint32_t copyUpTo; - - if (source->isArray() && !ObjectMayHaveExtraIndexedProperties(source)) { - // Optimize for the common case: if we have a dense array source, copy - // whatever we can, truncating to length. This path doesn't trigger - // GC, so we don't need to initialize all the array's slots before - // copying. - const Value *srcvp = source->getDenseElements(); - - srclen = source->getDenseInitializedLength(); - copyUpTo = Min(length, srclen); - - // Convert any existing holes into undefined. - Value elem; - for (uint32_t i = 0; i < copyUpTo; i++) { - elem = srcvp[i].isMagic(JS_ELEMENTS_HOLE) ? UndefinedValue() : srcvp[i]; - JSObject::initDenseElementWithType(cx, buffer, i, elem); - } - - // Fill the rest with undefineds. - for (uint32_t i = copyUpTo; i < length; i++) - JSObject::initDenseElementWithType(cx, buffer, i, UndefinedValue()); - } else { - // This path might GC. The GC expects an object's slots to be - // initialized, so we have to make sure all the array's slots are - // initialized. - for (uint32_t i = 0; i < length; i++) - JSObject::initDenseElementWithType(cx, buffer, i, UndefinedValue()); - - IndexInfo siv(cx); - RootedParallelArrayObject sourcePA(cx); - - if (!MaybeGetParallelArrayObjectAndLength(cx, source, &sourcePA, &siv, &srclen)) - return NULL; - copyUpTo = Min(length, srclen); - - // Copy elements pointwise. - RootedValue elem(cx); - for (uint32_t i = 0; i < copyUpTo; i++) { - if (!GetElementFromArrayLikeObject(cx, source, sourcePA, siv, i, &elem)) - return NULL; - JSObject::setDenseElementWithType(cx, buffer, i, elem); - } - } - - if (!SetArrayNewType(cx, buffer)) - return NULL; - - return *buffer.address(); -} - -static inline JSObject * -NewDenseArrayWithType(JSContext *cx, uint32_t length) -{ - RootedObject buffer(cx, NewDenseAllocatedArray(cx, length)); - if (!buffer) - return NULL; - - buffer->ensureDenseInitializedLength(cx, length, 0); - - if (!SetArrayNewType(cx, buffer)) - return NULL; - - return *buffer.address(); -} - -// Copy an array like object obj into an IndexVector, indices, using -// ToUint32. -static inline bool -ArrayLikeToIndexVector(JSContext *cx, HandleObject obj, IndexVector &indices, - bool *malformed) -{ - IndexInfo iv(cx); - RootedParallelArrayObject pa(cx); - uint32_t length; - - *malformed = false; - - if (!MaybeGetParallelArrayObjectAndLength(cx, obj, &pa, &iv, &length)) - return false; - - if (!indices.resize(length)) - return false; - - RootedValue elem(cx); - bool malformed_; - for (uint32_t i = 0; i < length; i++) { - if (!GetElementFromArrayLikeObject(cx, obj, pa, iv, i, &elem) || - !ToUint32(cx, elem, &indices[i], &malformed_)) - { - return false; - } - - if (malformed_) - *malformed = true; - } - - return true; -} - -static inline bool -IdIsInBoundsIndex(JSContext *cx, HandleObject obj, HandleId id) -{ - uint32_t i; - return js_IdIsIndex(id, &i) && i < ParallelArrayObject::as(obj)->outermostDimension(); -} - -template -static inline -JSBool NonGenericMethod(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, ParallelArrayObject::is, impl, args); -} - -// -// Operations Overview -// -// The different execution modes implement different versions of a set of -// operations with the same signatures, detailed below. -// -// build -// ----- -// The comprehension form. Build a parallel array from a dimension vector and -// using elementalFun, writing the results into buffer. The dimension vector -// and its partial products are kept in iv. The function elementalFun is passed -// indices as multiple arguments. -// -// bool build(JSContext *cx, -// IndexInfo &iv, -// HandleObject elementalFun, -// HandleObject buffer) -// -// map -// --- -// Map elementalFun over the elements of the outermost dimension of source, -// writing the results into buffer. The buffer must be as long as the -// outermost dimension of the source. The elementalFun is passed -// (element, index, collection) as arguments, in that order. -// -// bool map(JSContext *cx, -// HandleParallelArrayObject source, -// HandleObject elementalFun, -// HandleObject buffer) -// -// reduce -// ------ -// Reduce source in the outermost dimension using elementalFun. If vp is not -// null, then the final value of the reduction is stored into vp. If buffer is -// not null, then buffer[i] is the final value of calling reduce on the -// subarray from [0,i]. The elementalFun is passed 2 values to be -// reduced. There is no specified order in which the elements of the array are -// reduced. If elementalFun is not commutative and associative, there is no -// guarantee that the final value is deterministic. -// -// bool reduce(JSContext *cx, -// HandleParallelArrayObject source, -// HandleObject elementalFun, -// HandleObject buffer, -// MutableHandleValue vp) -// -// scatter -// ------- -// Reassign elements in source in the outermost dimension according to a -// scatter vector, targets, writing results into buffer. The targets object -// should be array-like. The element source[i] is reassigned to the index -// targets[i]. If multiple elements map to the same target index, the -// conflictFun is used to resolve the resolution. If nothing maps to i for -// some i, defaultValue is used for that index. Note that buffer can be longer -// than the source, in which case all the remaining holes are filled with -// defaultValue. -// -// bool scatter(JSContext *cx, -// HandleParallelArrayObject source, -// HandleObject targets, -// const Value &defaultValue, -// HandleObject conflictFun, -// HandleObject buffer) -// -// filter -// ------ -// Filter the source in the outermost dimension using an array of truthy -// values, filters, writing the results into buffer. All elements with index i -// in outermost dimension such that filters[i] is not truthy are removed. -// -// bool filter(JSContext *cx, -// HandleParallelArrayObject source, -// HandleObject filters, -// HandleObject buffer) -// - -ParallelArrayObject::SequentialMode ParallelArrayObject::sequential; -ParallelArrayObject::ParallelMode ParallelArrayObject::parallel; -ParallelArrayObject::FallbackMode ParallelArrayObject::fallback; - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::SequentialMode::build(JSContext *cx, IndexInfo &iv, - HandleObject elementalFun, HandleObject buffer) -{ - JS_ASSERT(iv.isInitialized()); - - uint32_t length = iv.scalarLengthOfDimensions(); - - InvokeArgsGuard args; - if (!cx->stack.pushInvokeArgs(cx, iv.dimensions.length(), &args)) - return ExecutionFailed; - - for (uint32_t i = 0; i < length; i++, iv.bump()) { - args.setCallee(ObjectValue(*elementalFun)); - args.setThis(UndefinedValue()); - - // Compute and set indices. - for (size_t j = 0; j < iv.indices.length(); j++) - args[j].setNumber(iv.indices[j]); - - if (!Invoke(cx, args)) - return ExecutionFailed; - - JSObject::setDenseElementWithType(cx, buffer, i, args.rval()); - } - - return ExecutionSucceeded; -} - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::SequentialMode::map(JSContext *cx, HandleParallelArrayObject source, - HandleObject elementalFun, HandleObject buffer) -{ - JS_ASSERT(is(source)); - JS_ASSERT(source->outermostDimension() == buffer->getDenseInitializedLength()); - JS_ASSERT(buffer->isArray()); - - uint32_t length = source->outermostDimension(); - - IndexInfo iv(cx); - if (!source->isOneDimensional() && !iv.initialize(cx, source, 1)) - return ExecutionFailed; - - InvokeArgsGuard args; - if (!cx->stack.pushInvokeArgs(cx, 3, &args)) - return ExecutionFailed; - - RootedValue elem(cx); - for (uint32_t i = 0; i < length; i++) { - args.setCallee(ObjectValue(*elementalFun)); - args.setThis(UndefinedValue()); - - if (!source->getParallelArrayElement(cx, i, &iv, &elem)) - return ExecutionFailed; - - // The arguments are in eic(h) order. - args[0] = elem; - args[1].setNumber(i); - args[2].setObject(*source); - - if (!Invoke(cx, args)) - return ExecutionFailed; - - JSObject::setDenseElementWithType(cx, buffer, i, args.rval()); - } - - return ExecutionSucceeded; -} - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::SequentialMode::reduce(JSContext *cx, HandleParallelArrayObject source, - HandleObject elementalFun, HandleObject buffer, - MutableHandleValue vp) -{ - JS_ASSERT(is(source)); - JS_ASSERT_IF(buffer, buffer->isArray()); - JS_ASSERT_IF(buffer, buffer->getDenseInitializedLength() >= 1); - - uint32_t length = source->outermostDimension(); - - // The accumulator: the objet petit a. - // - // "A VM's accumulator register is Objet petit a: the unattainable object - // of desire that sets in motion the symbolic movement of interpretation." - // -- PLT Žižek - RootedValue acc(cx); - IndexInfo iv(cx); - - if (!source->isOneDimensional() && !iv.initialize(cx, source, 1)) - return ExecutionFailed; - - if (!source->getParallelArrayElement(cx, 0, &iv, &acc)) - return ExecutionFailed; - - if (buffer) - JSObject::setDenseElementWithType(cx, buffer, 0, acc); - - InvokeArgsGuard args; - if (!cx->stack.pushInvokeArgs(cx, 2, &args)) - return ExecutionFailed; - - RootedValue elem(cx); - for (uint32_t i = 1; i < length; i++) { - args.setCallee(ObjectValue(*elementalFun)); - args.setThis(UndefinedValue()); - - if (!source->getParallelArrayElement(cx, i, &iv, &elem)) - return ExecutionFailed; - - // Set the two arguments to the elemental function. - args[0] = acc; - args[1] = elem; - - if (!Invoke(cx, args)) - return ExecutionFailed; - - // Update the accumulator. - acc = args.rval(); - if (buffer) - JSObject::setDenseElementWithType(cx, buffer, i, args.rval()); - } - - vp.set(acc); - - return ExecutionSucceeded; -} - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::SequentialMode::scatter(JSContext *cx, HandleParallelArrayObject source, - HandleObject targets, const Value &defaultValue, - HandleObject conflictFun, HandleObject buffer) -{ - JS_ASSERT(buffer->isArray()); - - uint32_t length = buffer->getDenseInitializedLength(); - - IndexInfo iv(cx); - if (!source->isOneDimensional() && !iv.initialize(cx, source, 1)) - return ExecutionFailed; - - // Index vector and parallel array pointer for targets, in case targets is - // a ParallelArray object. If not, these are uninitialized. - IndexInfo tiv(cx); - RootedParallelArrayObject targetsPA(cx); - - // The length of the scatter vector. - uint32_t targetsLength; - if (!MaybeGetParallelArrayObjectAndLength(cx, targets, &targetsPA, &tiv, &targetsLength)) - return ExecutionFailed; - - // Iterate over the scatter vector, but not more than the length of the - // source array. - RootedValue elem(cx); - RootedValue telem(cx); - RootedValue targetElem(cx); - for (uint32_t i = 0; i < Min(targetsLength, source->outermostDimension()); i++) { - uint32_t targetIndex; - bool malformed; - - if (!GetElementFromArrayLikeObject(cx, targets, targetsPA, tiv, i, &telem) || - !ToUint32(cx, telem, &targetIndex, &malformed)) - { - return ExecutionFailed; - } - - if (malformed) { - ReportBadArg(cx, ".prototype.scatter"); - return ExecutionFailed; - } - - if (targetIndex >= length) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_SCATTER_BOUNDS); - return ExecutionFailed; - } - - if (!source->getParallelArrayElement(cx, i, &iv, &elem)) - return ExecutionFailed; - - targetElem = buffer->getDenseElement(targetIndex); - - // We initialized the dense buffer with holes. If the target element - // in the source array is not a hole, that means we have set it - // already and we have a conflict. - if (!targetElem.isMagic(JS_ELEMENTS_HOLE)) { - if (conflictFun) { - InvokeArgsGuard args; - if (!cx->stack.pushInvokeArgs(cx, 2, &args)) - return ExecutionFailed; - - args.setCallee(ObjectValue(*conflictFun)); - args.setThis(UndefinedValue()); - args[0] = elem; - args[1] = targetElem; - - if (!Invoke(cx, args)) - return ExecutionFailed; - - elem = args.rval(); - } else { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_PAR_ARRAY_SCATTER_CONFLICT); - return ExecutionFailed; - } - } - - JSObject::setDenseElementWithType(cx, buffer, targetIndex, elem); - } - - // Fill holes with the default value. - for (uint32_t i = 0; i < length; i++) { - if (buffer->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) - JSObject::setDenseElementWithType(cx, buffer, i, defaultValue); - } - - return ExecutionSucceeded; -} - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::SequentialMode::filter(JSContext *cx, HandleParallelArrayObject source, - HandleObject filters, HandleObject buffer) -{ - JS_ASSERT(buffer->isArray()); - - IndexInfo iv(cx); - if (!source->isOneDimensional() && !iv.initialize(cx, source, 1)) - return ExecutionFailed; - - // Index vector and parallel array pointer for filters, in case filters is - // a ParallelArray object. If not, these are uninitialized. - IndexInfo fiv(cx); - RootedParallelArrayObject filtersPA(cx); - - // The length of the filter array. - uint32_t filtersLength; - - if (!MaybeGetParallelArrayObjectAndLength(cx, filters, &filtersPA, &fiv, &filtersLength)) - return ExecutionFailed; - - RootedValue elem(cx); - RootedValue felem(cx); - for (uint32_t i = 0, pos = 0; i < filtersLength; i++) { - if (!GetElementFromArrayLikeObject(cx, filters, filtersPA, fiv, i, &felem)) - return ExecutionFailed; - - // Skip if the filter element isn't truthy. - if (!ToBoolean(felem)) - continue; - - if (!source->getParallelArrayElement(cx, i, &iv, &elem)) - return ExecutionFailed; - - // Set the element on the buffer. If we couldn't stay dense, fail. - JSObject::EnsureDenseResult result = JSObject::ED_SPARSE; - result = buffer->ensureDenseElements(cx, pos, 1); - if (result != JSObject::ED_OK) - return ExecutionFailed; - if (i >= buffer->getArrayLength()) - buffer->setArrayLengthInt32(pos + 1); - JSObject::setDenseElementWithType(cx, buffer, pos, elem); - - // We didn't filter this element out, so bump the position. - pos++; - } - - return ExecutionSucceeded; -} - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::ParallelMode::build(JSContext *cx, IndexInfo &iv, - HandleObject elementalFun, HandleObject buffer) -{ - return ExecutionFailed; -} - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::ParallelMode::map(JSContext *cx, HandleParallelArrayObject source, - HandleObject elementalFun, HandleObject buffer) -{ - return ExecutionFailed; -} - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::ParallelMode::reduce(JSContext *cx, HandleParallelArrayObject source, - HandleObject elementalFun, HandleObject buffer, - MutableHandleValue vp) -{ - return ExecutionFailed; -} - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::ParallelMode::scatter(JSContext *cx, HandleParallelArrayObject source, - HandleObject targetsObj, const Value &defaultValue, - HandleObject conflictFun, HandleObject buffer) -{ - return ExecutionFailed; -} - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::ParallelMode::filter(JSContext *cx, HandleParallelArrayObject source, - HandleObject filtersObj, HandleObject buffer) -{ - return ExecutionFailed; -} - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::FallbackMode::build(JSContext *cx, IndexInfo &iv, - HandleObject elementalFun, HandleObject buffer) -{ - if (parallel.build(cx, iv, elementalFun, buffer) || - sequential.build(cx, iv, elementalFun, buffer)) - { - return ExecutionSucceeded; - } - return ExecutionFailed; -} - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::FallbackMode::map(JSContext *cx, HandleParallelArrayObject source, - HandleObject elementalFun, HandleObject buffer) -{ - if (parallel.map(cx, source, elementalFun, buffer) || - sequential.map(cx, source, elementalFun, buffer)) - { - return ExecutionSucceeded; - } - return ExecutionFailed; -} - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::FallbackMode::reduce(JSContext *cx, HandleParallelArrayObject source, - HandleObject elementalFun, HandleObject buffer, - MutableHandleValue vp) -{ - if (parallel.reduce(cx, source, elementalFun, buffer, vp) || - sequential.reduce(cx, source, elementalFun, buffer, vp)) - { - return ExecutionSucceeded; - } - return ExecutionFailed; -} - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::FallbackMode::scatter(JSContext *cx, HandleParallelArrayObject source, - HandleObject targetsObj, const Value &defaultValue, - HandleObject conflictFun, HandleObject buffer) -{ - if (parallel.scatter(cx, source, targetsObj, defaultValue, conflictFun, buffer) || - sequential.scatter(cx, source, targetsObj, defaultValue, conflictFun, buffer)) - { - return ExecutionSucceeded; - } - return ExecutionFailed; -} - -ParallelArrayObject::ExecutionStatus -ParallelArrayObject::FallbackMode::filter(JSContext *cx, HandleParallelArrayObject source, - HandleObject filtersObj, HandleObject buffer) -{ - if (parallel.filter(cx, source, filtersObj, buffer) || - sequential.filter(cx, source, filtersObj, buffer)) - { - return ExecutionSucceeded; - } - return ExecutionFailed; -} - -#ifdef DEBUG - -const char * -ParallelArrayObject::ExecutionStatusToString(ExecutionStatus ss) -{ - switch (ss) { - case ExecutionFailed: - return "failure"; - case ExecutionCompiled: - return "compilation"; - case ExecutionSucceeded: - return "success"; - } - return "(unknown status)"; -} - -bool -ParallelArrayObject::DebugOptions::init(JSContext *cx, const Value &v) -{ - RootedObject obj(cx, NonNullObject(cx, v)); - if (!obj) - return false; - - RootedId id(cx); - RootedValue propv(cx); - JSString *propStr; - JSBool match = false; - bool ok; - - id = AtomToId(Atomize(cx, "mode", strlen("mode"))); - if (!JSObject::getGeneric(cx, obj, obj, id, &propv)) - return false; - - propStr = ToString(cx, propv); - if (!propStr) - return false; - - if ((ok = JS_StringEqualsAscii(cx, propStr, "par", &match)) && match) - mode = ∥ - else if (ok && (ok = JS_StringEqualsAscii(cx, propStr, "seq", &match)) && match) - mode = &sequential; - else if (ok) - return ReportBadArg(cx); - else - return false; - - id = AtomToId(Atomize(cx, "expect", strlen("expect"))); - if (!JSObject::getGeneric(cx, obj, obj, id, &propv)) - return false; - - propStr = ToString(cx, propv); - if (!propStr) - return false; - - if ((ok = JS_StringEqualsAscii(cx, propStr, "fail", &match)) && match) - expect = ExecutionFailed; - else if (ok && (ok = JS_StringEqualsAscii(cx, propStr, "bail", &match)) && match) - expect = ExecutionCompiled; - else if (ok && (ok = JS_StringEqualsAscii(cx, propStr, "success", &match)) && match) - expect = ExecutionSucceeded; - else if (ok) - return ReportBadArg(cx); - else - return false; - - return true; -} - -bool -ParallelArrayObject::DebugOptions::check(JSContext *cx, ExecutionStatus actual) -{ - if (expect != actual) { - JS_ReportError(cx, "expected %s for %s execution, got %s", - ExecutionStatusToString(expect), - mode->toString(), - ExecutionStatusToString(actual)); - return false; - } - - return true; -} - -#endif // DEBUG // // ParallelArrayObject // +FixedHeapPtr ParallelArrayObject::ctorNames[NumCtors]; + JSFunctionSpec ParallelArrayObject::methods[] = { - JS_FN("map", NonGenericMethod, 1, 0), - JS_FN("reduce", NonGenericMethod, 1, 0), - JS_FN("scan", NonGenericMethod, 1, 0), - JS_FN("scatter", NonGenericMethod, 1, 0), - JS_FN("filter", NonGenericMethod, 1, 0), - JS_FN("flatten", NonGenericMethod, 0, 0), - JS_FN("partition", NonGenericMethod, 1, 0), - JS_FN("get", NonGenericMethod, 1, 0), - JS_FN(js_toString_str, NonGenericMethod, 0, 0), - JS_FN(js_toLocaleString_str, NonGenericMethod, 0, 0), + { "map", JSOP_NULLWRAPPER, 2, 0, "ParallelArrayMap" }, + { "reduce", JSOP_NULLWRAPPER, 2, 0, "ParallelArrayReduce" }, + { "scan", JSOP_NULLWRAPPER, 2, 0, "ParallelArrayScan" }, + { "scatter", JSOP_NULLWRAPPER, 5, 0, "ParallelArrayScatter" }, + { "filter", JSOP_NULLWRAPPER, 2, 0, "ParallelArrayFilter" }, + { "partition", JSOP_NULLWRAPPER, 1, 0, "ParallelArrayPartition" }, + { "flatten", JSOP_NULLWRAPPER, 0, 0, "ParallelArrayFlatten" }, + + // FIXME #838906. Note that `get()` is not currently defined on this table but + // rather is assigned to each instance of ParallelArray as an own + // property. This is a bit of a hack designed to supply a + // specialized version of get() based on the dimensionality of the + // receiver. In the future we can improve this by (1) extending + // TI to track the dimensionality of the receiver and (2) using a + // hint to aggressively inline calls to get(). + // { "get", JSOP_NULLWRAPPER, 1, 0, "ParallelArrayGet" }, + + { "toString", JSOP_NULLWRAPPER, 0, 0, "ParallelArrayToString" }, JS_FS_END }; @@ -927,8 +63,6 @@ Class ParallelArrayObject::protoClass = { Class ParallelArrayObject::class_ = { "ParallelArray", - Class::NON_NATIVE | - JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_ParallelArray), JS_PropertyStub, // addProperty JS_PropertyStub, // delProperty @@ -936,53 +70,139 @@ Class ParallelArrayObject::class_ = { JS_StrictPropertyStub, // setProperty JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, - NULL, // finalize - NULL, // checkAccess - NULL, // call - NULL, // construct - NULL, // hasInstance - mark, // trace - JS_NULL_CLASS_EXT, - { - lookupGeneric, - lookupProperty, - lookupElement, - lookupSpecial, - defineGeneric, - defineProperty, - defineElement, - defineSpecial, - getGeneric, - getProperty, - getElement, - getElementIfPresent, - getSpecial, - setGeneric, - setProperty, - setElement, - setSpecial, - getGenericAttributes, - getPropertyAttributes, - getElementAttributes, - getSpecialAttributes, - setGenericAttributes, - setPropertyAttributes, - setElementAttributes, - setSpecialAttributes, - deleteProperty, - deleteElement, - deleteSpecial, - NULL, // enumerate - NULL, // thisObject - } + JS_ConvertStub }; +/*static*/ bool +ParallelArrayObject::initProps(JSContext *cx, HandleObject obj) +{ + RootedValue undef(cx, UndefinedValue()); + RootedValue zero(cx, Int32Value(0)); + + if (!JSObject::setProperty(cx, obj, obj, cx->names().buffer, &undef, true)) + return false; + if (!JSObject::setProperty(cx, obj, obj, cx->names().offset, &zero, true)) + return false; + if (!JSObject::setProperty(cx, obj, obj, cx->names().shape, &undef, true)) + return false; + if (!JSObject::setProperty(cx, obj, obj, cx->names().get, &undef, true)) + return false; + + return true; +} + +/*static*/ JSBool +ParallelArrayObject::construct(JSContext *cx, unsigned argc, Value *vp) +{ + RootedFunction ctor(cx, getConstructor(cx, argc)); + if (!ctor) + return false; + CallArgs args = CallArgsFromVp(argc, vp); + return constructHelper(cx, &ctor, args); +} + + +/* static */ JSFunction * +ParallelArrayObject::getConstructor(JSContext *cx, unsigned argc) +{ + RootedPropertyName ctorName(cx, ctorNames[js::Min(argc, NumCtors - 1)]); + RootedValue ctorValue(cx); + if (!cx->global()->getIntrinsicValue(cx, ctorName, &ctorValue)) + return NULL; + JS_ASSERT(ctorValue.isObject() && ctorValue.toObject().isFunction()); + return ctorValue.toObject().toFunction(); +} + +/*static*/ JSObject * +ParallelArrayObject::newInstance(JSContext *cx) +{ + gc::AllocKind kind = gc::GetGCObjectKind(NumFixedSlots); + RootedObject result(cx, NewBuiltinClassInstance(cx, &class_, kind)); + if (!result) + return NULL; + + // Add in the basic PA properties now with default values: + if (!initProps(cx, result)) + return NULL; + + return result; +} + +/*static*/ JSBool +ParallelArrayObject::constructHelper(JSContext *cx, MutableHandleFunction ctor, CallArgs &args0) +{ + RootedObject result(cx, newInstance(cx)); + if (!result) + return false; + + if (cx->typeInferenceEnabled()) { + jsbytecode *pc; + RootedScript script(cx, cx->stack.currentScript(&pc)); + if (script) { + if (ctor->nonLazyScript()->shouldCloneAtCallsite) { + ctor.set(CloneFunctionAtCallsite(cx, ctor, script, pc)); + if (!ctor) + return false; + } + + // Create the type object for the PA. Add in the current + // properties as definite properties if this type object is newly + // created. To tell if it is newly created, we check whether it + // has any properties yet or not, since any returned type object + // must have been created by this same C++ code and hence would + // already have properties if it had been returned before. + types::TypeObject *paTypeObject = + types::TypeScript::InitObject(cx, script, pc, JSProto_ParallelArray); + if (!paTypeObject) + return false; + if (paTypeObject->getPropertyCount() == 0) { + if (!paTypeObject->addDefiniteProperties(cx, result)) + return false; + + // addDefiniteProperties() above should have added one + // property for each of the fixed slots: + JS_ASSERT(paTypeObject->getPropertyCount() == NumFixedSlots); + } + result->setType(paTypeObject); + } + } + + InvokeArgsGuard args; + if (!cx->stack.pushInvokeArgs(cx, args0.length(), &args)) + return false; + + args.setCallee(ObjectValue(*ctor)); + args.setThis(ObjectValue(*result)); + + for (uint32_t i = 0; i < args0.length(); i++) + args[i] = args0[i]; + + if (!Invoke(cx, args)) + return false; + + args0.rval().setObject(*result); + return true; +} + JSObject * -ParallelArrayObject::initClass(JSContext *cx, JSObject *obj) +ParallelArrayObject::initClass(JSContext *cx, HandleObject obj) { JS_ASSERT(obj->isNative()); + // Cache constructor names. + { + const char *ctorStrs[NumCtors] = { "ParallelArrayConstructEmpty", + "ParallelArrayConstructFromArray", + "ParallelArrayConstructFromFunction", + "ParallelArrayConstructFromFunctionMode" }; + for (uint32_t i = 0; i < NumCtors; i++) { + JSAtom *atom = Atomize(cx, ctorStrs[i], strlen(ctorStrs[i]), InternAtom); + if (!atom) + return NULL; + ctorNames[i].init(atom->asPropertyName()); + } + } + Rooted global(cx, &obj->asGlobal()); RootedObject proto(cx, global->createBlankPrototype(cx, &protoClass)); @@ -990,8 +210,8 @@ ParallelArrayObject::initClass(JSContext *cx, JSObject *obj) return NULL; JSProtoKey key = JSProto_ParallelArray; - RootedFunction ctor(cx); - ctor = global->createConstructor(cx, construct, cx->names().ParallelArray, 0); + RootedFunction ctor(cx, global->createConstructor(cx, construct, + cx->names().ParallelArray, 0)); if (!ctor || !LinkConstructorAndPrototype(cx, ctor, proto) || !DefinePropertiesAndBrand(cx, proto, NULL, methods) || @@ -1000,1105 +220,42 @@ ParallelArrayObject::initClass(JSContext *cx, JSObject *obj) return NULL; } - // Define the length and shape properties. - RootedId lengthId(cx, AtomToId(cx->names().length)); - RootedId shapeId(cx, AtomToId(cx->names().shape)); - unsigned flags = JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_GETTER; - - RootedObject scriptedLength(cx, NewFunction(cx, NullPtr(), NonGenericMethod, - 0, JSFunction::NATIVE_FUN, global, NullPtr())); - RootedObject scriptedShape(cx, NewFunction(cx, NullPtr(), NonGenericMethod, - 0, JSFunction::NATIVE_FUN, global, NullPtr())); - - RootedValue value(cx, UndefinedValue()); - if (!scriptedLength || !scriptedShape || - !DefineNativeProperty(cx, proto, lengthId, value, - JS_DATA_TO_FUNC_PTR(PropertyOp, scriptedLength.get()), NULL, - flags, 0, 0) || - !DefineNativeProperty(cx, proto, shapeId, value, - JS_DATA_TO_FUNC_PTR(PropertyOp, scriptedShape.get()), NULL, - flags, 0, 0)) + // Define the length getter. { - return NULL; + const char lengthStr[] = "ParallelArrayLength"; + JSAtom *atom = Atomize(cx, lengthStr, strlen(lengthStr)); + if (!atom) + return NULL; + Rooted lengthProp(cx, atom->asPropertyName()); + RootedValue lengthValue(cx); + if (!cx->global()->getIntrinsicValue(cx, lengthProp, &lengthValue)) + return NULL; + RootedObject lengthGetter(cx, &lengthValue.toObject()); + if (!lengthGetter) + return NULL; + + RootedId lengthId(cx, AtomToId(cx->names().length)); + unsigned flags = JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_GETTER; + RootedValue value(cx, UndefinedValue()); + if (!DefineNativeProperty(cx, proto, lengthId, value, + JS_DATA_TO_FUNC_PTR(PropertyOp, lengthGetter.get()), NULL, + flags, 0, 0)) + { + return NULL; + } } return proto; } bool -ParallelArrayObject::getParallelArrayElement(JSContext *cx, IndexInfo &iv, MutableHandleValue vp) +ParallelArrayObject::is(const Value &v) { - JS_ASSERT(iv.isInitialized()); - - // How many indices we have determine what dimension we are indexing. For - // example, if we have 2 indices [n,m], we are indexing something on the - // 2nd dimension. - uint32_t d = iv.indices.length(); - uint32_t ndims = iv.dimensions.length(); - JS_ASSERT(d <= ndims); - - uint32_t base = bufferOffset(); - uint32_t end = base + iv.scalarLengthOfDimensions(); - - // If we are provided an index vector with every dimension specified, we - // are indexing a leaf. Leaves are always value, so just return them. - if (d == ndims) { - uint32_t index = base + iv.toScalar(); - if (index >= end) - vp.setUndefined(); - else - vp.set(buffer()->getDenseElement(index)); - return true; - } - - // If we aren't indexing a leaf value, we should return a new - // ParallelArray of lesser dimensionality. Here we create a new 'view' on - // the underlying buffer, though whether a ParallelArray is a view or a - // copy is not observable by the user. - // - // It is not enough to compute the scalar index and check bounds that way, - // since the row length can be 0. - if (!iv.inBounds()) { - vp.setUndefined(); - return true; - } - - RootedObject buf(cx, buffer()); - IndexVector newDims(cx); - return (newDims.append(iv.dimensions.begin() + d, iv.dimensions.end()) && - create(cx, buf, base + iv.toScalar(), newDims, vp)); -} - -bool -ParallelArrayObject::getParallelArrayElement(JSContext *cx, uint32_t index, IndexInfo *maybeIV, - MutableHandleValue vp) -{ - // If we are one dimensional, we don't need to use IndexInfo. - if (isOneDimensional()) { - uint32_t base = bufferOffset(); - uint32_t end = base + outermostDimension(); - - if (base + index >= end) - vp.setUndefined(); - else - vp.set(buffer()->getDenseElement(base + index)); - - return true; - } - - // If we're higher dimensional, an initialized IndexInfo must be provided. - JS_ASSERT(maybeIV); - JS_ASSERT(maybeIV->isInitialized()); - JS_ASSERT(maybeIV->indices.length() == 1); - - maybeIV->indices[0] = index; - return getParallelArrayElement(cx, *maybeIV, vp); -} - -bool -ParallelArrayObject::getParallelArrayElement(JSContext *cx, uint32_t index, MutableHandleValue vp) -{ - if (isOneDimensional()) - return getParallelArrayElement(cx, index, NULL, vp); - - // Manually initialize to avoid re-rooting 'this', as this code could be - // called from inside a loop, though you really should hoist out the - // IndexInfo if that's the case. - IndexInfo iv(cx); - if (!getDimensions(cx, iv.dimensions) || !iv.initialize(1)) - return false; - iv.indices[0] = index; - return getParallelArrayElement(cx, iv, vp); -} - -bool -ParallelArrayObject::create(JSContext *cx, MutableHandleValue vp) -{ - IndexVector dims(cx); - if (!dims.append(0)) - return false; - RootedObject buffer(cx, NewDenseArrayWithType(cx, 0)); - if (!buffer) - return false; - return create(cx, buffer, 0, dims, vp); -} - -bool -ParallelArrayObject::create(JSContext *cx, HandleObject buffer, MutableHandleValue vp) -{ - IndexVector dims(cx); - if (!dims.append(buffer->getArrayLength())) - return false; - return create(cx, buffer, 0, dims, vp); -} - -bool -ParallelArrayObject::create(JSContext *cx, HandleObject buffer, uint32_t offset, - const IndexVector &dims, MutableHandleValue vp) -{ - JS_ASSERT(buffer->isArray()); - - RootedObject result(cx, NewBuiltinClassInstance(cx, &class_)); - if (!result) - return false; - - // Propagate element types. - if (cx->typeInferenceEnabled()) { - AutoEnterAnalysis enter(cx); - TypeObject *bufferType = buffer->getType(cx); - TypeObject *resultType = result->getType(cx); - if (!bufferType->unknownProperties() && !resultType->unknownProperties()) { - HeapTypeSet *bufferIndexTypes = bufferType->getProperty(cx, JSID_VOID, false); - HeapTypeSet *resultIndexTypes = resultType->getProperty(cx, JSID_VOID, true); - bufferIndexTypes->addSubset(cx, resultIndexTypes); - } - } - - // Store the dimension vector into a dense array for better GC / layout. - RootedObject dimArray(cx, NewDenseArrayWithType(cx, dims.length())); - if (!dimArray) - return false; - - for (uint32_t i = 0; i < dims.length(); i++) - JSObject::setDenseElementWithType(cx, dimArray, i, - Int32Value(static_cast(dims[i]))); - - result->setSlot(SLOT_DIMENSIONS, ObjectValue(*dimArray)); - - // Store the buffer and offset. - result->setSlot(SLOT_BUFFER, ObjectValue(*buffer)); - result->setSlot(SLOT_BUFFER_OFFSET, Int32Value(static_cast(offset))); - - // ParallelArray objects are frozen, so mark it as non-extensible here. - Shape *empty = EmptyShape::getInitialShape(cx, &class_, - result->getProto(), result->getParent(), - result->getAllocKind(), - BaseShape::NOT_EXTENSIBLE); - if (!empty) - return false; - result->setLastPropertyInfallible(empty); - - // This is usually args.rval() from build or construct. - vp.setObject(*result); - - return true; -} - -JSBool -ParallelArrayObject::construct(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - // Trivial case: create an empty ParallelArray object. - if (args.length() < 1) - return create(cx, args.rval()); - - // First case: initialize using an array value. - if (args.length() == 1) { - RootedObject source(cx, NonNullObject(cx, args[0])); - if (!source) - return false; - - // When using an array value we can only make one dimensional arrays. - IndexVector dims(cx); - uint32_t length; - if (!dims.resize(1) || !GetLength(cx, source, &length)) - return false; - dims[0] = length; - - RootedObject buffer(cx, NewDenseCopiedArrayWithType(cx, length, source)); - if (!buffer) - return false; - - return create(cx, buffer, 0, dims, args.rval()); - } - - // Second case: initialize using a length/dimensions vector and kernel. - // - // If the length is an integer, we build a 1-dimensional parallel - // array using the kernel. - // - // If the length is an array-like object of sizes, the i-th value in the - // dimension array is the size of the i-th dimension. - IndexInfo iv(cx); - bool malformed; - if (args[0].isObject()) { - RootedObject dimObj(cx, &(args[0].toObject())); - if (!ArrayLikeToIndexVector(cx, dimObj, iv.dimensions, &malformed)) - return false; - if (malformed) - return ReportBadLength(cx); - } else { - if (!iv.dimensions.resize(1)) - return false; - - if (!ToUint32(cx, args[0], &iv.dimensions[0], &malformed)) - return false; - if (malformed) { - RootedValue arg0(cx, args[0]); - return ReportBadLengthOrArg(cx, arg0); - } - } - - // If the first argument wasn't a array-like or had no length, assume - // empty parallel array, i.e. with shape being [0]. - if (iv.dimensions.length() == 0 && !iv.dimensions.append(0)) - return false; - - // Initialize with every dimension packed. - if (!iv.initialize(iv.dimensions.length())) - return false; - - // We checked that each individual dimension does not overflow; now check - // that the scalar length does not overflow. - uint32_t length = iv.scalarLengthOfDimensions(); - double d = iv.dimensions[0]; - for (uint32_t i = 1; i < iv.dimensions.length(); i++) - d *= iv.dimensions[i]; - if (d != static_cast(length)) - return ReportBadLength(cx); - - // Extract second argument, the elemental function. - RootedObject elementalFun(cx, ValueToCallable(cx, args[1], args.length() - 2)); - if (!elementalFun) - return false; - - // Create backing store. - RootedObject buffer(cx, NewDenseArrayWithType(cx, length)); - if (!buffer) - return false; - -#ifdef DEBUG - if (args.length() > 2) { - DebugOptions options; - if (!options.init(cx, args[2]) || - !options.check(cx, options.mode->build(cx, iv, elementalFun, buffer))) - { - return false; - } - - return create(cx, buffer, 0, iv.dimensions, args.rval()); - } -#endif - - if (fallback.build(cx, iv, elementalFun, buffer) != ExecutionSucceeded) - return false; - - return create(cx, buffer, 0, iv.dimensions, args.rval()); -} - -bool -ParallelArrayObject::map(JSContext *cx, CallArgs args) -{ - if (args.length() < 1) - return ReportMoreArgsNeeded(cx, "ParallelArray.prototype.map", "0", "s"); - - RootedParallelArrayObject obj(cx, as(&args.thisv().toObject())); - - uint32_t outer = obj->outermostDimension(); - RootedObject buffer(cx, NewDenseArrayWithType(cx, outer)); - if (!buffer) - return false; - - RootedObject elementalFun(cx, ValueToCallable(cx, args[0], args.length() - 1)); - if (!elementalFun) - return false; - -#ifdef DEBUG - if (args.length() > 1) { - DebugOptions options; - if (!options.init(cx, args[1]) || - !options.check(cx, options.mode->map(cx, obj, elementalFun, buffer))) - { - return false; - } - - return create(cx, buffer, args.rval()); - } -#endif - - if (fallback.map(cx, obj, elementalFun, buffer) != ExecutionSucceeded) - return false; - - return create(cx, buffer, args.rval()); -} - -bool -ParallelArrayObject::reduce(JSContext *cx, CallArgs args) -{ - if (args.length() < 1) - return ReportMoreArgsNeeded(cx, "ParallelArray.prototype.reduce", "0", "s"); - - RootedParallelArrayObject obj(cx, as(&args.thisv().toObject())); - uint32_t outer = obj->outermostDimension(); - - // Throw if the array is empty. - if (outer == 0) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_REDUCE_EMPTY); - return false; - } - - RootedObject elementalFun(cx, ValueToCallable(cx, args[0], args.length() - 1)); - if (!elementalFun) - return false; - -#ifdef DEBUG - if (args.length() > 1) { - DebugOptions options; - if (!options.init(cx, args[1])) - return false; - - return options.check(cx, options.mode->reduce(cx, obj, elementalFun, NullPtr(), - args.rval())); - } -#endif - - // Call reduce with a null destination buffer to not store intermediates. - return fallback.reduce(cx, obj, elementalFun, NullPtr(), args.rval()) == ExecutionSucceeded; -} - -bool -ParallelArrayObject::scan(JSContext *cx, CallArgs args) -{ - if (args.length() < 1) - return ReportMoreArgsNeeded(cx, "ParallelArray.prototype.scan", "0", "s"); - - RootedParallelArrayObject obj(cx, as(&args.thisv().toObject())); - uint32_t outer = obj->outermostDimension(); - - // Throw if the array is empty. - if (outer == 0) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_REDUCE_EMPTY); - return false; - } - - RootedObject buffer(cx, NewDenseArrayWithType(cx, outer)); - if (!buffer) - return false; - - RootedObject elementalFun(cx, ValueToCallable(cx, args[0], args.length() - 1)); - if (!elementalFun) - return false; - - // Call reduce with a dummy out value to be discarded and a buffer to - // store intermediates. - RootedValue dummy(cx); - -#ifdef DEBUG - if (args.length() > 1) { - DebugOptions options; - if (!options.init(cx, args[1]) || - !options.check(cx, options.mode->reduce(cx, obj, elementalFun, buffer, &dummy))) - { - return false; - } - - return create(cx, buffer, args.rval()); - } -#endif - - if (fallback.reduce(cx, obj, elementalFun, buffer, &dummy) != ExecutionSucceeded) - return false; - - return create(cx, buffer, args.rval()); -} - -bool -ParallelArrayObject::scatter(JSContext *cx, CallArgs args) -{ - if (args.length() < 1) - return ReportMoreArgsNeeded(cx, "ParallelArray.prototype.scatter", "0", "s"); - - RootedParallelArrayObject obj(cx, as(&args.thisv().toObject())); - uint32_t outer = obj->outermostDimension(); - - // Get the scatter vector. - RootedObject targets(cx, NonNullObject(cx, args[0])); - if (!targets) - return false; - - // The default value is optional and defaults to undefined. - RootedValue defaultValue(cx); - if (args.length() >= 2) - defaultValue = args[1]; - else - defaultValue.setUndefined(); - - // The conflict function is optional. - RootedObject conflictFun(cx); - if (args.length() >= 3 && !args[2].isUndefined()) { - conflictFun = ValueToCallable(cx, args[2], args.length() - 3); - if (!conflictFun) - return false; - } - - // The length of the result array is optional and defaults to the length - // of the source array. - uint32_t resultLength; - if (args.length() >= 4) { - bool malformed; - if (!ToUint32(cx, args[3], &resultLength, &malformed)) - return false; - if (malformed) { - RootedValue arg3(cx, args[3]); - return ReportBadLengthOrArg(cx, arg3, ".prototype.scatter"); - } - } else { - resultLength = outer; - } - - // Create a destination buffer. Fail if we can't maintain denseness. - RootedObject buffer(cx, NewDenseArrayWithType(cx, resultLength)); - if (!buffer) - return false; - -#ifdef DEBUG - if (args.length() > 4) { - DebugOptions options; - if (!options.init(cx, args[4]) || - !options.check(cx, options.mode->scatter(cx, obj, targets, defaultValue, - conflictFun, buffer))) - { - return false; - } - - return create(cx, buffer, args.rval()); - } -#endif - - if (fallback.scatter(cx, obj, targets, defaultValue, - conflictFun, buffer) != ExecutionSucceeded) - { - return false; - } - - return create(cx, buffer, args.rval()); -} - -bool -ParallelArrayObject::filter(JSContext *cx, CallArgs args) -{ - if (args.length() < 1) - return ReportMoreArgsNeeded(cx, "ParallelArray.prototype.filter", "0", "s"); - - RootedParallelArrayObject obj(cx, as(&args.thisv().toObject())); - - // Get the filter vector. - RootedObject filters(cx, NonNullObject(cx, args[0])); - if (!filters) - return false; - - RootedObject buffer(cx, NewDenseArrayWithType(cx, 0)); - if (!buffer) - return false; - -#ifdef DEBUG - if (args.length() > 1) { - DebugOptions options; - if (!options.init(cx, args[1]) || - !options.check(cx, options.mode->filter(cx, obj, filters, buffer))) - { - return false; - } - - return create(cx, buffer, args.rval()); - } -#endif - - if (fallback.filter(cx, obj, filters, buffer) != ExecutionSucceeded) - return false; - - return create(cx, buffer, args.rval()); -} - -bool -ParallelArrayObject::flatten(JSContext *cx, CallArgs args) -{ - RootedParallelArrayObject obj(cx, as(&args.thisv().toObject())); - - IndexVector dims(cx); - if (!obj->getDimensions(cx, dims)) - return false; - - // Throw if already flat. - if (dims.length() == 1) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_ALREADY_FLAT); - return false; - } - - // Flatten the two outermost dimensions. - dims[1] *= dims[0]; - dims.erase(dims.begin()); - - RootedObject buffer(cx, obj->buffer()); - return create(cx, buffer, obj->bufferOffset(), dims, args.rval()); -} - -bool -ParallelArrayObject::partition(JSContext *cx, CallArgs args) -{ - if (args.length() < 1) - return ReportMoreArgsNeeded(cx, "ParallelArray.prototype.partition", "0", "s"); - - uint32_t newDimension; - bool malformed; - if (!ToUint32(cx, args[0], &newDimension, &malformed)) - return false; - if (malformed) - return ReportBadPartition(cx); - - RootedParallelArrayObject obj(cx, as(&args.thisv().toObject())); - - // Throw if the outer dimension is not divisible by the new dimension. - uint32_t outer = obj->outermostDimension(); - if (newDimension == 0 || outer % newDimension) - return ReportBadPartition(cx); - - IndexVector dims(cx); - if (!obj->getDimensions(cx, dims)) - return false; - - // Set the new outermost dimension to be the quotient of the old outermost - // dimension and the new dimension. - if (!dims.insert(dims.begin(), outer / newDimension)) - return false; - - // Set the old outermost dimension to be the new dimension. - dims[1] = newDimension; - - RootedObject buffer(cx, obj->buffer()); - return create(cx, buffer, obj->bufferOffset(), dims, args.rval()); -} - -bool -ParallelArrayObject::get(JSContext *cx, CallArgs args) -{ - if (args.length() < 1) - return ReportMoreArgsNeeded(cx, "ParallelArray.prototype.get", "0", "s"); - - RootedParallelArrayObject obj(cx, as(&args.thisv().toObject())); - RootedObject indicesObj(cx, NonNullObject(cx, args[0])); - if (!indicesObj) - return false; - - IndexInfo iv(cx); - if (!iv.initialize(cx, obj, 0)) - return false; - - bool malformed; - if (!ArrayLikeToIndexVector(cx, indicesObj, iv.indices, &malformed)) - return false; - - // Throw if the shape of the index vector is wrong. - if (iv.indices.length() == 0 || iv.indices.length() > iv.dimensions.length()) - return ReportBadArg(cx, ".prototype.get"); - - // Don't throw on overflow, just return undefined. - if (malformed) { - args.rval().setUndefined(); - return true; - } - - return obj->getParallelArrayElement(cx, iv, args.rval()); -} - -bool -ParallelArrayObject::dimensionsGetter(JSContext *cx, CallArgs args) -{ - RootedObject dimArray(cx, as(&args.thisv().toObject())->dimensionArray()); - RootedObject copy(cx, NewDenseCopiedArray(cx, dimArray->getDenseInitializedLength(), - dimArray, 0)); - if (!copy) - return false; - // Reuse the existing dimension array's type. - copy->setType(dimArray->type()); - args.rval().setObject(*copy); - return true; -} - -bool -ParallelArrayObject::lengthGetter(JSContext *cx, CallArgs args) -{ - args.rval().setNumber(as(&args.thisv().toObject())->outermostDimension()); - return true; -} - -bool -ParallelArrayObject::toStringBuffer(JSContext *cx, bool useLocale, StringBuffer &sb) -{ - if (!JS_CHECK_OPERATION_LIMIT(cx)) - return false; - - RootedParallelArrayObject self(cx, this); - IndexInfo iv(cx); - - if (!self->getDimensions(cx, iv.dimensions) || !iv.initialize(iv.dimensions.length())) - return false; - - uint32_t length = iv.scalarLengthOfDimensions(); - - RootedValue tmp(cx); - RootedValue localeElem(cx); - RootedId id(cx); - - const Value *start = buffer()->getDenseElements() + bufferOffset(); - const Value *end = start + length; - const Value *elem; - - for (elem = start; elem < end; elem++, iv.bump()) { - // All holes in parallel arrays are eagerly filled with undefined. - JS_ASSERT(!elem->isMagic(JS_ELEMENTS_HOLE)); - - if (!OpenDelimiters(iv, sb)) - return false; - - if (!elem->isNullOrUndefined()) { - if (useLocale) { - tmp = *elem; - RootedObject robj(cx, ToObject(cx, tmp)); - if (!robj) - return false; - - id = NameToId(cx->names().toLocaleString); - if (!robj->callMethod(cx, id, 0, NULL, &localeElem) || - !ValueToStringBuffer(cx, localeElem, sb)) - { - return false; - } - } else { - if (!ValueToStringBuffer(cx, *elem, sb)) - return false; - } - } - - if (!CloseDelimiters(iv, sb)) - return false; - } - - return true; -} - -bool -ParallelArrayObject::toString(JSContext *cx, CallArgs args) -{ - StringBuffer sb(cx); - if (!as(&args.thisv().toObject())->toStringBuffer(cx, false, sb)) - return false; - - if (JSString *str = sb.finishString()) { - args.rval().setString(str); - return true; - } - - return false; -} - -bool -ParallelArrayObject::toLocaleString(JSContext *cx, CallArgs args) -{ - StringBuffer sb(cx); - if (!as(&args.thisv().toObject())->toStringBuffer(cx, true, sb)) - return false; - - if (JSString *str = sb.finishString()) { - args.rval().setString(str); - return true; - } - - return false; -} - -void -ParallelArrayObject::mark(JSTracer *trc, RawObject obj) -{ - gc::MarkSlot(trc, &obj->getSlotRef(SLOT_DIMENSIONS), "parallelarray.shape"); - gc::MarkSlot(trc, &obj->getSlotRef(SLOT_BUFFER), "parallelarray.buffer"); -} - -JSBool -ParallelArrayObject::lookupGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleObject objp, MutableHandleShape propp) -{ - uint32_t i; - if (js_IdIsIndex(id, &i)) - return lookupElement(cx, obj, i, objp, propp); - - RootedObject proto(cx, obj->getProto()); - if (proto) - return JSObject::lookupGeneric(cx, proto, id, objp, propp); - - objp.set(NULL); - propp.set(NULL); - return true; -} - -JSBool -ParallelArrayObject::lookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - MutableHandleObject objp, MutableHandleShape propp) -{ - RootedId id(cx, NameToId(name)); - return lookupGeneric(cx, obj, id, objp, propp); -} - -JSBool -ParallelArrayObject::lookupElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleObject objp, MutableHandleShape propp) -{ - // No prototype walking for elements. - if (index < as(obj)->outermostDimension()) { - MarkNonNativePropertyFound(propp); - objp.set(obj); - return true; - } - - objp.set(NULL); - propp.set(NULL); - return true; -} - -JSBool -ParallelArrayObject::lookupSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, - MutableHandleObject objp, MutableHandleShape propp) -{ - RootedId id(cx, SPECIALID_TO_JSID(sid)); - return lookupGeneric(cx, obj, id, objp, propp); -} - -JSBool -ParallelArrayObject::defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value, - JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs) -{ - uint32_t i; - if (js_IdIsIndex(id, &i) && i < as(obj)->outermostDimension()) { - RootedValue existingValue(cx); - if (!as(obj)->getParallelArrayElement(cx, i, &existingValue)) - return false; - - bool same; - if (!SameValue(cx, value, existingValue, &same)) - return false; - if (!same) - return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP); - } else { - RootedValue tmp(cx, value); - if (!setGeneric(cx, obj, id, &tmp, true)) - return false; - } - - return setGenericAttributes(cx, obj, id, &attrs); -} - -JSBool -ParallelArrayObject::defineProperty(JSContext *cx, HandleObject obj, - HandlePropertyName name, HandleValue value, - JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs) -{ - RootedId id(cx, NameToId(name)); - return defineGeneric(cx, obj, id, value, getter, setter, attrs); -} - -JSBool -ParallelArrayObject::defineElement(JSContext *cx, HandleObject obj, - uint32_t index, HandleValue value, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - return defineGeneric(cx, obj, id, value, getter, setter, attrs); -} - -JSBool -ParallelArrayObject::defineSpecial(JSContext *cx, HandleObject obj, - HandleSpecialId sid, HandleValue value, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs) -{ - RootedId id(cx, SPECIALID_TO_JSID(sid)); - return defineGeneric(cx, obj, id, value, getter, setter, attrs); -} - -JSBool -ParallelArrayObject::getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, - HandleId id, MutableHandleValue vp) -{ - RootedValue idval(cx, IdToValue(id)); - - uint32_t index; - if (IsDefinitelyIndex(idval, &index)) - return getElement(cx, obj, receiver, index, vp); - - Rooted sid(cx); - if (ValueIsSpecial(obj, &idval, &sid, cx)) - return getSpecial(cx, obj, receiver, sid, vp); - - JSAtom *atom = ToAtom(cx, idval); - if (!atom) - return false; - - if (atom->isIndex(&index)) - return getElement(cx, obj, receiver, index, vp); - - Rooted name(cx, atom->asPropertyName()); - return getProperty(cx, obj, receiver, name, vp); -} - -JSBool -ParallelArrayObject::getProperty(JSContext *cx, HandleObject obj, HandleObject receiver, - HandlePropertyName name, MutableHandleValue vp) -{ - RootedObject proto(cx, obj->getProto()); - if (proto) - return JSObject::getProperty(cx, proto, receiver, name, vp); - - vp.setUndefined(); - return true; -} - -JSBool -ParallelArrayObject::getElement(JSContext *cx, HandleObject obj, HandleObject receiver, - uint32_t index, MutableHandleValue vp) -{ - // Unlike normal arrays, [] for ParallelArray does not walk the prototype - // chain and just returns undefined. - return as(obj)->getParallelArrayElement(cx, index, vp); -} - -JSBool -ParallelArrayObject::getElementIfPresent(JSContext *cx, HandleObject obj, HandleObject receiver, - uint32_t index, MutableHandleValue vp, bool *present) -{ - RootedParallelArrayObject source(cx, as(obj)); - if (index < source->outermostDimension()) { - if (!source->getParallelArrayElement(cx, index, vp)) - return false; - *present = true; - return true; - } - - *present = false; - vp.setUndefined(); - return true; -} - -JSBool -ParallelArrayObject::getSpecial(JSContext *cx, HandleObject obj, HandleObject receiver, - HandleSpecialId sid, MutableHandleValue vp) -{ - if (!obj->getProto()) { - vp.setUndefined(); - return true; - } - - RootedId id(cx, SPECIALID_TO_JSID(sid)); - return baseops::GetProperty(cx, obj, receiver, id, vp); -} - -JSBool -ParallelArrayObject::setGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleValue vp, JSBool strict) -{ - JS_ASSERT(!obj->isExtensible()); - - if (IdIsInBoundsIndex(cx, obj, id)) { - if (strict) - return JSObject::reportReadOnly(cx, id); - if (cx->hasStrictOption()) - return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING); - } else { - if (strict) - return obj->reportNotExtensible(cx); - if (cx->hasStrictOption()) - return obj->reportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING); - } - - return true; -} - -JSBool -ParallelArrayObject::setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - MutableHandleValue vp, JSBool strict) -{ - RootedId id(cx, NameToId(name)); - return setGeneric(cx, obj, id, vp, strict); -} - -JSBool -ParallelArrayObject::setElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleValue vp, JSBool strict) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - return setGeneric(cx, obj, id, vp, strict); -} - -JSBool -ParallelArrayObject::setSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, - MutableHandleValue vp, JSBool strict) -{ - RootedId id(cx, SPECIALID_TO_JSID(sid)); - return setGeneric(cx, obj, id, vp, strict); -} - -JSBool -ParallelArrayObject::getGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, - unsigned *attrsp) -{ - *attrsp = JSPROP_PERMANENT | JSPROP_READONLY; - uint32_t i; - if (js_IdIsIndex(id, &i)) - *attrsp |= JSPROP_ENUMERATE; - return true; -} - -JSBool -ParallelArrayObject::getPropertyAttributes(JSContext *cx, HandleObject obj, HandlePropertyName name, - unsigned *attrsp) -{ - *attrsp = JSPROP_PERMANENT | JSPROP_READONLY; - return true; -} - -JSBool -ParallelArrayObject::getElementAttributes(JSContext *cx, HandleObject obj, uint32_t index, - unsigned *attrsp) -{ - *attrsp = JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_ENUMERATE; - return true; -} - -JSBool -ParallelArrayObject::getSpecialAttributes(JSContext *cx, HandleObject obj, HandleSpecialId sid, - unsigned *attrsp) -{ - *attrsp = JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_ENUMERATE; - return true; -} - -JSBool -ParallelArrayObject::setGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, - unsigned *attrsp) -{ - if (IdIsInBoundsIndex(cx, obj, id)) { - unsigned attrs; - if (!getGenericAttributes(cx, obj, id, &attrs)) - return false; - if (*attrsp != attrs) - return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP); - } - - return obj->reportNotExtensible(cx); -} - -JSBool -ParallelArrayObject::setPropertyAttributes(JSContext *cx, HandleObject obj, HandlePropertyName name, - unsigned *attrsp) -{ - RootedId id(cx, NameToId(name)); - return setGenericAttributes(cx, obj, id, attrsp); -} - -JSBool -ParallelArrayObject::setElementAttributes(JSContext *cx, HandleObject obj, uint32_t index, - unsigned *attrsp) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - return setGenericAttributes(cx, obj, id, attrsp); -} - -JSBool -ParallelArrayObject::setSpecialAttributes(JSContext *cx, HandleObject obj, HandleSpecialId sid, - unsigned *attrsp) -{ - RootedId id(cx, SPECIALID_TO_JSID(sid)); - return setGenericAttributes(cx, obj, id, attrsp); -} - -JSBool -ParallelArrayObject::deleteGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleValue rval, JSBool strict) -{ - if (IdIsInBoundsIndex(cx, obj, id)) { - if (strict) - return obj->reportNotConfigurable(cx, id); - if (cx->hasStrictOption()) { - if (!obj->reportNotConfigurable(cx, id, JSREPORT_STRICT | JSREPORT_WARNING)) - return false; - } - - rval.setBoolean(false); - return true; - } - - rval.setBoolean(true); - return true; -} - -JSBool -ParallelArrayObject::deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - MutableHandleValue rval, JSBool strict) -{ - RootedId id(cx, NameToId(name)); - return deleteGeneric(cx, obj, id, rval, strict); -} - -JSBool -ParallelArrayObject::deleteElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleValue rval, JSBool strict) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - return deleteGeneric(cx, obj, id, rval, strict); -} - -JSBool -ParallelArrayObject::deleteSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, - MutableHandleValue rval, JSBool strict) -{ - RootedId id(cx, SPECIALID_TO_JSID(sid)); - return deleteGeneric(cx, obj, id, rval, strict); -} - -bool -ParallelArrayObject::enumerate(JSContext *cx, HandleObject obj, unsigned flags, - AutoIdVector *props) -{ - RootedParallelArrayObject source(cx, as(obj)); - - // ParallelArray objects have no holes. - if (source->outermostDimension() > 0) { - for (uint32_t i = 0; i < source->outermostDimension(); i++) { - if (!props->append(INT_TO_JSID(i))) - return false; - } - } - - if (flags & JSITER_OWNONLY) - return true; - - RootedObject proto(cx, obj->getProto()); - if (proto) { - AutoIdVector protoProps(cx); - if (!GetPropertyNames(cx, proto, flags, &protoProps)) - return false; - - // ParallelArray objects do not inherit any indexed properties on the - // prototype chain. - uint32_t dummy; - for (uint32_t i = 0; i < protoProps.length(); i++) { - if (!js_IdIsIndex(protoProps[i], &dummy) && !props->append(protoProps[i])) - return false; - } - } - - return true; + return v.isObject() && v.toObject().hasClass(&class_); } JSObject * -js_InitParallelArrayClass(JSContext *cx, HandleObject obj) +js_InitParallelArrayClass(JSContext *cx, js::HandleObject obj) { return ParallelArrayObject::initClass(cx, obj); } diff --git a/js/src/builtin/ParallelArray.h b/js/src/builtin/ParallelArray.h index 1cff2e3c19d..d63b87872fb 100644 --- a/js/src/builtin/ParallelArray.h +++ b/js/src/builtin/ParallelArray.h @@ -12,402 +12,45 @@ #include "jscntxt.h" #include "jsobj.h" +#include "ion/Ion.h" +#include "vm/ForkJoin.h" +#include "vm/ThreadPool.h" + namespace js { -class ParallelArrayObject; -typedef Rooted RootedParallelArrayObject; -typedef Handle HandleParallelArrayObject; +class ParallelArrayObject : public JSObject +{ + static Class protoClass; + static JSFunctionSpec methods[]; + static const uint32_t NumFixedSlots = 4; + static const uint32_t NumCtors = 4; + static FixedHeapPtr ctorNames[NumCtors]; -// -// ParallelArray Overview -// -// Parallel arrays are immutable, possibly multi-dimensional arrays which -// enable parallel computation based on a few base operations. The execution -// model is one of fallback: try to execute operations in parallel, falling -// back to sequential implementation if (for whatever reason) the operation -// could not be executed in paralle. The API allows leeway to implementers to -// decide both representation and what is considered parallelizable. -// -// Currently ParallelArray objects are backed by dense arrays for ease of -// GC. For the higher-dimensional case, data is stored in a packed, row-major -// order representation in the backing dense array. See notes below about -// IndexInfo in how to convert between scalar offsets into the backing array -// and a vector of indices. -// -// ParallelArray objects are always dense. That is, all holes are eagerly -// filled in with undefined instead of being JS_ARRAY_HOLE. This results in a -// break from the behavior of normal JavaScript arrays: if a ParallelArray p -// is missing an indexed property i, p[i] is _always_ undefined and will never -// walk up the prototype chain in search of i. -// -// Except for the comprehension form, all operations (e.g. map, filter, -// reduce) operate on the outermost dimension only. That is, those operations -// only operate on the "rows" of the array. "Element" is used in context of -// ParallelArray objects to mean any indexable value of a ParallelArray -// object. For a one dimensional array, elements are always scalar values. For -// a higher dimensional array, elements could either be scalar values -// (i.e. leaves) or ParallelArray objects of lesser dimensions -// (i.e. subarrays). -// + static bool initProps(JSContext *cx, HandleObject obj); -class ParallelArrayObject : public JSObject { public: - typedef Vector IndexVector; - - // - // Helper structure to help index higher-dimensional arrays to convert - // between a vector of indices and scalar offsets for use in the flat - // backing dense array. - // - // IndexInfo instances _must_ be initialized using one of the initialize - // methods before use. - // - // Typical usage is stack allocating an IndexInfo, initializing it with a - // particular source ParallelArray object's dimensionality, and mutating - // the indices member vector. For instance, to iterate through everything - // in the first 2 dimensions of an array of > 2 dimensions: - // - // IndexInfo iv(cx); - // if (!iv.initialize(cx, source, 2)) - // return false; - // for (uint32_t i = 0; i < iv.dimensions[0]; i++) { - // for (uint32_t j = 0; j < iv.dimensions[1]; j++) { - // iv.indices[0] = i; - // iv.indices[1] = j; - // if (source->getParallelArrayElement(cx, iv, &elem)) - // ... - // } - // } - // - // Note from the above that it is not required to fill out the indices - // vector up to the full dimensionality. For an N-dimensional array, - // having an indices vector of length D < N indexes a subarray. - // - - struct IndexInfo { - // Vector of indices. Should be large enough to hold up to - // dimensions.length indices. - IndexVector indices; - - // Vector of dimensions of the ParallelArray object that the indices - // are meant to index into. - IndexVector dimensions; - - // Cached partial products of the dimensions defined by the following - // recurrence: - // - // partialProducts[n] = - // 1 if n == |dimensions| - // dimensions[n+1] * partialProducts[n+1] otherwise - // - // These are used for computing scalar offsets. - IndexVector partialProducts; - - IndexInfo(JSContext *cx) - : indices(cx), dimensions(cx), partialProducts(cx) - {} - - // Prepares indices and computes partial products. The space argument - // is the index space. The indices vector is resized to be of length - // space. - // - // The dimensions vector must be filled already, and space must be <= - // dimensions.length(). - inline bool initialize(uint32_t space); - - // Load dimensions from a source, then initialize as above. - inline bool initialize(JSContext *cx, HandleParallelArrayObject source, - uint32_t space); - - // Bump the index by 1, wrapping over if necessary. Returns false when - // the increment would go out of bounds. - inline bool bump(); - - // Get the scalar length according to the dimensions vector, i.e. the - // product of the dimensions vector. - inline uint32_t scalarLengthOfDimensions(); - - // Compute the scalar index from the current index vector. - inline uint32_t toScalar(); - - // Set the index vector according to a scalar index. - inline bool fromScalar(uint32_t index); - - inline bool inBounds() const; - bool isInitialized() const; - }; - - static JSObject *initClass(JSContext *cx, JSObject *obj); static Class class_; - static inline bool is(const Value &v); - static inline bool is(JSObject *obj); - static inline ParallelArrayObject *as(JSObject *obj); - - inline JSObject *dimensionArray(); - inline JSObject *buffer(); - inline uint32_t bufferOffset(); - inline uint32_t outermostDimension(); - inline bool isOneDimensional(); - inline bool getDimensions(JSContext *cx, IndexVector &dims); - - // The general case; requires an initialized IndexInfo. - bool getParallelArrayElement(JSContext *cx, IndexInfo &iv, MutableHandleValue vp); - - // Get the element at index in the outermost dimension. This is a - // convenience function designed to require an IndexInfo only if it is - // actually needed. - // - // If the parallel array is multidimensional, then the caller must provide - // an IndexInfo initialized to length 1, which is used to access the - // array. This argument is modified. If the parallel array is - // one-dimensional, then maybeIV may be null. - bool getParallelArrayElement(JSContext *cx, uint32_t index, IndexInfo *maybeIV, - MutableHandleValue vp); - - // Get the element at index in the outermost dimension. This is a - // convenience function that initializes a temporary - // IndexInfo if the parallel array is multidimensional. - bool getParallelArrayElement(JSContext *cx, uint32_t index, MutableHandleValue vp); - - bool toStringBuffer(JSContext *cx, bool useLocale, StringBuffer &sb); - - // Note that this is not an object op but called directly from the - // iteration code, as we enumerate prototypes ourselves. - static bool enumerate(JSContext *cx, HandleObject obj, unsigned flags, - AutoIdVector *props); - - private: - enum { - // The ParallelArray API refers to dimensions as "shape", but to avoid - // confusion with the internal engine notion of a shape we call it - // "dimensions" here. - SLOT_DIMENSIONS = 0, - - // Underlying dense array. - SLOT_BUFFER, - - // First index of the underlying buffer to be considered in bounds. - SLOT_BUFFER_OFFSET, - - RESERVED_SLOTS - }; - - enum ExecutionStatus { - ExecutionFailed = 0, - ExecutionCompiled, - ExecutionSucceeded - }; - - // Execution modes are kept as static instances of structs that implement - // a signature that comprises of build, map, fold, scatter, and filter, - // whose argument lists are defined in the macros below. - // - // Even though the base class |ExecutionMode| is purely abstract, we only - // use dynamic dispatch when using the debug options. Almost always we - // directly call the member function on one of the statics. - -#define JS_PA_build_ARGS \ - JSContext *cx, \ - IndexInfo &iv, \ - HandleObject elementalFun, \ - HandleObject buffer - -#define JS_PA_map_ARGS \ - JSContext *cx, \ - HandleParallelArrayObject source, \ - HandleObject elementalFun, \ - HandleObject buffer - -#define JS_PA_reduce_ARGS \ - JSContext *cx, \ - HandleParallelArrayObject source, \ - HandleObject elementalFun, \ - HandleObject buffer, \ - MutableHandleValue vp - -#define JS_PA_scatter_ARGS \ - JSContext *cx, \ - HandleParallelArrayObject source, \ - HandleObject targets, \ - const Value &defaultValue, \ - HandleObject conflictFun, \ - HandleObject buffer - -#define JS_PA_filter_ARGS \ - JSContext *cx, \ - HandleParallelArrayObject source, \ - HandleObject filters, \ - HandleObject buffer - -#define JS_PA_DECLARE_OP(NAME) \ - ExecutionStatus NAME(JS_PA_ ## NAME ## _ARGS) - -#define JS_PA_DECLARE_ALL_OPS \ - JS_PA_DECLARE_OP(build); \ - JS_PA_DECLARE_OP(map); \ - JS_PA_DECLARE_OP(reduce); \ - JS_PA_DECLARE_OP(scatter); \ - JS_PA_DECLARE_OP(filter); - - class ExecutionMode { - public: - // The comprehension form. Builds a higher-dimensional array using a - // kernel function. - virtual JS_PA_DECLARE_OP(build) = 0; - - // Maps a kernel function over the outermost dimension of the array. - virtual JS_PA_DECLARE_OP(map) = 0; - - // Reduce to a value using a kernel function. Scan is like reduce, but - // keeps the intermediate results in an array. - virtual JS_PA_DECLARE_OP(reduce) = 0; - - // Scatter elements according to an index map. - virtual JS_PA_DECLARE_OP(scatter) = 0; - - // Filter elements according to a truthy array. - virtual JS_PA_DECLARE_OP(filter) = 0; - - virtual const char *toString() = 0; - }; - - // Fallback means try parallel first, and if unable to execute in - // parallel, execute sequentially. - class FallbackMode : public ExecutionMode { - public: - JS_PA_DECLARE_ALL_OPS - const char *toString() { return "fallback"; } - }; - - class ParallelMode : public ExecutionMode { - public: - JS_PA_DECLARE_ALL_OPS - const char *toString() { return "parallel"; } - }; - - class SequentialMode : public ExecutionMode { - public: - JS_PA_DECLARE_ALL_OPS - const char *toString() { return "sequential"; } - }; - - static SequentialMode sequential; - static ParallelMode parallel; - static FallbackMode fallback; - -#undef JS_PA_build_ARGS -#undef JS_PA_map_ARGS -#undef JS_PA_reduce_ARGS -#undef JS_PA_scatter_ARGS -#undef JS_PA_filter_ARGS -#undef JS_PA_DECLARE_OP -#undef JS_PA_DECLARE_ALL_OPS - -#ifdef DEBUG - // Debug options can be passed in as an extra argument to the - // operations. The grammar is: - // - // options ::= { mode: "par" | "seq", - // expect: "fail" | "bail" | "success" } - struct DebugOptions { - ExecutionMode *mode; - ExecutionStatus expect; - bool init(JSContext *cx, const Value &v); - bool check(JSContext *cx, ExecutionStatus actual); - }; - - static const char *ExecutionStatusToString(ExecutionStatus ss); -#endif - - static JSFunctionSpec methods[]; - static Class protoClass; - - static inline bool DenseArrayToIndexVector(JSContext *cx, HandleObject obj, - IndexVector &indices); - - static bool create(JSContext *cx, MutableHandleValue vp); - static bool create(JSContext *cx, HandleObject buffer, MutableHandleValue vp); - static bool create(JSContext *cx, HandleObject buffer, uint32_t offset, - const IndexVector &dims, MutableHandleValue vp); static JSBool construct(JSContext *cx, unsigned argc, Value *vp); + static JSBool constructHelper(JSContext *cx, MutableHandleFunction ctor, CallArgs &args); - static bool map(JSContext *cx, CallArgs args); - static bool reduce(JSContext *cx, CallArgs args); - static bool scan(JSContext *cx, CallArgs args); - static bool scatter(JSContext *cx, CallArgs args); - static bool filter(JSContext *cx, CallArgs args); - static bool flatten(JSContext *cx, CallArgs args); - static bool partition(JSContext *cx, CallArgs args); - static bool get(JSContext *cx, CallArgs args); - static bool dimensionsGetter(JSContext *cx, CallArgs args); - static bool lengthGetter(JSContext *cx, CallArgs args); - static bool toString(JSContext *cx, CallArgs args); - static bool toLocaleString(JSContext *cx, CallArgs args); - static bool toSource(JSContext *cx, CallArgs args); + // Creates a new ParallelArray instance with the correct number of slots + // and so forth. + // + // NOTE: This object will NOT have the correct type object! It is + // up to the caller to adjust the type object appropriately + // before releasing the object into the wild. You probably want + // to be calling construct() above, which will adjust the type + // object for you, since ParallelArray type objects must be setup + // in a rather particular way to interact well with the + // self-hosted code. See constructHelper() for details. + static JSObject *newInstance(JSContext *cx); - static void mark(JSTracer *trc, RawObject obj); - static JSBool lookupGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleObject objp, MutableHandleShape propp); - static JSBool lookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - MutableHandleObject objp, MutableHandleShape propp); - static JSBool lookupElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleObject objp, MutableHandleShape propp); - static JSBool lookupSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, - MutableHandleObject objp, MutableHandleShape propp); - static JSBool defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value, - JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs); - static JSBool defineProperty(JSContext *cx, HandleObject obj, - HandlePropertyName name, HandleValue value, - JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs); - static JSBool defineElement(JSContext *cx, HandleObject obj, - uint32_t index, HandleValue value, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs); - static JSBool defineSpecial(JSContext *cx, HandleObject obj, - HandleSpecialId sid, HandleValue value, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs); - static JSBool getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, - HandleId id, MutableHandleValue vp); - static JSBool getProperty(JSContext *cx, HandleObject obj, HandleObject receiver, - HandlePropertyName name, MutableHandleValue vp); - static JSBool getElement(JSContext *cx, HandleObject obj, HandleObject receiver, - uint32_t index, MutableHandleValue vp); - static JSBool getSpecial(JSContext *cx, HandleObject obj, HandleObject receiver, - HandleSpecialId sid, MutableHandleValue vp); - static JSBool setGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleValue vp, JSBool strict); - static JSBool setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - MutableHandleValue vp, JSBool strict); - static JSBool setElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleValue vp, JSBool strict); - static JSBool getElementIfPresent(JSContext *cx, HandleObject obj, HandleObject receiver, - uint32_t index, MutableHandleValue vp, bool *present); - static JSBool setSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, - MutableHandleValue vp, JSBool strict); - static JSBool getGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, - unsigned *attrsp); - static JSBool getPropertyAttributes(JSContext *cx, HandleObject obj, HandlePropertyName name, - unsigned *attrsp); - static JSBool getElementAttributes(JSContext *cx, HandleObject obj, uint32_t index, - unsigned *attrsp); - static JSBool getSpecialAttributes(JSContext *cx, HandleObject obj, HandleSpecialId sid, - unsigned *attrsp); - static JSBool setGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, - unsigned *attrsp); - static JSBool setPropertyAttributes(JSContext *cx, HandleObject obj, HandlePropertyName name, - unsigned *attrsp); - static JSBool setElementAttributes(JSContext *cx, HandleObject obj, uint32_t index, - unsigned *attrsp); - static JSBool setSpecialAttributes(JSContext *cx, HandleObject obj, HandleSpecialId sid, - unsigned *attrsp); - static JSBool deleteGeneric(JSContext *cx, HandleObject obj, HandleId id, - MutableHandleValue rval, JSBool strict); - static JSBool deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, - MutableHandleValue rval, JSBool strict); - static JSBool deleteElement(JSContext *cx, HandleObject obj, uint32_t index, - MutableHandleValue rval, JSBool strict); - static JSBool deleteSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid, - MutableHandleValue rval, JSBool strict); + // Get the constructor function for argc number of arguments. + static JSFunction *getConstructor(JSContext *cx, unsigned argc); + + static JSObject *initClass(JSContext *cx, HandleObject obj); + static bool is(const Value &v); }; } // namespace js diff --git a/js/src/builtin/ParallelArray.js b/js/src/builtin/ParallelArray.js new file mode 100644 index 00000000000..a96e5633e75 --- /dev/null +++ b/js/src/builtin/ParallelArray.js @@ -0,0 +1,1283 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// FIXME(bug 844882): Parallel array properties should not be exposed. + +/** + * Determine the number of chunks of size CHUNK_SIZE; + * note that the final chunk may be smaller than CHUNK_SIZE. + */ +function ComputeNumChunks(length) { + var chunks = length >>> CHUNK_SHIFT; + if (chunks << CHUNK_SHIFT === length) + return chunks; + return chunks + 1; +} + +/** + * Computes the bounds for slice |sliceIndex| of |numItems| items, + * assuming |numSlices| total slices. If numItems is not evenly + * divisible by numSlices, then the final thread may have a bit of + * extra work. + */ +function ComputeSliceBounds(numItems, sliceIndex, numSlices) { + var sliceWidth = (numItems / numSlices) | 0; + var startIndex = sliceWidth * sliceIndex; + var endIndex = sliceIndex === numSlices - 1 ? numItems : sliceWidth * (sliceIndex + 1); + return [startIndex, endIndex]; +} + +/** + * Divides |numItems| items amongst |numSlices| slices. The result + * is an array containing multiple values per slice: the start + * index, end index, current position, and some padding. The + * current position is initially the same as the start index. To + * access the values for a particular slice, use the macros + * SLICE_START() and so forth. + */ +function ComputeAllSliceBounds(numItems, numSlices) { + // FIXME(bug 844890): Use typed arrays here. + var info = []; + for (var i = 0; i < numSlices; i++) { + var [start, end] = ComputeSliceBounds(numItems, i, numSlices); + ARRAY_PUSH(info, SLICE_INFO(start, end)); + } + return info; +} + +/** + * Compute the partial products in reverse order. + * e.g., if the shape is [A,B,C,D], then the + * array |products| will be [1,D,CD,BCD]. + */ +function ComputeProducts(shape) { + var product = 1; + var products = [1]; + var sdimensionality = shape.length; + for (var i = sdimensionality - 1; i > 0; i--) { + product *= shape[i]; + ARRAY_PUSH(products, product); + } + return products; +} + +/** + * Given a shape and some index |index1d|, computes and returns an + * array containing the N-dimensional index that maps to |index1d|. + */ +function ComputeIndices(shape, index1d) { + + var products = ComputeProducts(shape); + var l = shape.length; + + var result = []; + for (var i = 0; i < l; i++) { + // Obtain product of all higher dimensions. + // So if i == 0 and shape is [A,B,C,D], yields BCD. + var stride = products[l - i - 1]; + + // Compute how many steps of width stride we could take. + var index = (index1d / stride) | 0; + result[i] = index; + + // Adjust remaining indices for smaller dimensions. + index1d -= (index * stride); + } + + return result; +} + +function StepIndices(shape, indices) { + for (var i = shape.length - 1; i >= 0; i--) { + var indexi = indices[i] + 1; + if (indexi < shape[i]) { + indices[i] = indexi; + return; + } + indices[i] = 0; + } +} + +function IsInteger(v) { + return (v | 0) === v; +} + +// Constructor +// +// We split the 3 construction cases so that we don't case on arguments. + +/** + * This is the function invoked for |new ParallelArray()| + */ +function ParallelArrayConstructEmpty() { + this.buffer = []; + this.offset = 0; + this.shape = [0]; + this.get = ParallelArrayGet1; +} + +/** + * This is the function invoked for |new ParallelArray(array)|. + * It copies the data from its array-like argument |array|. + */ +function ParallelArrayConstructFromArray(buffer) { + var buffer = ToObject(buffer); + var length = buffer.length >>> 0; + if (length !== buffer.length) + ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); + + var buffer1 = []; + for (var i = 0; i < length; i++) + buffer1[i] = buffer[i]; + + this.buffer = buffer1; + this.offset = 0; + this.shape = [length]; + this.get = ParallelArrayGet1; +} + +/** + * Wrapper around |ParallelArrayConstructFromComprehension()| for the + * case where 2 arguments are supplied. This is typically what users will + * invoke. We provide an explicit two-argument version rather than + * relying on JS's semantics for absent arguments because it simplifies + * the ion code that does inlining of PA constructors. + */ +function ParallelArrayConstructFromFunction(shape, func) { + return ParallelArrayConstructFromComprehension(this, shape, func, undefined); +} + +/** + * Wrapper around |ParallelArrayConstructFromComprehension()| for the + * case where 3 arguments are supplied. + */ +function ParallelArrayConstructFromFunctionMode(shape, func, mode) { + return ParallelArrayConstructFromComprehension(this, shape, func, mode); +} + +/** + * "Comprehension form": This is the function invoked for |new + * ParallelArray(dim, fn)|. If |dim| is a number, then it creates a + * new 1-dimensional parallel array with shape |[dim]| where index |i| + * is equal to |fn(i)|. If |dim| is a vector, then it creates a new + * N-dimensional parallel array where index |a, b, ... z| is equal to + * |fn(a, b, ...z)|. + * + * The final |mode| argument is an internal argument used only + * during our unit-testing. + */ +function ParallelArrayConstructFromComprehension(self, shape, func, mode) { + // FIXME(bug 844887): Check |IsCallable(func)| + + if (typeof shape === "number") { + var length = shape >>> 0; + if (length !== shape) + ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); + ParallelArrayBuild(self, [length], func, mode); + } else if (!shape || typeof shape.length !== "number") { + ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); + } else { + var shape1 = []; + for (var i = 0, l = shape.length; i < l; i++) { + var s0 = shape[i]; + var s1 = s0 >>> 0; + if (s1 !== s0) + ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); + shape1[i] = s1; + } + ParallelArrayBuild(self, shape1, func, mode); + } +} + +/** + * Internal function used when constructing new parallel arrays. The + * NewParallelArray() intrinsic takes a ctor function which it invokes + * with the given shape, buffer, offset. The |this| parameter will be + * the newly constructed parallel array. + */ +function ParallelArrayView(shape, buffer, offset) { + this.shape = shape; + this.buffer = buffer; + this.offset = offset; + + switch (shape.length) { + case 1: this.get = ParallelArrayGet1; break; + case 2: this.get = ParallelArrayGet2; break; + case 3: this.get = ParallelArrayGet3; break; + default: this.get = ParallelArrayGetN; break; + } + + // Due to inlining of NewParallelArray, the return type of this function + // gets recorded as the return type of NewParallelArray at inlined sites, so + // we must take care to return the same thing. + return this; +} + +/** + * Helper for the comprehension form. Constructs an N-dimensional + * array where |N == shape.length|. |shape| must be an array of + * integers. The data for any given index vector |i| is determined by + * |func(...i)|. + */ +function ParallelArrayBuild(self, shape, func, mode) { + self.offset = 0; + self.shape = shape; + + var length; + var xDimension, yDimension, zDimension; + var computefunc; + + switch (shape.length) { + case 1: + length = shape[0]; + self.get = ParallelArrayGet1; + computefunc = fill1; + break; + case 2: + xDimension = shape[0]; + yDimension = shape[1]; + length = xDimension * yDimension; + self.get = ParallelArrayGet2; + computefunc = fill2; + break; + case 3: + xDimension = shape[0]; + yDimension = shape[1]; + zDimension = shape[2]; + length = xDimension * yDimension * zDimension; + self.get = ParallelArrayGet3; + computefunc = fill3; + break; + default: + length = 1; + for (var i = 0; i < shape.length; i++) + length *= shape[i]; + self.get = ParallelArrayGetN; + computefunc = fillN; + break; + } + + var buffer = self.buffer = NewDenseArray(length); + + parallel: for (;;) { + // Avoid parallel compilation if we are already nested in another + // parallel section or the user told us not to parallelize. The + // use of a for (;;) loop is working around some ion limitations: + // + // - Breaking out of named blocks does not currently work (bug 684384); + // - Unreachable Code Elim. can't properly handle if (a && b) (bug 669796) + if (ShouldForceSequential()) + break parallel; + if (!TRY_PARALLEL(mode)) + break parallel; + if (computefunc === fillN) + break parallel; + + var chunks = ComputeNumChunks(length); + var numSlices = ParallelSlices(); + var info = ComputeAllSliceBounds(chunks, numSlices); + ParallelDo(constructSlice, CheckParallel(mode)); + return; + } + + // Sequential fallback: + ASSERT_SEQUENTIAL_IS_OK(mode); + computefunc(0, length); + return; + + function constructSlice(sliceId, numSlices, warmup) { + var chunkPos = info[SLICE_POS(sliceId)]; + var chunkEnd = info[SLICE_END(sliceId)]; + + if (warmup && chunkEnd > chunkPos) + chunkEnd = chunkPos + 1; + + while (chunkPos < chunkEnd) { + var indexStart = chunkPos << CHUNK_SHIFT; + var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); + computefunc(indexStart, indexEnd); + UnsafeSetElement(info, SLICE_POS(sliceId), ++chunkPos); + } + } + + function fill1(indexStart, indexEnd) { + for (var i = indexStart; i < indexEnd; i++) + UnsafeSetElement(buffer, i, func(i)); + } + + function fill2(indexStart, indexEnd) { + var x = (indexStart / yDimension) | 0; + var y = indexStart - x * yDimension; + for (var i = indexStart; i < indexEnd; i++) { + UnsafeSetElement(buffer, i, func(x, y)); + if (++y == yDimension) { + y = 0; + ++x; + } + } + } + + function fill3(indexStart, indexEnd) { + var x = (indexStart / (yDimension * zDimension)) | 0; + var r = indexStart - x * yDimension * zDimension; + var y = (r / zDimension) | 0; + var z = r - y * zDimension; + for (var i = indexStart; i < indexEnd; i++) { + UnsafeSetElement(buffer, i, func(x, y, z)); + if (++z == zDimension) { + z = 0; + if (++y == yDimension) { + y = 0; + ++x; + } + } + } + } + + function fillN(indexStart, indexEnd) { + var indices = ComputeIndices(shape, indexStart); + for (var i = indexStart; i < indexEnd; i++) { + var result = callFunction(std_Function_apply, func, null, indices); + UnsafeSetElement(buffer, i, result); + StepIndices(shape, indices); + } + } +} + +/** + * Creates a new parallel array by applying |func(e, i, self)| for each + * element |e| with index |i|. Note that + * this always operates on the outermost dimension only. + */ +function ParallelArrayMap(func, mode) { + // FIXME(bug 844887): Check |this instanceof ParallelArray| + // FIXME(bug 844887): Check |IsCallable(func)| + + var self = this; + var length = self.shape[0]; + var buffer = NewDenseArray(length); + + parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc + if (ShouldForceSequential()) + break parallel; + if (!TRY_PARALLEL(mode)) + break parallel; + + var chunks = ComputeNumChunks(length); + var numSlices = ParallelSlices(); + var info = ComputeAllSliceBounds(chunks, numSlices); + ParallelDo(mapSlice, CheckParallel(mode)); + return NewParallelArray(ParallelArrayView, [length], buffer, 0); + } + + // Sequential fallback: + ASSERT_SEQUENTIAL_IS_OK(mode); + for (var i = 0; i < length; i++) { + // Note: Unlike JS arrays, parallel arrays cannot have holes. + buffer[i] = func(self.get(i), i, self); + } + return NewParallelArray(ParallelArrayView, [length], buffer, 0); + + function mapSlice(sliceId, numSlices, warmup) { + var chunkPos = info[SLICE_POS(sliceId)]; + var chunkEnd = info[SLICE_END(sliceId)]; + + if (warmup && chunkEnd > chunkPos + 1) + chunkEnd = chunkPos + 1; + + while (chunkPos < chunkEnd) { + var indexStart = chunkPos << CHUNK_SHIFT; + var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); + + for (var i = indexStart; i < indexEnd; i++) + UnsafeSetElement(buffer, i, func(self.get(i), i, self)); + + UnsafeSetElement(info, SLICE_POS(sliceId), ++chunkPos); + } + } +} + +/** + * Reduces the elements in a parallel array's outermost dimension + * using the given reduction function. + */ +function ParallelArrayReduce(func, mode) { + // FIXME(bug 844887): Check |this instanceof ParallelArray| + // FIXME(bug 844887): Check |IsCallable(func)| + + var self = this; + var length = self.shape[0]; + + if (length === 0) + ThrowError(JSMSG_PAR_ARRAY_REDUCE_EMPTY); + + parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc + if (ShouldForceSequential()) + break parallel; + if (!TRY_PARALLEL(mode)) + break parallel; + + var chunks = ComputeNumChunks(length); + var numSlices = ParallelSlices(); + if (chunks < numSlices) + break parallel; + + var info = ComputeAllSliceBounds(chunks, numSlices); + var subreductions = NewDenseArray(numSlices); + ParallelDo(reduceSlice, CheckParallel(mode)); + var accumulator = subreductions[0]; + for (var i = 1; i < numSlices; i++) + accumulator = func(accumulator, subreductions[i]); + return accumulator; + } + + // Sequential fallback: + ASSERT_SEQUENTIAL_IS_OK(mode); + var accumulator = self.get(0); + for (var i = 1; i < length; i++) + accumulator = func(accumulator, self.get(i)); + return accumulator; + + function reduceSlice(sliceId, numSlices, warmup) { + var chunkStart = info[SLICE_START(sliceId)]; + var chunkPos = info[SLICE_POS(sliceId)]; + var chunkEnd = info[SLICE_END(sliceId)]; + + // (*) This function is carefully designed so that the warmup + // (which executes with chunkStart === chunkPos) will execute all + // potential loads and stores. In particular, the warmup run + // processes two chunks rather than one. Moreover, it stores + // accumulator into subreductions and then loads it again to + // ensure that the load is executed during the warmup, as it will + // certainly be executed during subsequent runs. + + if (warmup && chunkEnd > chunkPos + 2) + chunkEnd = chunkPos + 2; + + if (chunkStart === chunkPos) { + var indexPos = chunkStart << CHUNK_SHIFT; + var accumulator = reduceChunk(self.get(indexPos), indexPos + 1, indexPos + CHUNK_SIZE); + + UnsafeSetElement(subreductions, sliceId, accumulator, // see (*) above + info, SLICE_POS(sliceId), ++chunkPos); + } + + var accumulator = subreductions[sliceId]; // see (*) above + + while (chunkPos < chunkEnd) { + var indexPos = chunkPos << CHUNK_SHIFT; + accumulator = reduceChunk(accumulator, indexPos, indexPos + CHUNK_SIZE); + UnsafeSetElement(subreductions, sliceId, accumulator, + info, SLICE_POS(sliceId), ++chunkPos); + } + } + + function reduceChunk(accumulator, from, to) { + to = std_Math_min(to, length); + for (var i = from; i < to; i++) + accumulator = func(accumulator, self.get(i)); + return accumulator; + } +} + +/** + * |scan()| returns an array [s_0, ..., s_N] where + * |s_i| is equal to the reduction (as per |reduce()|) + * of elements |0..i|. This is the generalization + * of partial sum. + */ +function ParallelArrayScan(func, mode) { + // FIXME(bug 844887): Check |this instanceof ParallelArray| + // FIXME(bug 844887): Check |IsCallable(func)| + + var self = this; + var length = self.shape[0]; + + if (length === 0) + ThrowError(JSMSG_PAR_ARRAY_REDUCE_EMPTY); + + var buffer = NewDenseArray(length); + + parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc + if (ShouldForceSequential()) + break parallel; + if (!TRY_PARALLEL(mode)) + break parallel; + + var chunks = ComputeNumChunks(length); + var numSlices = ParallelSlices(); + if (chunks < numSlices) + break parallel; + var info = ComputeAllSliceBounds(chunks, numSlices); + + // Scan slices individually (see comment on phase1()). + ParallelDo(phase1, CheckParallel(mode)); + + // Compute intermediates array (see comment on phase2()). + var intermediates = []; + var accumulator = intermediates[0] = buffer[finalElement(0)]; + for (var i = 1; i < numSlices - 1; i++) + accumulator = intermediates[i] = func(accumulator, buffer[finalElement(i)]); + + // Reset the current position information for each slice, but + // convert from chunks to indices (see comment on phase2()). + for (var i = 0; i < numSlices; i++) { + info[SLICE_POS(i)] = info[SLICE_START(i)] << CHUNK_SHIFT; + info[SLICE_END(i)] = info[SLICE_END(i)] << CHUNK_SHIFT; + } + info[SLICE_END(numSlices - 1)] = std_Math_min(info[SLICE_END(numSlices - 1)], length); + + // Complete each slice using intermediates array (see comment on phase2()). + ParallelDo(phase2, CheckParallel(mode)); + return NewParallelArray(ParallelArrayView, [length], buffer, 0); + } + + // Sequential fallback: + ASSERT_SEQUENTIAL_IS_OK(mode); + scan(self.get(0), 0, length); + return NewParallelArray(ParallelArrayView, [length], buffer, 0); + + function scan(accumulator, start, end) { + UnsafeSetElement(buffer, start, accumulator); + for (var i = start + 1; i < end; i++) { + accumulator = func(accumulator, self.get(i)); + UnsafeSetElement(buffer, i, accumulator); + } + return accumulator; + } + + /** + * In phase 1, we divide the source array into |numSlices| slices and + * compute scan on each slice sequentially as if it were the entire + * array. This function is responsible for computing one of those + * slices. + * + * So, if we have an array [A,B,C,D,E,F,G,H,I], |numSlices == 3|, + * and our function |func| is sum, then we would wind up computing a + * result array like: + * + * [A, A+B, A+B+C, D, D+E, D+E+F, G, G+H, G+H+I] + * ^~~~~~~~~~~~^ ^~~~~~~~~~~~^ ^~~~~~~~~~~~~^ + * Slice 0 Slice 1 Slice 2 + * + * Read on in phase2 to see what we do next! + */ + function phase1(sliceId, numSlices, warmup) { + var chunkStart = info[SLICE_START(sliceId)]; + var chunkPos = info[SLICE_POS(sliceId)]; + var chunkEnd = info[SLICE_END(sliceId)]; + + if (warmup && chunkEnd > chunkPos + 2) + chunkEnd = chunkPos + 2; + + if (chunkPos == chunkStart) { + // For the first chunk, the accumulator begins as the value in + // the input at the start of the chunk. + var indexStart = chunkPos << CHUNK_SHIFT; + var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); + scan(self.get(indexStart), indexStart, indexEnd); + UnsafeSetElement(info, SLICE_POS(sliceId), ++chunkPos); + } + + while (chunkPos < chunkEnd) { + // For each subsequent chunk, the accumulator begins as the + // combination of the final value of prev chunk and the value in + // the input at the start of this chunk. Note that this loop is + // written as simple as possible, at the cost of an extra read + // from the buffer per iteration. + var indexStart = chunkPos << CHUNK_SHIFT; + var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); + var accumulator = func(buffer[indexStart - 1], self.get(indexStart)); + scan(accumulator, indexStart, indexEnd); + UnsafeSetElement(info, SLICE_POS(sliceId), ++chunkPos); + } + } + + /** + * Computes the index of the final element computed by the slice |sliceId|. + */ + function finalElement(sliceId) { + var chunkEnd = info[SLICE_END(sliceId)]; // last chunk written by |sliceId| is endChunk - 1 + var indexStart = std_Math_min(chunkEnd << CHUNK_SHIFT, length); + return indexStart - 1; + } + + /** + * After computing the phase1 results, we compute an + * |intermediates| array. |intermediates[i]| contains the result + * of reducing the final value from each preceding slice j> |length|, Divide-Scatter-Vector seems like + // a clear win over Divide-Output-Range, since for the latter, the + // expense of redundantly scanning the |targets| will diminish the + // gain from processing |length| in parallel, while for the former, + // the total expense of building separate output buffers and the + // merging post-process is small compared to the gain from + // processing |targets| in parallel. + // + // If |targets.length| << |length|, then Divide-Output-Range seems + // like it *could* win over Divide-Scatter-Vector. (But when is + // |targets.length| << |length| or even |targets.length| < |length|? + // Seems like an odd situation and an uncommon case at best.) + // + // The unanswered question is which strategy performs better when + // |targets.length| approximately equals |length|, especially for + // special cases like collision-free scatters and permutations. + + if (targets.length >>> 0 !== targets.length) + ThrowError(JSMSG_BAD_ARRAY_LENGTH, ".prototype.scatter"); + + var targetsLength = std_Math_min(targets.length, self.length); + + if (length >>> 0 !== length) + ThrowError(JSMSG_BAD_ARRAY_LENGTH, ".prototype.scatter"); + + parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc + if (ShouldForceSequential()) + break parallel; + if (!TRY_PARALLEL(mode)) + break parallel; + + if (forceDivideScatterVector()) + return parDivideScatterVector(); + else if (forceDivideOutputRange()) + return parDivideOutputRange(); + else if (conflictFunc === undefined && targetsLength < length) + return parDivideOutputRange(); + return parDivideScatterVector(); + } + + // Sequential fallback: + ASSERT_SEQUENTIAL_IS_OK(mode); + return seq(); + + function forceDivideScatterVector() { + return mode && mode.strategy && mode.strategy == "divide-scatter-vector"; + } + + function forceDivideOutputRange() { + return mode && mode.strategy && mode.strategy == "divide-output-range"; + } + + function collide(elem1, elem2) { + if (conflictFunc === undefined) + ThrowError(JSMSG_PAR_ARRAY_SCATTER_CONFLICT); + + return conflictFunc(elem1, elem2); + } + + + function parDivideOutputRange() { + var chunks = ComputeNumChunks(targetsLength); + var numSlices = ParallelSlices(); + var checkpoints = NewDenseArray(numSlices); + for (var i = 0; i < numSlices; i++) + checkpoints[i] = 0; + + var buffer = NewDenseArray(length); + var conflicts = NewDenseArray(length); + + for (var i = 0; i < length; i++) + buffer[i] = defaultValue; + + ParallelDo(fill, CheckParallel(mode)); + return NewParallelArray(ParallelArrayView, [length], buffer, 0); + + function fill(sliceId, numSlices, warmup) { + var indexPos = checkpoints[sliceId]; + var indexEnd = targetsLength; + if (warmup) + indexEnd = std_Math_min(indexEnd, indexPos + CHUNK_SIZE); + + // Range in the output for which we are responsible: + var [outputStart, outputEnd] = ComputeSliceBounds(length, sliceId, numSlices); + + for (; indexPos < indexEnd; indexPos++) { + var x = self.get(indexPos); + var t = targets[indexPos]; + checkTarget(indexPos, t); + if (t < outputStart || t >= outputEnd) + continue; + if (conflicts[t]) + x = collide(x, buffer[t]); + UnsafeSetElement(buffer, t, x, + conflicts, t, true, + checkpoints, sliceId, indexPos + 1); + } + } + } + + function parDivideScatterVector() { + // Subtle: because we will be mutating the localBuffers and + // conflict arrays in place, we can never replay an entry in the + // target array for fear of inducing a conflict where none existed + // before. Therefore, we must proceed not by chunks but rather by + // individual indices. + var numSlices = ParallelSlices(); + var info = ComputeAllSliceBounds(targetsLength, numSlices); + + // FIXME(bug 844890): Use typed arrays here. + var localBuffers = NewDenseArray(numSlices); + for (var i = 0; i < numSlices; i++) + localBuffers[i] = NewDenseArray(length); + var localConflicts = NewDenseArray(numSlices); + for (var i = 0; i < numSlices; i++) + localConflicts[i] = NewDenseArray(length); + + // Initialize the 0th buffer, which will become the output. For + // the other buffers, we track which parts have been written to + // using the conflict buffer so they do not need to be + // initialized. + var outputBuffer = localBuffers[0]; + for (var i = 0; i < length; i++) + UnsafeSetElement(outputBuffer, i, defaultValue); + + ParallelDo(fill, CheckParallel(mode)); + mergeBuffers(); + return NewParallelArray(ParallelArrayView, [length], outputBuffer, 0); + + function fill(sliceId, numSlices, warmup) { + var indexPos = info[SLICE_POS(sliceId)]; + var indexEnd = info[SLICE_END(sliceId)]; + if (warmup) + indexEnd = std_Math_min(indexEnd, indexPos + CHUNK_SIZE); + + var localbuffer = localBuffers[sliceId]; + var conflicts = localConflicts[sliceId]; + while (indexPos < indexEnd) { + var x = self.get(indexPos); + var t = targets[indexPos]; + checkTarget(indexPos, t); + if (conflicts[t]) + x = collide(x, localbuffer[t]); + UnsafeSetElement(localbuffer, t, x, + conflicts, t, true, + info, SLICE_POS(sliceId), ++indexPos); + } + } + + /** + * Merge buffers 1..NUMSLICES into buffer 0. In principle, we could + * parallelize the merge work as well. But for this first cut, + * just do the merge sequentially. + */ + function mergeBuffers() { + var buffer = localBuffers[0]; + var conflicts = localConflicts[0]; + for (var i = 1; i < numSlices; i++) { + var otherbuffer = localBuffers[i]; + var otherconflicts = localConflicts[i]; + for (var j = 0; j < length; j++) { + if (otherconflicts[j]) { + if (conflicts[j]) { + buffer[j] = collide(otherbuffer[j], buffer[j]); + } else { + buffer[j] = otherbuffer[j]; + conflicts[j] = true; + } + } + } + } + } + } + + function seq() { + var buffer = NewDenseArray(length); + var conflicts = NewDenseArray(length); + + for (var i = 0; i < length; i++) + buffer[i] = defaultValue; + + for (var i = 0; i < targetsLength; i++) { + var x = self.get(i); + var t = targets[i]; + checkTarget(i, t); + if (conflicts[t]) + x = collide(x, buffer[t]); + + UnsafeSetElement(buffer, t, x, + conflicts, t, true); + } + + return NewParallelArray(ParallelArrayView, [length], buffer, 0); + } + + function checkTarget(i, t) { + if ((t | 0) !== t) + ThrowError(JSMSG_PAR_ARRAY_SCATTER_BAD_TARGET, i); + + if (t >= length) + ThrowError(JSMSG_PAR_ARRAY_SCATTER_BOUNDS); + } +} + +/** + * The familiar filter() operation applied across the outermost + * dimension. + */ +function ParallelArrayFilter(func, mode) { + // FIXME(bug 844887): Check |this instanceof ParallelArray| + // FIXME(bug 844887): Check |IsCallable(func)| + + var self = this; + var length = self.shape[0]; + + parallel: for (;;) { // see ParallelArrayBuild() to explain why for(;;) etc + if (ShouldForceSequential()) + break parallel; + if (!TRY_PARALLEL(mode)) + break parallel; + + var chunks = ComputeNumChunks(length); + var numSlices = ParallelSlices(); + if (chunks < numSlices * 2) + break parallel; + + var info = ComputeAllSliceBounds(chunks, numSlices); + + // Step 1. Compute which items from each slice of the result + // buffer should be preserved. When we're done, we have an array + // |survivors| containing a bitset for each chunk, indicating + // which members of the chunk survived. We also keep an array + // |counts| containing the total number of items that are being + // preserved from within one slice. + // + // FIXME(bug 844890): Use typed arrays here. + var counts = NewDenseArray(numSlices); + for (var i = 0; i < numSlices; i++) + counts[i] = 0; + var survivors = NewDenseArray(chunks); + ParallelDo(findSurvivorsInSlice, CheckParallel(mode)); + + // Step 2. Compress the slices into one contiguous set. + var count = 0; + for (var i = 0; i < numSlices; i++) + count += counts[i]; + var buffer = NewDenseArray(count); + if (count > 0) + ParallelDo(copySurvivorsInSlice, CheckParallel(mode)); + + return NewParallelArray(ParallelArrayView, [count], buffer, 0); + } + + // Sequential fallback: + ASSERT_SEQUENTIAL_IS_OK(mode); + var buffer = [], count = 0; + for (var i = 0; i < length; i++) { + var elem = self.get(i); + if (func(elem, i, self)) + buffer[count++] = elem; + } + return NewParallelArray(ParallelArrayView, [count], buffer, 0); + + /** + * As described above, our goal is to determine which items we + * will preserve from a given slice. We do this one chunk at a + * time. When we finish a chunk, we record our current count and + * the next chunk sliceId, lest we should bail. + */ + function findSurvivorsInSlice(sliceId, numSlices, warmup) { + + var chunkPos = info[SLICE_POS(sliceId)]; + var chunkEnd = info[SLICE_END(sliceId)]; + + if (warmup && chunkEnd > chunkPos) + chunkEnd = chunkPos + 1; + + var count = counts[sliceId]; + while (chunkPos < chunkEnd) { + var indexStart = chunkPos << CHUNK_SHIFT; + var indexEnd = std_Math_min(indexStart + CHUNK_SIZE, length); + var chunkBits = 0; + + for (var bit = 0; indexStart + bit < indexEnd; bit++) { + var keep = !!func(self.get(indexStart + bit), indexStart + bit, self); + chunkBits |= keep << bit; + count += keep; + } + + UnsafeSetElement(survivors, chunkPos, chunkBits, + counts, sliceId, count, + info, SLICE_POS(sliceId), ++chunkPos); + } + } + + function copySurvivorsInSlice(sliceId, numSlices, warmup) { + // Copies the survivors from this slice into the correct position. + // Note that this is an idempotent operation that does not invoke + // user code. Therefore, we don't expect bailouts and make an + // effort to proceed chunk by chunk or avoid duplicating work. + + // During warmup, we only execute with sliceId 0. This would fail to + // execute the loop below. Therefore, during warmup, we + // substitute 1 for the sliceId. + if (warmup && sliceId == 0 && numSlices != 1) + sliceId = 1; + + // Total up the items preserved by previous slices. + var count = 0; + if (sliceId > 0) { // FIXME(#819219)---work around a bug in Ion's range checks + for (var i = 0; i < sliceId; i++) + count += counts[i]; + } + + // Compute the final index we expect to write. + var total = count + counts[sliceId]; + if (count == total) + return; + + // Iterate over the chunks assigned to us. Read the bitset for + // each chunk. Copy values where a 1 appears until we have + // written all the values that we expect to. We can just iterate + // from 0...CHUNK_SIZE without fear of a truncated final chunk + // because we are already checking for when count==total. + var chunkStart = info[SLICE_START(sliceId)]; + var chunkEnd = info[SLICE_END(sliceId)]; + for (var chunk = chunkStart; chunk < chunkEnd; chunk++) { + var chunkBits = survivors[chunk]; + if (!chunkBits) + continue; + + var indexStart = chunk << CHUNK_SHIFT; + for (var i = 0; i < CHUNK_SIZE; i++) { + if (chunkBits & (1 << i)) { + UnsafeSetElement(buffer, count++, self.get(indexStart + i)); + if (count == total) + break; + } + } + } + } +} + +/** + * Divides the outermost dimension into two dimensions. Does not copy + * or affect the underlying data, just how it is divided amongst + * dimensions. So if we had a vector with shape [N, ...] and you + * partition with amount=4, you get a [N/4, 4, ...] vector. Note that + * N must be evenly divisible by 4 in that case. + */ +function ParallelArrayPartition(amount) { + if (amount >>> 0 !== amount) + ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, ""); + + var length = this.shape[0]; + var partitions = (length / amount) | 0; + + if (partitions * amount !== length) + ThrowError(JSMSG_PAR_ARRAY_BAD_PARTITION); + + var shape = [partitions, amount]; + for (var i = 1; i < this.shape.length; i++) + ARRAY_PUSH(shape, this.shape[i]); + return NewParallelArray(ParallelArrayView, shape, this.buffer, this.offset); +} + +/** + * Collapses two outermost dimensions into one. So if you had + * a [X, Y, ...] vector, you get a [X*Y, ...] vector. + */ +function ParallelArrayFlatten() { + if (this.shape.length < 2) + ThrowError(JSMSG_PAR_ARRAY_ALREADY_FLAT); + + var shape = [this.shape[0] * this.shape[1]]; + for (var i = 2; i < this.shape.length; i++) + ARRAY_PUSH(shape, this.shape[i]); + return NewParallelArray(ParallelArrayView, shape, this.buffer, this.offset); +} + +// +// Accessors and utilities. +// + +/** + * Specialized variant of get() for one-dimensional case + */ +function ParallelArrayGet1(i) { + if (i === undefined) + return undefined; + return this.buffer[this.offset + i]; +} + +/** + * Specialized variant of get() for two-dimensional case + */ +function ParallelArrayGet2(x, y) { + var xDimension = this.shape[0]; + var yDimension = this.shape[1]; + if (x === undefined) + return undefined; + if (x >= xDimension) + return undefined; + if (y === undefined) + return NewParallelArray(ParallelArrayView, [yDimension], this.buffer, this.offset + x * yDimension); + if (y >= yDimension) + return undefined; + var offset = y + x * yDimension; + return this.buffer[this.offset + offset]; +} + +/** + * Specialized variant of get() for three-dimensional case + */ +function ParallelArrayGet3(x, y, z) { + var xDimension = this.shape[0]; + var yDimension = this.shape[1]; + var zDimension = this.shape[2]; + if (x === undefined) + return undefined; + if (x >= xDimension) + return undefined; + if (y === undefined) + return NewParallelArray(ParallelArrayView, [yDimension, zDimension], + this.buffer, this.offset + x * yDimension * zDimension); + if (y >= yDimension) + return undefined; + if (z === undefined) + return NewParallelArray(ParallelArrayView, [zDimension], + this.buffer, this.offset + y * zDimension + x * yDimension * zDimension); + if (z >= zDimension) + return undefined; + var offset = z + y*zDimension + x * yDimension * zDimension; + return this.buffer[this.offset + offset]; +} + +/** + * Generalized version of get() for N-dimensional case + */ +function ParallelArrayGetN(...coords) { + if (coords.length == 0) + return undefined; + + var products = ComputeProducts(this.shape); + + // Compute the offset of the given coordinates. Each index is + // multipled by its corresponding entry in the |products| + // array, counting in reverse. So if |coords| is [a,b,c,d], + // then you get |a*BCD + b*CD + c*D + d|. + var offset = this.offset; + var sDimensionality = this.shape.length; + var cDimensionality = coords.length; + for (var i = 0; i < cDimensionality; i++) { + if (coords[i] >= this.shape[i]) + return undefined; + offset += coords[i] * products[sDimensionality - i - 1]; + } + + if (cDimensionality < sDimensionality) { + var shape = callFunction(std_Array_slice, this.shape, cDimensionality); + return NewParallelArray(ParallelArrayView, shape, this.buffer, offset); + } + return this.buffer[offset]; +} + +/** The length property yields the outermost dimension */ +function ParallelArrayLength() { + return this.shape[0]; +} + +function ParallelArrayToString() { + var l = this.length; + if (l == 0) + return ""; + + var open, close; + if (this.shape.length > 1) { + open = "<"; + close = ">"; + } else { + open = close = ""; + } + + var result = ""; + for (var i = 0; i < l - 1; i++) { + result += open + String(this.get(i)) + close; + result += ","; + } + result += open + String(this.get(l - 1)) + close; + return result; +} + +/** + * Internal debugging tool: checks that the given `mode` permits + * sequential execution + */ +function AssertSequentialIsOK(mode) { + if (mode && mode.mode !== "seq") + ThrowError(JSMSG_WRONG_VALUE, "par", "seq"); +} + +/** + * Internal debugging tool: returns a function to be supplied to + * ParallelDo() that will check that the parallel results + * bailout/succeed as expected. Returns null if no mode is supplied + * or we are building with some strange IF_DEF configuration such that + * we don't expect parallel execution to work. + */ +function CheckParallel(mode) { + if (!mode || !ParallelTestsShouldPass()) + return null; + + return function(bailouts) { + if (!("expect" in mode) || mode.expect === "any") { + return; // Ignore result when unspecified or unimportant. + } + + var result; + if (bailouts === 0) + result = "success"; + else if (bailouts === global.Infinity) + result = "disqualified"; + else + result = "bailout"; + + if (mode.expect === "mixed") { + if (result === "disqualified") + ThrowError(JSMSG_WRONG_VALUE, mode.expect, result); + } else if (result !== mode.expect) { + ThrowError(JSMSG_WRONG_VALUE, mode.expect, result); + } + }; +} + +/* + * Mark the main operations as clone-at-callsite for better precision. + * This is slightly overkill, as all that we really need is to + * specialize to the receiver and the elemental function, but in + * practice this is likely not so different, since element functions + * are often used in exactly one place. + */ +SetScriptHints(ParallelArrayConstructEmpty, { cloneAtCallsite: true }); +SetScriptHints(ParallelArrayConstructFromArray, { cloneAtCallsite: true }); +SetScriptHints(ParallelArrayConstructFromFunction, { cloneAtCallsite: true }); +SetScriptHints(ParallelArrayConstructFromFunctionMode, { cloneAtCallsite: true }); +SetScriptHints(ParallelArrayConstructFromComprehension, { cloneAtCallsite: true }); +SetScriptHints(ParallelArrayView, { cloneAtCallsite: true }); +SetScriptHints(ParallelArrayBuild, { cloneAtCallsite: true }); +SetScriptHints(ParallelArrayMap, { cloneAtCallsite: true }); +SetScriptHints(ParallelArrayReduce, { cloneAtCallsite: true }); +SetScriptHints(ParallelArrayScan, { cloneAtCallsite: true }); +SetScriptHints(ParallelArrayScatter, { cloneAtCallsite: true }); +SetScriptHints(ParallelArrayFilter, { cloneAtCallsite: true }); + +/* + * Mark the common getters as clone-at-callsite and inline. This is + * overkill as we should only clone per receiver, but we have no + * mechanism for that right now. Bug 804767 might permit another + * alternative by specializing the inlined gets. + */ +SetScriptHints(ParallelArrayGet1, { cloneAtCallsite: true, inline: true }); +SetScriptHints(ParallelArrayGet2, { cloneAtCallsite: true, inline: true }); +SetScriptHints(ParallelArrayGet3, { cloneAtCallsite: true, inline: true }); diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js index fa1bb98a085..2094783849f 100644 --- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -32,6 +32,7 @@ var std_Boolean_toString = Boolean.prototype.toString; var Std_Date = Date; var std_Date_now = Date.now; var std_Function_bind = Function.prototype.bind; +var std_Function_apply = Function.prototype.apply; var std_Math_floor = Math.floor; var std_Math_max = Math.max; var std_Math_min = Math.min; diff --git a/js/src/builtin/js2c.py b/js/src/builtin/js2c.py index e3fe1ed9850..1cd3d66382a 100644 --- a/js/src/builtin/js2c.py +++ b/js/src/builtin/js2c.py @@ -278,8 +278,9 @@ def JS2C(source, target, env): lines = ExpandConstants(lines, consts) lines = ExpandMacros(lines, macros) Validate(lines, filename) - if not env['DEBUG']: - lines = minifier.JSMinify(lines) + # FIXME #824112 + #if not env['DEBUG']: + # lines = minifier.JSMinify(lines) id = (os.path.split(filename)[1])[:-3] if debugger: id = id[:-9] raw_length = len(lines) diff --git a/js/src/builtin/macros.py b/js/src/builtin/macros.py index 08fa82e686d..50d50228f26 100644 --- a/js/src/builtin/macros.py +++ b/js/src/builtin/macros.py @@ -214,6 +214,26 @@ macro OVERRIDE_SUBJECT(override) = ((override)[(override).length - 1]); # 1-based so index of 1 returns the first capture macro OVERRIDE_CAPTURE(override, index) = ((override)[(index)]); +# The mode asserts options object for ParallelArray +macro TRY_PARALLEL(MODE) = ((!MODE || MODE.mode === "par")); +macro ASSERT_SEQUENTIAL_IS_OK(MODE) = do { if (MODE) AssertSequentialIsOK(MODE) } while(false); + +# How many items at a time do we do recomp. for parallel execution. +# Note that filter currently assumes that this is no greater than 32 +# in order to make use of a bitset. +const CHUNK_SHIFT = 5; +const CHUNK_SIZE = 32; + +# Slice array: see ComputeAllSliceBounds() in ParallelArray.js +macro SLICE_INFO(START, END) = START, END, START, 0; +macro SLICE_START(ID) = ((ID << 2) + 0); +macro SLICE_END(ID) = ((ID << 2) + 1); +macro SLICE_POS(ID) = ((ID << 2) + 2); + +# Safe version of ARRAY.push(ELEMENT) +macro ARRAY_PUSH(ARRAY, ELEMENT) = callFunction(std_Array_push, ARRAY, ELEMENT); +macro ARRAY_SLICE(ARRAY, ELEMENT) = callFunction(std_Array_slice, ARRAY, ELEMENT); + # PropertyDescriptor return value indices - must match # PropertyDescriptorIndices in runtime.cc. const IS_ACCESSOR_INDEX = 0; diff --git a/js/src/ion/CodeGenerator.cpp b/js/src/ion/CodeGenerator.cpp index a91578e06aa..b689070db30 100644 --- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -2190,6 +2190,50 @@ CodeGenerator::generateBody() return true; } +// Out-of-line object allocation for LNewParallelArray. +class OutOfLineNewParallelArray : public OutOfLineCodeBase +{ + LNewParallelArray *lir_; + + public: + OutOfLineNewParallelArray(LNewParallelArray *lir) + : lir_(lir) + { } + + bool accept(CodeGenerator *codegen) { + return codegen->visitOutOfLineNewParallelArray(this); + } + + LNewParallelArray *lir() const { + return lir_; + } +}; + +typedef JSObject *(*NewInitParallelArrayFn)(JSContext *, HandleObject); +static const VMFunction NewInitParallelArrayInfo = + FunctionInfo(NewInitParallelArray); + +bool +CodeGenerator::visitNewParallelArrayVMCall(LNewParallelArray *lir) +{ + JS_ASSERT(gen->info().executionMode() == SequentialExecution); + + Register objReg = ToRegister(lir->output()); + + JS_ASSERT(!lir->isCall()); + saveLive(lir); + + pushArg(ImmGCPtr(lir->mir()->templateObject())); + if (!callVM(NewInitParallelArrayInfo, lir)) + return false; + + if (ReturnReg != objReg) + masm.movePtr(ReturnReg, objReg); + + restoreLive(lir); + return true; +} + // Out-of-line object allocation for LNewArray. class OutOfLineNewArray : public OutOfLineCodeBase { @@ -2263,6 +2307,32 @@ CodeGenerator::visitNewSlots(LNewSlots *lir) return true; } +bool +CodeGenerator::visitNewParallelArray(LNewParallelArray *lir) +{ + Register objReg = ToRegister(lir->output()); + JSObject *templateObject = lir->mir()->templateObject(); + + OutOfLineNewParallelArray *ool = new OutOfLineNewParallelArray(lir); + if (!addOutOfLineCode(ool)) + return false; + + masm.newGCThing(objReg, templateObject, ool->entry()); + masm.initGCThing(objReg, templateObject); + + masm.bind(ool->rejoin()); + return true; +} + +bool +CodeGenerator::visitOutOfLineNewParallelArray(OutOfLineNewParallelArray *ool) +{ + if (!visitNewParallelArrayVMCall(ool->lir())) + return false; + masm.jump(ool->rejoin()); + return true; +} + bool CodeGenerator::visitNewArray(LNewArray *lir) { diff --git a/js/src/ion/CodeGenerator.h b/js/src/ion/CodeGenerator.h index a7090ce8841..387ea2041bc 100644 --- a/js/src/ion/CodeGenerator.h +++ b/js/src/ion/CodeGenerator.h @@ -98,6 +98,8 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitCallDirectEval(LCallDirectEval *lir); bool visitDoubleToInt32(LDoubleToInt32 *lir); bool visitNewSlots(LNewSlots *lir); + bool visitNewParallelArrayVMCall(LNewParallelArray *lir); + bool visitNewParallelArray(LNewParallelArray *lir); bool visitOutOfLineNewParallelArray(OutOfLineNewParallelArray *ool); bool visitNewArrayCallVM(LNewArray *lir); bool visitNewArray(LNewArray *lir); diff --git a/js/src/ion/IonBuilder.h b/js/src/ion/IonBuilder.h index c86eb460f4a..9abbaa5f2c3 100644 --- a/js/src/ion/IonBuilder.h +++ b/js/src/ion/IonBuilder.h @@ -445,6 +445,13 @@ class IonBuilder : public MIRGenerator InliningStatus inlineNewDenseArray(CallInfo &callInfo); InliningStatus inlineNewDenseArrayForSequentialExecution(CallInfo &callInfo); InliningStatus inlineNewDenseArrayForParallelExecution(CallInfo &callInfo); + InliningStatus inlineNewParallelArray(CallInfo &callInfo); + InliningStatus inlineParallelArray(CallInfo &callInfo); + InliningStatus inlineParallelArrayTail(CallInfo &callInfo, + HandleFunction target, + MDefinition *ctor, + types::StackTypeSet *ctorTypes, + uint32_t discards); InliningStatus inlineThrowError(CallInfo &callInfo); InliningStatus inlineDump(CallInfo &callInfo); diff --git a/js/src/ion/MCallOptimize.cpp b/js/src/ion/MCallOptimize.cpp index 929fda806c1..0038aba4fd5 100644 --- a/js/src/ion/MCallOptimize.cpp +++ b/js/src/ion/MCallOptimize.cpp @@ -101,6 +101,10 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native) return inlineShouldForceSequentialOrInParallelSection(callInfo); if (native == testingFunc_inParallelSection) return inlineShouldForceSequentialOrInParallelSection(callInfo); + if (native == intrinsic_NewParallelArray) + return inlineNewParallelArray(callInfo); + if (native == ParallelArrayObject::construct) + return inlineParallelArray(callInfo); if (native == intrinsic_NewDenseArray) return inlineNewDenseArray(callInfo); @@ -1058,6 +1062,152 @@ IonBuilder::inlineShouldForceSequentialOrInParallelSection(CallInfo &callInfo) JS_NOT_REACHED("Invalid execution mode"); } +IonBuilder::InliningStatus +IonBuilder::inlineNewParallelArray(CallInfo &callInfo) +{ + // Rewrites a call like + // + // NewParallelArray(ParallelArrayView, arg0, ..., argN) + // + // to + // + // x = MNewParallelArray() + // ParallelArrayView(x, arg0, ..., argN) + + uint32_t argc = callInfo.argc(); + if (argc < 1 || callInfo.constructing()) + return InliningStatus_NotInlined; + + types::StackTypeSet *ctorTypes = getInlineArgTypeSet(callInfo, 0); + RawObject targetObj = ctorTypes->getSingleton(); + RootedFunction target(cx); + if (targetObj && targetObj->isFunction()) + target = targetObj->toFunction(); + if (target && target->isInterpreted() && target->nonLazyScript()->shouldCloneAtCallsite) { + RootedScript scriptRoot(cx, script()); + target = CloneFunctionAtCallsite(cx, target, scriptRoot, pc); + if (!target) + return InliningStatus_Error; + } + MDefinition *ctor = makeCallsiteClone( + target, + callInfo.getArg(0)->toPassArg()->getArgument()); + + // Discard the function. + return inlineParallelArrayTail(callInfo, target, ctor, + target ? NULL : ctorTypes, 1); +} + +IonBuilder::InliningStatus +IonBuilder::inlineParallelArray(CallInfo &callInfo) +{ + if (!callInfo.constructing()) + return InliningStatus_NotInlined; + + uint32_t argc = callInfo.argc(); + RootedFunction target(cx, ParallelArrayObject::getConstructor(cx, argc)); + if (!target) + return InliningStatus_Error; + + JS_ASSERT(target->nonLazyScript()->shouldCloneAtCallsite); + RootedScript script(cx, script_); + target = CloneFunctionAtCallsite(cx, target, script, pc); + if (!target) + return InliningStatus_Error; + + MConstant *ctor = MConstant::New(ObjectValue(*target)); + current->add(ctor); + + return inlineParallelArrayTail(callInfo, target, ctor, NULL, 0); +} + +IonBuilder::InliningStatus +IonBuilder::inlineParallelArrayTail(CallInfo &callInfo, + HandleFunction target, + MDefinition *ctor, + types::StackTypeSet *ctorTypes, + uint32_t discards) +{ + // Rewrites either NewParallelArray(...) or new ParallelArray(...) from a + // call to a native ctor into a call to the relevant function in the + // self-hosted code. + + uint32_t argc = callInfo.argc() - discards; + + // Create the new parallel array object. Parallel arrays have specially + // constructed type objects, so we can only perform the inlining if we + // already have one of these type objects. + types::StackTypeSet *returnTypes = getInlineReturnTypeSet(); + if (returnTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT) + return InliningStatus_NotInlined; + if (returnTypes->getObjectCount() != 1) + return InliningStatus_NotInlined; + types::TypeObject *typeObject = returnTypes->getTypeObject(0); + + // Create the call and add in the non-this arguments. + uint32_t targetArgs = argc; + if (target && !target->isNative()) + targetArgs = Max(target->nargs, argc); + + MCall *call = MCall::New(target, targetArgs + 1, argc, false, ctorTypes); + if (!call) + return InliningStatus_Error; + + callInfo.unwrapArgs(); + + // Explicitly pad any missing arguments with |undefined|. + // This permits skipping the argumentsRectifier. + for (int32_t i = targetArgs; i > (int)argc; i--) { + JS_ASSERT_IF(target, !target->isNative()); + MConstant *undef = MConstant::New(UndefinedValue()); + current->add(undef); + MPassArg *pass = MPassArg::New(undef); + current->add(pass); + call->addArg(i, pass); + } + + MPassArg *oldThis = MPassArg::New(callInfo.thisArg()); + current->add(oldThis); + + // Add explicit arguments. + // Skip addArg(0) because it is reserved for this + for (int32_t i = 0; i < argc; i++) { + MDefinition *arg = callInfo.getArg(i + discards); + MPassArg *passArg = MPassArg::New(arg); + current->add(passArg); + call->addArg(i + 1, passArg); + } + + // Place an MPrepareCall before the first passed argument, before we + // potentially perform rearrangement. + MPrepareCall *start = new MPrepareCall; + oldThis->block()->insertBefore(oldThis, start); + call->initPrepareCall(start); + + // Create the MIR to allocate the new parallel array. Take the type + // object is taken from the prediction set. + RootedObject templateObject(cx, ParallelArrayObject::newInstance(cx)); + if (!templateObject) + return InliningStatus_Error; + templateObject->setType(typeObject); + MNewParallelArray *newObject = MNewParallelArray::New(templateObject); + current->add(newObject); + MPassArg *newThis = MPassArg::New(newObject); + current->add(newThis); + call->addArg(0, newThis); + + // Set the new callee. + call->initFunction(ctor); + + current->add(call); + current->push(newObject); + + if (!resumeAfter(call)) + return InliningStatus_Error; + + return InliningStatus_Inlined; +} + IonBuilder::InliningStatus IonBuilder::inlineNewDenseArray(CallInfo &callInfo) { diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index 2026869e79e..115f1a376c4 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -1077,6 +1077,10 @@ class MNewParallelArray : public MNullaryInstruction return new MNewParallelArray(templateObject); } + AliasSet getAliasSet() const { + return AliasSet::None(); + } + JSObject *templateObject() const { return templateObject_; } diff --git a/js/src/ion/VMFunctions.cpp b/js/src/ion/VMFunctions.cpp index f4df8d9d053..a388f31f7dc 100644 --- a/js/src/ion/VMFunctions.cpp +++ b/js/src/ion/VMFunctions.cpp @@ -269,6 +269,21 @@ IteratorMore(JSContext *cx, HandleObject obj, JSBool *res) return true; } +JSObject * +NewInitParallelArray(JSContext *cx, HandleObject templateObject) +{ + JS_ASSERT(templateObject->getClass() == &ParallelArrayObject::class_); + JS_ASSERT(!templateObject->hasSingletonType()); + + RootedObject obj(cx, ParallelArrayObject::newInstance(cx)); + if (!obj) + return NULL; + + obj->setType(templateObject->type()); + + return obj; +} + JSObject* NewInitArray(JSContext *cx, uint32_t count, types::TypeObject *typeArg) { diff --git a/js/src/ion/VMFunctions.h b/js/src/ion/VMFunctions.h index c079f28695a..23c49e1b94d 100644 --- a/js/src/ion/VMFunctions.h +++ b/js/src/ion/VMFunctions.h @@ -436,7 +436,8 @@ JSBool ObjectEmulatesUndefined(RawObject obj); bool IteratorMore(JSContext *cx, HandleObject obj, JSBool *res); -// Allocation functions for JSOP_NEWARRAY and JSOP_NEWOBJECT +// Allocation functions for JSOP_NEWARRAY and JSOP_NEWOBJECT and parallel array inlining +JSObject *NewInitParallelArray(JSContext *cx, HandleObject templateObj); JSObject *NewInitArray(JSContext *cx, uint32_t count, types::TypeObject *type); JSObject *NewInitObject(JSContext *cx, HandleObject templateObject); diff --git a/js/src/jit-test/lib/parallelarray-helpers.js b/js/src/jit-test/lib/parallelarray-helpers.js index df76f379760..841440433c9 100644 --- a/js/src/jit-test/lib/parallelarray-helpers.js +++ b/js/src/jit-test/lib/parallelarray-helpers.js @@ -2,7 +2,118 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -load(libdir + "eqArrayHelper.js"); +// Explanation of minItemsTestingThreshold: +// +// If the volume of input items in a test is small, then all of them +// may be processed during warmup alone, and the parallel-invocation +// will trivially succeed even if we are intentionally trying to +// detect a failure. +// +// The maximum number of items processed by sequential warmups for +// ParallelArrayBuild is: +// maxSeqItems = maxBailouts * numSlices * CHUNK_SIZE +// +// For maxBailouts = 3, maxSeqItems == 3 * 8 * 32 == 768 +// For maxBailouts = 5, maxSeqItems == 5 * 8 * 32 == 1280 +// +// Our test code does not have access to the values of these constants +// (maxBailouts, numSlices, CHUNK_SIZE). Therefore, the value of +// minItemsTestingThreshold should be kept in sync with some value +// greater than maxSeqItems as calculated above. +// +// This is still imperfect since it assumes numSlices <= 8, but +// numSlices is machine-dependent. +// (TODO: consider exposing numSlices via builtin/TestingFunctions.cpp) + +var minItemsTestingThreshold = 1024; + +function build(n, f) { + var result = []; + for (var i = 0; i < n; i++) + result.push(f(i)); + return result; +} + +function range(n, m) { + // Returns an array with [n..m] (include on n, exclusive on m) + + var result = []; + for (var i = n; i < m; i++) + result.push(i); + return result; +} + +function seq_scan(array, f) { + // Simple sequential version of scan() that operates over an array + + var result = []; + result[0] = array[0]; + for (var i = 1; i < array.length; i++) { + result[i] = f(result[i-1], array[i]); + } + return result; +} + +function assertAlmostEq(v1, v2) { + if (v1 === v2) + return true; + // + and other fp ops can vary somewhat when run in parallel! + assertEq(typeof v1, "number"); + assertEq(typeof v2, "number"); + var diff = Math.abs(v1 - v2); + var percent = diff / v1 * 100.0; + print("v1 = " + v1); + print("v2 = " + v2); + print("% diff = " + percent); + assertEq(percent < 1e-10, true); // off by an less than 1e-10%...good enough. +} + +function assertStructuralEq(e1, e2) { + if (e1 instanceof ParallelArray && e2 instanceof ParallelArray) { + assertEqParallelArray(e1, e2); + } else if (e1 instanceof Array && e2 instanceof ParallelArray) { + assertEqParallelArrayArray(e2, e1); + } else if (e1 instanceof ParallelArray && e2 instanceof Array) { + assertEqParallelArrayArray(e1, e2); + } else if (e1 instanceof Array && e2 instanceof Array) { + assertEqArray(e1, e2); + } else if (e1 instanceof Object && e2 instanceof Object) { + assertEq(e1.__proto__, e2.__proto__); + for (prop in e1) { + if (e1.hasOwnProperty(prop)) { + assertEq(e2.hasOwnProperty(prop), true); + assertStructuralEq(e1[prop], e2[prop]); + } + } + } else { + assertEq(e1, e2); + } +} + +function assertEqParallelArrayArray(a, b) { + assertEq(a.shape.length, 1); + assertEq(a.length, b.length); + for (var i = 0, l = a.length; i < l; i++) { + try { + assertStructuralEq(a.get(i), b[i]); + } catch (e) { + print("...in index ", i, " of ", l); + throw e; + } + } +} + +function assertEqArray(a, b) { + assertEq(a.length, b.length); + for (var i = 0, l = a.length; i < l; i++) { + try { + assertStructuralEq(a[i], b[i]); + } catch (e) { + print("...in index ", i, " of ", l); + throw e; + } + } +} function assertEqParallelArray(a, b) { assertEq(a instanceof ParallelArray, true); @@ -24,29 +135,89 @@ function assertEqParallelArray(a, b) { var iv = shape.map(function () { return 0; }); do { - var e1 = a.get(iv); - var e2 = b.get(iv); - if (e1 instanceof ParallelArray && e2 instanceof ParallelArray) - assertEqParallelArray(e1, e2); - else if (e1 instanceof Array && e2 instanceof Array) - assertEqArray(e1, e2); - else - assertEq(e1, e2); + try { + var e1 = a.get.apply(a, iv); + var e2 = b.get.apply(b, iv); + assertStructuralEq(e1, e2); + } catch (e) { + print("...in indices ", iv, " of ", shape); + throw e; + } } while (bump(iv)); } -function assertParallelArrayModesCommute(modes, pa, op) { - var args = Array.slice(arguments, 3); - var acc; +function assertParallelArrayModesEq(modes, acc, opFunction, cmpFunction) { + if (!cmpFunction) { cmpFunction = assertStructuralEq; } modes.forEach(function (mode) { - var result = op.apply(pa, args.concat([{ mode: mode, expect: "success" }])); - if (acc) { - if (acc instanceof ParallelArray) - assertEqParallelArray(acc, result); - else - assertEq(acc, result); - } else { - acc = result; - } + var result = opFunction({ mode: mode, expect: "success" }); + cmpFunction(acc, result); }); } + +function assertParallelArrayModesCommute(modes, opFunction) { + var acc = opFunction({ mode: modes[0], expect: "success" }); + assertParallelArrayModesEq(modes.slice(1), acc, opFunction); +} + +function comparePerformance(opts) { + var measurements = []; + for (var i = 0; i < opts.length; i++) { + var start = new Date(); + opts[i].func(); + var end = new Date(); + var diff = (end.getTime() - start.getTime()); + measurements.push(diff); + print("Option " + opts[i].name + " took " + diff + "ms"); + } + + for (var i = 1; i < opts.length; i++) { + var rel = (measurements[i] - measurements[0]) * 100 / measurements[0]; + print("Option " + opts[i].name + " relative to option " + + opts[0].name + ": " + (rel|0) + "%"); + } +} + +function compareAgainstArray(jsarray, opname, func, cmpFunction) { + var expected = jsarray[opname].apply(jsarray, [func]); + var parray = new ParallelArray(jsarray); + + // Unfortunately, it sometimes happens that running 'par' twice in a + // row causes bailouts and other unfortunate things! + + assertParallelArrayModesEq(["seq", "par", "par"], expected, function(m) { + print(m.mode + " " + m.expect); + var result = parray[opname].apply(parray, [func, m]); + // print(result.toString()); + return result; + }, cmpFunction); +} + +function testFilter(jsarray, func, cmpFunction) { + compareAgainstArray(jsarray, "filter", func, cmpFunction); + + // var expected = jsarray.filter(func); + // var filters = jsarray.map(func); + // var parray = new ParallelArray(jsarray); + // + // // Unfortunately, it sometimes happens that running 'par' twice in a + // // row causes bailouts and other unfortunate things! + // + // assertParallelArrayModesEq(["seq", "par", "par"], expected, function(m) { + // print(m.mode + " " + m.expect); + // return parray.filter(filters, m); + // }, cmpFunction); +} + +function testScan(jsarray, func, cmpFunction) { + var expected = seq_scan(jsarray, func); + var parray = new ParallelArray(jsarray); + + // Unfortunately, it sometimes happens that running 'par' twice in a + // row causes bailouts and other unfortunate things! + + assertParallelArrayModesEq(["seq", "par", "par"], expected, function(m) { + print(m.mode + " " + m.expect); + var p = parray.scan(func, m); + return p; + }, cmpFunction); +} diff --git a/js/src/jit-test/tests/auto-regress/bug755564.js b/js/src/jit-test/tests/auto-regress/bug755564.js index 267bf6aa91c..5f1331f8230 100644 --- a/js/src/jit-test/tests/auto-regress/bug755564.js +++ b/js/src/jit-test/tests/auto-regress/bug755564.js @@ -1,5 +1,3 @@ -// |jit-test| error:Error - // Binary: cache/js-dbg-64-50177d59c0e1-linux // Flags: // diff --git a/js/src/jit-test/tests/auto-regress/bug784011.js b/js/src/jit-test/tests/auto-regress/bug784011.js index cfad6c3d81f..e905620f846 100644 --- a/js/src/jit-test/tests/auto-regress/bug784011.js +++ b/js/src/jit-test/tests/auto-regress/bug784011.js @@ -1,5 +1,3 @@ -// |jit-test| error:TypeError - // Binary: cache/js-dbg-64-c676b554c7bb-linux // Flags: // diff --git a/js/src/jit-test/tests/auto-regress/bug789107.js b/js/src/jit-test/tests/auto-regress/bug789107.js index 435054e6e89..23b9ba165cf 100644 --- a/js/src/jit-test/tests/auto-regress/bug789107.js +++ b/js/src/jit-test/tests/auto-regress/bug789107.js @@ -1,5 +1,3 @@ -// |jit-test| error:TypeError - // Binary: cache/js-dbg-64-5d63594c05a9-linux // Flags: // diff --git a/js/src/jit-test/tests/parallelarray/alloc-array.js b/js/src/jit-test/tests/parallelarray/alloc-array.js new file mode 100644 index 00000000000..e6fa286102c --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/alloc-array.js @@ -0,0 +1,32 @@ +load(libdir + "parallelarray-helpers.js"); + +function buildSimple() { + + assertParallelArrayModesCommute(["seq", "par"], function(m) { + return new ParallelArray([256], function(i) { + return [i, i+1, i+2, i+3]; + }, m); + }); + + assertParallelArrayModesCommute(["seq", "par"], function(m) { + return new ParallelArray([256], function(i) { + var x = []; + for (var i = 0; i < 4; i++) { + x[i] = i; + } + return x; + }, m); + }); + + assertParallelArrayModesCommute(["seq", "par"], function(m) { + return new ParallelArray([256], function(i) { + var x = []; + for (var i = 0; i < 99; i++) { + x[i] = i; + } + return x; + }, m); + }); +} + +buildSimple(); diff --git a/js/src/jit-test/tests/parallelarray/alloc-different-objs.js b/js/src/jit-test/tests/parallelarray/alloc-different-objs.js new file mode 100644 index 00000000000..4591c79894a --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/alloc-different-objs.js @@ -0,0 +1,21 @@ +load(libdir + "parallelarray-helpers.js"); + +function testMap() { + compareAgainstArray(range(0, 64), "map", function (v) { + var x = []; + var N = 2; + for (var i = 0; i < 10; i++) { + if ((i % N) == 0) { + x[i] = {f1: v}; + } else if ((i % N) == 1) { + x[i] = {f1: v, f2: v, f3: v, + f4: v, f5: v, f6: v, + f7: v, f8: v, f9: v}; + } + } + return x; + }); +} + +testMap(); + diff --git a/js/src/jit-test/tests/parallelarray/alloc-many-objs.js b/js/src/jit-test/tests/parallelarray/alloc-many-objs.js new file mode 100644 index 00000000000..2c23b710501 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/alloc-many-objs.js @@ -0,0 +1,23 @@ +load(libdir + "parallelarray-helpers.js"); + +function testMap() { + // At least on my machine, this test is successful, whereas + // `alloc-too-many-objs.js` fails to run in parallel because of + // issues around GC. + + var nums = new ParallelArray(range(0, 10)); + + assertParallelArrayModesCommute(["seq", "par"], function(m) { + print(m.mode+" "+m.expect); + nums.map(function (v) { + var x = []; + for (var i = 0; i < 45000; i++) { + x[i] = {from: v}; + } + return x; + }, m) + }); +} + +testMap(); + diff --git a/js/src/jit-test/tests/parallelarray/alloc-obj.js b/js/src/jit-test/tests/parallelarray/alloc-obj.js new file mode 100644 index 00000000000..91ab66a8931 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/alloc-obj.js @@ -0,0 +1,13 @@ +load(libdir + "parallelarray-helpers.js"); + +function buildSimple() { + + assertParallelArrayModesCommute(["seq", "par"], function(m) { + return new ParallelArray([256], function(i) { + return { x: i, y: i + 1, z: i + 2 }; + }, m); + }); + +} + +buildSimple(); diff --git a/js/src/jit-test/tests/parallelarray/alloc-too-many-objs.js b/js/src/jit-test/tests/parallelarray/alloc-too-many-objs.js new file mode 100644 index 00000000000..c9773911884 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/alloc-too-many-objs.js @@ -0,0 +1,35 @@ +// |jit-test| slow; + +load(libdir + "parallelarray-helpers.js"); + +function testMap() { + + // Note: This is the same kernel function as `alloc-many-objs`, but + // with a larger bound. This often fails par. exec. because it + // triggers GC at inconvenient times. But let's just test that it + // doesn't crash or something! + + var ints = range(0, 100000); + var pints = new ParallelArray(ints); + + // The bailout occurs because at some point during execution we will + // request a GC. The GC is actually deferred until after execution + // fails. + pints.map(kernel, {mode: "par", expect: "bailout"}); + + function kernel(v) { + var x = []; + + for (var i = 0; i < 50; i++) { + x[i] = []; + for (var j = 0; j < 1024; j++) { + x[i][j] = j; + } + } + + return x; + } +} + +testMap(); + diff --git a/js/src/jit-test/tests/parallelarray/bailout-executed.js b/js/src/jit-test/tests/parallelarray/bailout-executed.js new file mode 100644 index 00000000000..d5de61b1558 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/bailout-executed.js @@ -0,0 +1,22 @@ +load(libdir + "parallelarray-helpers.js"); + +function makeObject(e, i, c) { + var v = {element: e, index: i, collection: c}; + + if (e == 512) // note: happens once + delete v.i; + + return v; +} + +function test() { + var array = range(0, 768); + var array1 = array.map(makeObject); + + var pa = new ParallelArray(array); + var pa1 = pa.map(makeObject, {mode: "par", expect: "mixed"}); + + assertStructuralEq(pa1, array1); +} + +test(); diff --git a/js/src/jit-test/tests/parallelarray/bailout-never-executed.js b/js/src/jit-test/tests/parallelarray/bailout-never-executed.js new file mode 100644 index 00000000000..3a2e86f0418 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/bailout-never-executed.js @@ -0,0 +1,16 @@ +// |jit-test| slow; +// ^^ This test is slow when --no-ion is used, specifically, +// as part of TBPL. + +load(libdir + "parallelarray-helpers.js"); + +function makeObject(e, i, c) { + var v = {element: e, index: i, collection: c}; + + if (e == 512) // note: never happens + delete v.i; + + return v; +} + +compareAgainstArray(range(0, 512), "map", makeObject); diff --git a/js/src/jit-test/tests/parallelarray/closure-1.js b/js/src/jit-test/tests/parallelarray/closure-1.js new file mode 100644 index 00000000000..42031d80c47 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/closure-1.js @@ -0,0 +1,12 @@ +function testClosureCreation() { + var a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9,10, + 11,12,13,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30, + 31,32,33,34,35,36,27,38,39,40]; + var p = new ParallelArray(a); + var makeadd1 = function (v) { return function (x) { return x+1; }; }; + var m = p.map(makeadd1, {mode: "par", expect: "success"}); + assertEq(m.get(1)(2), 3); // (\x.x+1) 2 == 3 +} + +testClosureCreation(); diff --git a/js/src/jit-test/tests/parallelarray/closure-2.js b/js/src/jit-test/tests/parallelarray/closure-2.js new file mode 100644 index 00000000000..9a2fef39a41 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/closure-2.js @@ -0,0 +1,11 @@ +load(libdir + "parallelarray-helpers.js"); + +function testClosureCreation() { + var a = range(0, 64); + var p = new ParallelArray(a); + var makeadd1 = function (v) { return function (x) { return x+1; }; }; + var m = p.map(makeadd1, {mode: "par", expect: "success"}); + assertEq(m.get(1)(2), 3); // (\x.x+1) 2 == 3 +} + +testClosureCreation(); diff --git a/js/src/jit-test/tests/parallelarray/closure-3.js b/js/src/jit-test/tests/parallelarray/closure-3.js new file mode 100644 index 00000000000..de865a7d9f1 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/closure-3.js @@ -0,0 +1,12 @@ +load(libdir + "parallelarray-helpers.js"); + +function testClosureCreationAndInvocation() { + var a = range(1, 65); + var p = new ParallelArray(a); + function etaadd1(v) { return (function (x) { return x+1; })(v); }; + // eta-expansion is (or at least can be) treated as call with unknown target + var m = p.map(etaadd1, {mode: "par", expect: "success"}); + assertEq(m.get(1), 3); // (\x.x+1) 2 == 3 +} + +testClosureCreationAndInvocation(); diff --git a/js/src/jit-test/tests/parallelarray/closure-4.js b/js/src/jit-test/tests/parallelarray/closure-4.js new file mode 100644 index 00000000000..6bc7b809542 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/closure-4.js @@ -0,0 +1,12 @@ +load(libdir + "parallelarray-helpers.js"); + +function testClosureCreationAndInvocation() { + var a = range(1, 65); + var p = new ParallelArray(a); + function makeaddv(v) { return function (x) { return x+v; }; }; + var m = p.map(makeaddv, {mode: "par", expect: "success"}); + assertEq(m.get(1)(1), 3); // (\x.x+v){v=2} 1 == 3 + assertEq(m.get(2)(2), 5); // (\x.x+v){v=3} 2 == 5 +} + +testClosureCreationAndInvocation(); diff --git a/js/src/jit-test/tests/parallelarray/closure-5.js b/js/src/jit-test/tests/parallelarray/closure-5.js new file mode 100644 index 00000000000..e3b8c9397dc --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/closure-5.js @@ -0,0 +1,37 @@ +load(libdir + "parallelarray-helpers.js"); + +function testClosureCreationAndInvocation() { + var a = range(1, 65); + var p = new ParallelArray(a); + function makeaddv(v) { + var u = v - 1; + var t = v - 2; + var s = v - 3; + var r = v - 4; + var q = v - 5; + var p = v - 6; + var o = v - 7; + var n = v - 8; + var m = v - 9; + var l = v - 10; + var k = v - 11; + var j = v - 12; + var i = v - 13; + var h = v - 14; + var g = v - 15; + var f = v - 16; + var e = v - 17; + var d = v - 18; + var c = v - 19; + var b = v - 20; + var a = v - 21; + return function (x) { return [x,v,u,t,v,s,r,q, + p,o,m,n,l,k,j,i, + h,g,f,e,d,c,b,a]; }; + }; + var m = p.map(makeaddv); + print(m.get(20)(1)[2]); + assertEq(m.get(20)(1)[2], 20); +} + +testClosureCreationAndInvocation(); diff --git a/js/src/jit-test/tests/parallelarray/closure-6.js b/js/src/jit-test/tests/parallelarray/closure-6.js new file mode 100644 index 00000000000..b223930622f --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/closure-6.js @@ -0,0 +1,48 @@ +load(libdir + "parallelarray-helpers.js"); + +function testClosureCreationAndInvocation() { + var a = range(0, 64); + var p = new ParallelArray(a); + function makeaddv(v) { + var u = v - 1; + var t = v - 2; + var s = v - 3; + var r = v - 4; + var q = v - 5; + var p = v - 6; + var o = v - 7; + var n = v - 8; + var m = v - 9; + var l = v - 10; + var k = v - 11; + var j = v - 12; + var i = v - 13; + var h = v - 14; + var g = v - 15; + var f = v - 16; + var e = v - 17; + var d = v - 18; + var c = v - 19; + var b = v - 20; + var a = v - 21; + return function (x) { + switch (x) { + case 0: return a; case 1: return b; + case 2: return c; case 3: return d; + case 4: return e; case 5: return f; + case 6: return g; case 7: return h; + case 8: return i; case 9: return j; + case 10: return k; case 11: return l; + case 12: return m; case 13: return n; + case 14: return o; case 15: return p; + case 16: return q; case 17: return r; + case 18: return s; case 19: return t; + case 20: return u; + } + }; + }; + var m = p.map(makeaddv); + assertEq(m.get(21)(1), 1); // v == 21; x == 1 ==> inner function returns b == 1 +} + +testClosureCreationAndInvocation(); diff --git a/js/src/jit-test/tests/parallelarray/closure-7.js b/js/src/jit-test/tests/parallelarray/closure-7.js new file mode 100644 index 00000000000..23749a69fbc --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/closure-7.js @@ -0,0 +1,48 @@ +load(libdir + "parallelarray-helpers.js"); + +function testClosureCreationAndInvocation() { + var a = range(0, 64); + var p = new ParallelArray(a); + function makeaddv(v) { + var u = 1; + var t = 2; + var s = 3; + var r = 4; + var q = 5; + var p = 6; + var o = 7; + var n = 8; + var m = 9; + var l = 10; + var k = 11; + var j = 12; + var i = 13; + var h = 14; + var g = 15; + var f = 16; + var e = 17; + var d = 18; + var c = 19; + var b = 20; + var a = 21; + return function (x) { + switch (x) { + case 0: return a; case 1: return b; + case 2: return c; case 3: return d; + case 4: return e; case 5: return f; + case 6: return g; case 7: return h; + case 8: return i; case 9: return j; + case 10: return k; case 11: return l; + case 12: return m; case 13: return n; + case 14: return o; case 15: return p; + case 16: return q; case 17: return r; + case 18: return s; case 19: return t; + case 20: return u; + } + }; + }; + var m = p.map(makeaddv, {mode: "par", expect: "success"}); + assertEq(m.get(21)(1), 20); // v == 21; x == 1 ==> inner function returns b == 20 +} + +testClosureCreationAndInvocation(); diff --git a/js/src/jit-test/tests/parallelarray/closure-8.js b/js/src/jit-test/tests/parallelarray/closure-8.js new file mode 100644 index 00000000000..3cbc89aed8c --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/closure-8.js @@ -0,0 +1,53 @@ +load(libdir + "parallelarray-helpers.js"); + +function testClosureCreationAndInvocation() { + var a = range(0, 64); + var p = new ParallelArray(a); + function makeaddv(v) { + var u = 1; + var t = 2; + var s = 3; + var r = 4; + var q = 5; + var p = 6; + var o = 7; + var n = 8; + var m = 9; + var l = 10; + var k = 11; + var j = 12; + var i = 13; + var h = 14; + var g = 15; + var f = 16; + var e = 17; + var d = 18; + var c = 19; + var b = 20; + var a = 21; + return ((v % 2 == 0) + ? function (x) { return a; } + : function (x) { + switch (x) { + case 0: return a; case 1: return b; + case 2: return c; case 3: return d; + case 4: return e; case 5: return f; + case 6: return g; case 7: return h; + case 8: return i; case 9: return j; + case 10: return k; case 11: return l; + case 12: return m; case 13: return n; + case 14: return o; case 15: return p; + case 16: return q; case 17: return r; + case 18: return s; case 19: return t; + case 20: return u; + } + }); + } + var m = p.map(makeaddv, {mode: "par", expect: "success"}); + assertEq(m.get(21)(1), 20); // v == 21; x == 1 ==> inner function returns b == 20 + + var n = p.map(function (v) { return function (x) { return v; }}); + assertEq(n.get(21)(1), 21); // v == 21 +} + +testClosureCreationAndInvocation(); diff --git a/js/src/jit-test/tests/parallelarray/compare-strings-eq.js b/js/src/jit-test/tests/parallelarray/compare-strings-eq.js new file mode 100644 index 00000000000..34dabbdb890 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/compare-strings-eq.js @@ -0,0 +1,6 @@ +load(libdir + "parallelarray-helpers.js"); + +compareAgainstArray(["a", "b", "c", "d", "e", "f", "g", "h", + "i", "j", "k", "l", "m", "n", "o", "p", + "q", "r", "s", "t", "u", "v", "w", "x", + "y", "z"], "map", function(e) { return e == "u" || e == "x"; }); diff --git a/js/src/jit-test/tests/parallelarray/compare-strings-ne.js b/js/src/jit-test/tests/parallelarray/compare-strings-ne.js new file mode 100644 index 00000000000..9b8745d9408 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/compare-strings-ne.js @@ -0,0 +1,6 @@ +load(libdir + "parallelarray-helpers.js"); + +compareAgainstArray(["a", "b", "c", "d", "e", "f", "g", "h", + "i", "j", "k", "l", "m", "n", "o", "p", + "q", "r", "s", "t", "u", "v", "w", "x", + "y", "z"], "map", function(e) { return e != "u" && e != "x"; }); diff --git a/js/src/jit-test/tests/parallelarray/comprehension-in-loop.js b/js/src/jit-test/tests/parallelarray/comprehension-in-loop.js new file mode 100644 index 00000000000..57e9b378397 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/comprehension-in-loop.js @@ -0,0 +1,19 @@ +// |jit-test| slow; + +load(libdir + "parallelarray-helpers.js") + +function buildComprehension() { + var pa1 = new ParallelArray(256, function (idx) { return idx; }); + for (var i = 0; i < 20000; i++) { + print(i); + buildAndCompare(); + } + + function buildAndCompare() { + // this will be ion-generated: + var pa2 = new ParallelArray(256, function (idx) { return idx; }); + assertStructuralEq(pa1, pa2); + } +} + +buildComprehension(); diff --git a/js/src/jit-test/tests/parallelarray/comprehension-nested.js b/js/src/jit-test/tests/parallelarray/comprehension-nested.js new file mode 100644 index 00000000000..470f7ca3ab6 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/comprehension-nested.js @@ -0,0 +1,16 @@ +load(libdir + "parallelarray-helpers.js") + +function test() { + var pa1 = new ParallelArray(256, function (x) { + return new ParallelArray(256, function(y) { return x*1000 + y; }); + }); + + for (var x = 0; x < 256; x++) { + var pax = pa1.get(x); + for (var y = 0; y < 256; y++) { + assertEq(pax.get(y), x * 1000 + y); + } + } +} + +test(); diff --git a/js/src/jit-test/tests/parallelarray/comprehension-scale.js b/js/src/jit-test/tests/parallelarray/comprehension-scale.js index 80d69359618..684c6040031 100644 --- a/js/src/jit-test/tests/parallelarray/comprehension-scale.js +++ b/js/src/jit-test/tests/parallelarray/comprehension-scale.js @@ -14,7 +14,7 @@ function buildComprehension() { } } } - var p2 = new ParallelArray(a).partition(d).partition(H); + var p2 = new ParallelArray(a).partition(d).partition(W); assertEqParallelArray(p, p2); } diff --git a/js/src/jit-test/tests/parallelarray/comprehension-throws.js b/js/src/jit-test/tests/parallelarray/comprehension-throws.js index bc0b5e28915..b12165b0c22 100644 --- a/js/src/jit-test/tests/parallelarray/comprehension-throws.js +++ b/js/src/jit-test/tests/parallelarray/comprehension-throws.js @@ -22,4 +22,5 @@ function buildComprehension() { }, RangeError); } -buildComprehension(); +// FIXME(bug 844887) throw correct exception +// buildComprehension(); diff --git a/js/src/jit-test/tests/parallelarray/constructor-1.js b/js/src/jit-test/tests/parallelarray/constructor-1.js index 8111c580609..651d218eed7 100644 --- a/js/src/jit-test/tests/parallelarray/constructor-1.js +++ b/js/src/jit-test/tests/parallelarray/constructor-1.js @@ -1,14 +1,14 @@ -load(libdir + "eqArrayHelper.js"); +load(libdir + "parallelarray-helpers.js"); function buildSimple() { // Simple constructor var a = [1,2,3,4,5]; var p = new ParallelArray(a); - assertEqArray(p, a); + assertEqParallelArrayArray(p, a); var a2 = a.slice(); a[0] = 9; // No sharing - assertEqArray(p, a2); + assertEqParallelArrayArray(p, a2); } buildSimple(); diff --git a/js/src/jit-test/tests/parallelarray/constructor-2.js b/js/src/jit-test/tests/parallelarray/constructor-2.js index de3ae8cb383..709fcde2632 100644 --- a/js/src/jit-test/tests/parallelarray/constructor-2.js +++ b/js/src/jit-test/tests/parallelarray/constructor-2.js @@ -10,4 +10,5 @@ function buildWithHoles() { assertEq(Object.keys(p).join(","), Object.keys(b).join(",")); } -buildWithHoles(); +// FIXME(bug 844882) self-hosted object not array-like, exposes internal properties +// buildWithHoles(); diff --git a/js/src/jit-test/tests/parallelarray/constructor-3.js b/js/src/jit-test/tests/parallelarray/constructor-3.js index fa8cf858cae..52068bd8431 100644 --- a/js/src/jit-test/tests/parallelarray/constructor-3.js +++ b/js/src/jit-test/tests/parallelarray/constructor-3.js @@ -7,7 +7,7 @@ function buildArrayLike() { var a = { 0: 1, 1: 2, 2: 3, 3: 4, length: 4 }; var p = new ParallelArray(a); var e = Array.prototype.join.call(a, ","); - assertEq(p.toString(), bracket(e)); + assertEq(p.toString(), e); } buildArrayLike(); diff --git a/js/src/jit-test/tests/parallelarray/constructor-4.js b/js/src/jit-test/tests/parallelarray/constructor-4.js index 9d18cd881cb..0a26955b1ab 100644 --- a/js/src/jit-test/tests/parallelarray/constructor-4.js +++ b/js/src/jit-test/tests/parallelarray/constructor-4.js @@ -5,9 +5,11 @@ function buildPA() { var p1 = new ParallelArray([1,2,3,4]); var p2 = new ParallelArray(p1); assertEqParallelArray(p1, p2); + var p1d = new ParallelArray([2,2], function(i,j) { return i + j; }); var p2d = new ParallelArray(p1d); assertEq(p1d.toString(), p2d.toString()); } -buildPA(); +// FIXME(bug 844882) self-hosted object not array-like, exposes internal properties +// buildPA(); diff --git a/js/src/jit-test/tests/parallelarray/constructor-5.js b/js/src/jit-test/tests/parallelarray/constructor-5.js index 923a1e86e70..de89107a4cc 100644 --- a/js/src/jit-test/tests/parallelarray/constructor-5.js +++ b/js/src/jit-test/tests/parallelarray/constructor-5.js @@ -1,3 +1,7 @@ +// |jit-test| slow; +// ^^ This test is slow when --no-ion is used, specifically, +// as part of TBPL. + function testCopyBigArray() { // Don't crash var a = new Array(1000 * 1000); diff --git a/js/src/jit-test/tests/parallelarray/element-1.js b/js/src/jit-test/tests/parallelarray/element-1.js index d56a763df77..e5ddd30f6af 100644 --- a/js/src/jit-test/tests/parallelarray/element-1.js +++ b/js/src/jit-test/tests/parallelarray/element-1.js @@ -3,11 +3,11 @@ function testElement() { var a = [1,{},"a",false] var p = new ParallelArray(a); for (var i = 0; i < a.length; i++) { - assertEq(p[i], p[i]); - assertEq(p[i], a[i]); + assertEq(p.get(i), p.get(i)); + assertEq(p.get(i), a[i]); } // Test out of bounds - assertEq(p[42], undefined); + assertEq(p.get(42), undefined); } testElement(); diff --git a/js/src/jit-test/tests/parallelarray/element-2.js b/js/src/jit-test/tests/parallelarray/element-2.js index 8db24aae828..440f375abf3 100644 --- a/js/src/jit-test/tests/parallelarray/element-2.js +++ b/js/src/jit-test/tests/parallelarray/element-2.js @@ -4,16 +4,22 @@ function testElement() { // Test getting element from higher dimension var p = new ParallelArray([2,2,2], function () { return 0; }); var p0 = new ParallelArray([2,2], function () { return 0; }); - assertEqParallelArray(p[0], p0); + print("0"); + assertEqParallelArray(p.get(0), p0); + // Should create new wrapper - assertEq(p[0] !== p[0], true); + print("1"); + assertEq(p.get(0) !== p.get(0), true); + // Test out of bounds - assertEq(p[42], undefined); + print("2"); + assertEq(p.get(42), undefined); + // Test getting element from 0-lengthed higher dimension var pp = new ParallelArray([0,0], function() { return 0; }); - assertEq(pp[2], undefined); + assertEq(pp.get(2), undefined); var pp2 = new ParallelArray([2,0], function() { return 0; }); - assertEqParallelArray(pp2[0], new ParallelArray()); + assertEqParallelArray(pp2.get(0), new ParallelArray()); } testElement(); diff --git a/js/src/jit-test/tests/parallelarray/element-3.js b/js/src/jit-test/tests/parallelarray/element-3.js index 4308e2bfec0..9e919b81c36 100644 --- a/js/src/jit-test/tests/parallelarray/element-3.js +++ b/js/src/jit-test/tests/parallelarray/element-3.js @@ -7,4 +7,5 @@ function testElement() { assertEq(desc.value, 9); } -testElement(); +// FIXME(bug 844882) self-hosted object not array-like, exposes internal properties +// testElement(); diff --git a/js/src/jit-test/tests/parallelarray/enumerate-1.js b/js/src/jit-test/tests/parallelarray/enumerate-1.js index 4fae6252237..69dfd437a89 100644 --- a/js/src/jit-test/tests/parallelarray/enumerate-1.js +++ b/js/src/jit-test/tests/parallelarray/enumerate-1.js @@ -4,4 +4,5 @@ function testEnumerate() { assertEq(i >= 0 && i < p.length, true); } -testEnumerate(); +// FIXME(bug 844882) self-hosted object not array-like, exposes internal properties +// testEnumerate(); diff --git a/js/src/jit-test/tests/parallelarray/filter-1.js b/js/src/jit-test/tests/parallelarray/filter-1.js deleted file mode 100644 index 58340b78c48..00000000000 --- a/js/src/jit-test/tests/parallelarray/filter-1.js +++ /dev/null @@ -1,14 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testFilterAll() { - // Test filtering everything (leaving everything in) - var p = new ParallelArray([0,1,2,3,4]); - var all = p.map(function (i) { return true; }); - var r = p.filter(all); - assertEqParallelArray(r, p); - var p = new ParallelArray([5,2], function(i,j) { return i+j; }); - var r = p.filter(all); - assertEqParallelArray(r, new ParallelArray(p)); -} - -testFilterAll(); diff --git a/js/src/jit-test/tests/parallelarray/filter-2.js b/js/src/jit-test/tests/parallelarray/filter-2.js deleted file mode 100644 index 57a7ba01bbe..00000000000 --- a/js/src/jit-test/tests/parallelarray/filter-2.js +++ /dev/null @@ -1,14 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testFilterNone() { - // Test filtering (removing everything) - var p = new ParallelArray([0,1,2,3,4]); - var none = p.map(function () { return false; }); - var r = p.filter(none); - assertEqParallelArray(r, new ParallelArray); - var p = new ParallelArray([5,2], function(i,j) { return i+j; }); - var r = p.filter(none); - assertEqParallelArray(r, new ParallelArray); -} - -testFilterNone(); diff --git a/js/src/jit-test/tests/parallelarray/filter-3.js b/js/src/jit-test/tests/parallelarray/filter-3.js deleted file mode 100644 index 1479646c3f1..00000000000 --- a/js/src/jit-test/tests/parallelarray/filter-3.js +++ /dev/null @@ -1,14 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testFilterSome() { - var p = new ParallelArray([0,1,2,3,4]); - var evenBelowThree = p.map(function (i) { return ((i%2) === 0) && (i < 3); }); - var r = p.filter(evenBelowThree); - assertEqParallelArray(r, new ParallelArray([0,2])); - var p = new ParallelArray([5,2], function (i,j) { return i; }); - var evenBelowThree = p.map(function (i) { return ((i[0]%2) === 0) && (i[0] < 3); }); - var r = p.filter(evenBelowThree); - assertEqParallelArray(r, new ParallelArray([p[0], p[2]])); -} - -testFilterSome(); diff --git a/js/src/jit-test/tests/parallelarray/filter-4.js b/js/src/jit-test/tests/parallelarray/filter-4.js deleted file mode 100644 index d6e7089b06a..00000000000 --- a/js/src/jit-test/tests/parallelarray/filter-4.js +++ /dev/null @@ -1,16 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testFilterMisc() { - var p = new ParallelArray([0,1,2]); - // Test array - var r = p.filter([true, false, true]); - assertEqParallelArray(r, new ParallelArray([p[0], p[2]])); - // Test array-like - var r = p.filter({ 0: true, 1: false, 2: true, length: 3 }); - assertEqParallelArray(r, new ParallelArray([p[0], p[2]])); - // Test truthy - var r = p.filter([1, "", {}]); - assertEqParallelArray(r, new ParallelArray([p[0], p[2]])); -} - -testFilterMisc(); diff --git a/js/src/jit-test/tests/parallelarray/filter-all.js b/js/src/jit-test/tests/parallelarray/filter-all.js new file mode 100644 index 00000000000..32ee8f72efa --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/filter-all.js @@ -0,0 +1,2 @@ +load(libdir + "parallelarray-helpers.js"); +testFilter(range(0, 1024), function() { return true; }); diff --git a/js/src/jit-test/tests/parallelarray/filter-every-third-element.js b/js/src/jit-test/tests/parallelarray/filter-every-third-element.js new file mode 100644 index 00000000000..b613229edde --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/filter-every-third-element.js @@ -0,0 +1,5 @@ +load(libdir + "parallelarray-helpers.js"); +testFilter(range(0, 1024), function(e, i) { + return (i % 3) != 0; +}); + diff --git a/js/src/jit-test/tests/parallelarray/filter-non-divisible.js b/js/src/jit-test/tests/parallelarray/filter-non-divisible.js new file mode 100644 index 00000000000..929ef02a299 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/filter-non-divisible.js @@ -0,0 +1,6 @@ +load(libdir + "parallelarray-helpers.js"); + +// since we divide things into chunks of 32, and filter uses some +// bitsets, test that all that logic works fine if the number of items +// is not evenly divisible by 32: +testFilter(range(0, 617), function(i) { return (i % 2) == 0; }); diff --git a/js/src/jit-test/tests/parallelarray/filter-none.js b/js/src/jit-test/tests/parallelarray/filter-none.js new file mode 100644 index 00000000000..409a8c356c7 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/filter-none.js @@ -0,0 +1,2 @@ +load(libdir + "parallelarray-helpers.js"); +testFilter(range(0, 1024), function() { return false; }); diff --git a/js/src/jit-test/tests/parallelarray/filter-throws.js b/js/src/jit-test/tests/parallelarray/filter-throws.js deleted file mode 100644 index 9d61cb62b66..00000000000 --- a/js/src/jit-test/tests/parallelarray/filter-throws.js +++ /dev/null @@ -1,11 +0,0 @@ -load(libdir + "asserts.js"); - -function testFilterThrows() { - var p = new ParallelArray([1,2,3,4,5]); - - assertThrowsInstanceOf(function () { - p.filter({ length: 0xffffffff + 1 }); - }, RangeError); -} - -testFilterThrows(); diff --git a/js/src/jit-test/tests/parallelarray/filter-truthy.js b/js/src/jit-test/tests/parallelarray/filter-truthy.js new file mode 100644 index 00000000000..874a714861c --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/filter-truthy.js @@ -0,0 +1,18 @@ +load(libdir + "parallelarray-helpers.js"); + +function testFilterMisc() { + function truthy(e, i, c) { + switch (i % 6) { + case 0: return 1; + case 1: return ""; + case 2: return {}; + case 3: return []; + case 4: return false; + case 5: return true; + } + } + + testFilter(range(0, 1024), truthy); +} + +testFilterMisc(); diff --git a/js/src/jit-test/tests/parallelarray/filter-very-few.js b/js/src/jit-test/tests/parallelarray/filter-very-few.js new file mode 100644 index 00000000000..e4f98b296e4 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/filter-very-few.js @@ -0,0 +1,2 @@ +load(libdir + "parallelarray-helpers.js"); +testFilter(range(0, 1024), function(i) { return i <= 1 || i >= 1022; }); diff --git a/js/src/jit-test/tests/parallelarray/flatten-3.js b/js/src/jit-test/tests/parallelarray/flatten-3.js new file mode 100644 index 00000000000..642d16f766a --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/flatten-3.js @@ -0,0 +1,24 @@ +load(libdir + "parallelarray-helpers.js"); + +function testFlatten() { + var p0 = new ParallelArray([0,1]); + var p1 = new ParallelArray([2,3]); + var p = new ParallelArray([p0, p1]); + var p2 = new ParallelArray([0,1,2,3]); + assertEqParallelArray(p.flatten(), p2); + + // Test flatten crossing packed boundary with non-shape uniform elements + var blah = new ParallelArray([2,2], function() { return 0; }); + var pp = new ParallelArray([p0, p1, blah]); + var p2 = new ParallelArray([0,1,2,3,blah[0],blah[1]]); + assertEqParallelArray(pp.flatten(), p2); + + var p0 = new ParallelArray([2,2], function() { return 1; }); + var p1 = new ParallelArray([2,2], function() { return 2; }); + var p = new ParallelArray([p0, p1]); + var p2 = new ParallelArray([p0[0],p0[1],p1[0],p1[1]]); + assertEqParallelArray(p.flatten(), p2); +} + +// FIXME(bug 844991) logical shape not implemented +// testFlatten(); diff --git a/js/src/jit-test/tests/parallelarray/get-1.js b/js/src/jit-test/tests/parallelarray/get-1.js index e073f487f08..11f097b370d 100644 --- a/js/src/jit-test/tests/parallelarray/get-1.js +++ b/js/src/jit-test/tests/parallelarray/get-1.js @@ -2,7 +2,7 @@ function testGet() { var a = [1,2,3,4,5]; var p = new ParallelArray(a); for (var i = 0; i < a.length; i++) - assertEq(p.get([i]), a[i]); + assertEq(p.get(i), a[i]); } testGet(); diff --git a/js/src/jit-test/tests/parallelarray/get-2.js b/js/src/jit-test/tests/parallelarray/get-2.js index a27be56dbdc..173470b40db 100644 --- a/js/src/jit-test/tests/parallelarray/get-2.js +++ b/js/src/jit-test/tests/parallelarray/get-2.js @@ -2,12 +2,12 @@ load(libdir + "parallelarray-helpers.js"); function testGet() { var p = new ParallelArray([2,2,2], function(i,j,k) { return i+j+k; }); - assertEq(p.get([1,1,1]), 1+1+1); + assertEq(p.get(1,1,1), 1+1+1); var p2 = new ParallelArray([2], function(i) { return 1+1+i; }); - assertEqParallelArray(p.get([1,1]), p2); + assertEqParallelArray(p.get(1,1), p2); var p3 = new ParallelArray([2,2], function(i,j) { return 1+i+j; }); - assertEqParallelArray(p.get([1]), p3); - assertEq(p.get([5,5]), undefined); + assertEqParallelArray(p.get(1), p3); + assertEq(p.get(5,5), undefined); } testGet(); diff --git a/js/src/jit-test/tests/parallelarray/get-3.js b/js/src/jit-test/tests/parallelarray/get-3.js index 39ad7d83b17..c9810bded20 100644 --- a/js/src/jit-test/tests/parallelarray/get-3.js +++ b/js/src/jit-test/tests/parallelarray/get-3.js @@ -2,7 +2,7 @@ function testGetNoCraziness() { // .get shouldn't do prototype walks ParallelArray.prototype[42] = "foo"; var p = new ParallelArray([1,2,3,4]); - assertEq(p.get([42]), undefined); + assertEq(p.get(42), undefined); } testGetNoCraziness(); diff --git a/js/src/jit-test/tests/parallelarray/get-4.js b/js/src/jit-test/tests/parallelarray/get-4.js index c5961202164..2cbcee10ae5 100644 --- a/js/src/jit-test/tests/parallelarray/get-4.js +++ b/js/src/jit-test/tests/parallelarray/get-4.js @@ -1,6 +1,6 @@ function testGetBounds() { var p = new ParallelArray([1,2,3,4]); - assertEq(p.get([42]), undefined); + assertEq(p.get(42), undefined); } testGetBounds(); diff --git a/js/src/jit-test/tests/parallelarray/get-5.js b/js/src/jit-test/tests/parallelarray/get-5.js deleted file mode 100644 index 37278720f6a..00000000000 --- a/js/src/jit-test/tests/parallelarray/get-5.js +++ /dev/null @@ -1,9 +0,0 @@ -function testGet() { - // Test .get on array-like - var p = new ParallelArray([1,2,3,4]); - assertEq(p.get({ 0: 1, length: 1 }), 2); - var p2 = new ParallelArray([2,2], function(i,j) { return i+j; }); - assertEq(p2.get({ 0: 1, 1: 0, length: 2 }), 1); -} - -testGet(); diff --git a/js/src/jit-test/tests/parallelarray/get-6.js b/js/src/jit-test/tests/parallelarray/get-6.js new file mode 100644 index 00000000000..a1fe34b6c34 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/get-6.js @@ -0,0 +1,13 @@ +function testGet() { + // Test getting higher dimension inferred shape + var p0 = new ParallelArray([0,1]); + var p1 = new ParallelArray([2,3]); + var p = new ParallelArray([p0, p1]); + assertEq(p.get(0,0), 0); + assertEq(p.get(0,1), 1); + assertEq(p.get(1,0), 2); + assertEq(p.get(1,1), 3); +} + +// FIXME(bug 844991) logical shape not implemented +// testGet(); diff --git a/js/src/jit-test/tests/parallelarray/get-throws.js b/js/src/jit-test/tests/parallelarray/get-throws.js deleted file mode 100644 index f975fc182a5..00000000000 --- a/js/src/jit-test/tests/parallelarray/get-throws.js +++ /dev/null @@ -1,14 +0,0 @@ -load(libdir + "asserts.js"); - -function testGetThrows() { - // Throw if argument not object - var p = new ParallelArray([1,2,3,4]); - assertThrowsInstanceOf(function () { p.get(42); }, TypeError); - assertThrowsInstanceOf(function () { p.get(.42); }, TypeError); - var p = new ParallelArray([2,2], function (i,j) { return i+j; }); - assertThrowsInstanceOf(function () { - p.get({ 0: 1, 1: 0, testGet: 2 }); - }, TypeError); -} - -testGetThrows(); diff --git a/js/src/jit-test/tests/parallelarray/holes-1.js b/js/src/jit-test/tests/parallelarray/holes-1.js index 643d6a6048c..32ae6db63b9 100644 --- a/js/src/jit-test/tests/parallelarray/holes-1.js +++ b/js/src/jit-test/tests/parallelarray/holes-1.js @@ -7,23 +7,23 @@ function testHoles() { // force the VM to call a conversion function, which will crash if the value // we got out is a JS_ARRAY_HOLE. var p = new ParallelArray([,1]); - assertEq(p[0] * 42, NaN); + assertEq(p.get(0) * 42, NaN); var m = p.map(f1); - assertEq(m[0], NaN); - assertEq(m[1], 42); + assertEq(m.get(0), NaN); + assertEq(m.get(1), 42); var r = p.reduce(f2); assertEq(r, NaN); var s = p.scan(f2); - assertEq(s[0] * 42, NaN); - assertEq(s[1], NaN); + assertEq(s.get(0) * 42, NaN); + assertEq(s.get(1), NaN); var k = p.scatter([1,0]); - assertEq(k[0], 1); + assertEq(k.get(0), 1); assertEq(k[1] * 42, NaN); - var l = p.filter([1,0]); - assertEq(l[0] * 42, NaN); + var l = p.filter(function (e, i) { return i == 0; }); + assertEq(l.get(0) * 42, NaN); var p2 = p.partition(1); - assertEq(p2[0][0] * 42, NaN); - var g = p.get([0]); + assertEq(p2.get(0).get(0) * 42, NaN); + var g = p.get(0); assertEq(g * 42, NaN); } diff --git a/js/src/jit-test/tests/parallelarray/holes-2.js b/js/src/jit-test/tests/parallelarray/holes-2.js index 2e72da2a7ed..89ae0821bd9 100644 --- a/js/src/jit-test/tests/parallelarray/holes-2.js +++ b/js/src/jit-test/tests/parallelarray/holes-2.js @@ -20,4 +20,5 @@ function testElement() { assertEq(p2[0], undefined); } -testElement(); +// FIXME(bug 844882) self-hosted object not array-like, exposes internal properties +// testElement(); diff --git a/js/src/jit-test/tests/parallelarray/index-1.js b/js/src/jit-test/tests/parallelarray/index-1.js new file mode 100644 index 00000000000..465c8a79555 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/index-1.js @@ -0,0 +1,8 @@ +function test() { + var N = 22; + var p = new ParallelArray([N], function(i) { return i; }); + for (var i = 0; i < N; i++) + assertEq(p.get(i), i); +} + +test(); diff --git a/js/src/jit-test/tests/parallelarray/index-2.js b/js/src/jit-test/tests/parallelarray/index-2.js new file mode 100644 index 00000000000..3a9171fbb21 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/index-2.js @@ -0,0 +1,17 @@ +function test() { + function mk(i, j) { return i*100+j; } + + var N = 22; + var M = 44; + + var p = new ParallelArray([N,M], mk); + for (var i = 0; i < N; i++) { + for (var j = 0; j < M; j++) { + print([i, j]); + assertEq(p.get(i, j), mk(i, j)); + assertEq(p.get(i).get(j), mk(i, j)); + } + } +} + +test(); diff --git a/js/src/jit-test/tests/parallelarray/index-3.js b/js/src/jit-test/tests/parallelarray/index-3.js new file mode 100644 index 00000000000..f6df9d4730a --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/index-3.js @@ -0,0 +1,24 @@ +function test() { + function mk(i, j, k) { return i*10000+j*100+k; } + + var N = 10; + var M = 20; + var O = 30; + + print("Computing"); + var p = new ParallelArray([N,M,O], mk); + + print("Checking"); + for (var i = 0; i < N; i++) { + for (var j = 0; j < M; j++) { + for (var k = 0; k < O; k++) { + assertEq(p.get(i, j, k), mk(i, j, k)); + assertEq(p.get(i, j).get(k), mk(i, j, k)); + assertEq(p.get(i).get(j).get(k), mk(i, j, k)); + assertEq(p.get(i).get(j, k), mk(i, j, k)); + } + } + } +} + +test(); diff --git a/js/src/jit-test/tests/parallelarray/index-4.js b/js/src/jit-test/tests/parallelarray/index-4.js new file mode 100644 index 00000000000..2b3d8c1a6af --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/index-4.js @@ -0,0 +1,26 @@ +function test() { + function mk(i, j, k, l) { return i*1000000+j*10000+k*100+l; } + + var N = 2; + var M = 4; + var O = 6; + var P = 8; + + print("Computing"); + var p = new ParallelArray([N,M,O,P], mk); + + print("Checking"); + for (var i = 0; i < N; i++) { + for (var j = 0; j < M; j++) { + for (var k = 0; k < O; k++) { + for (var l = 0; l < P; l++) { + assertEq(p.get(i, j, k, l), mk(i, j, k, l)); + assertEq(p.get(i, j).get(k, l), mk(i, j, k, l)); + assertEq(p.get(i).get(j).get(k).get(l), mk(i, j, k, l)); + } + } + } + } +} + +test(); diff --git a/js/src/jit-test/tests/parallelarray/length-3.js b/js/src/jit-test/tests/parallelarray/length-3.js index 1715432906f..8888a915baf 100644 --- a/js/src/jit-test/tests/parallelarray/length-3.js +++ b/js/src/jit-test/tests/parallelarray/length-3.js @@ -6,4 +6,5 @@ function testLength() { assertEq(p.length, 4); } -testLength(); +// FIXME(bug 844988) immutability not enforced +// testLength(); diff --git a/js/src/jit-test/tests/parallelarray/mandelbrot.js b/js/src/jit-test/tests/parallelarray/mandelbrot.js new file mode 100644 index 00000000000..6f32a079543 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/mandelbrot.js @@ -0,0 +1,49 @@ +// Adapted from +// +// https://github.com/RiverTrail/RiverTrail/blob/master/examples/mandelbrot/mandelbrot.js +// +// which in turn is adapted from a WebCL implementation available at +// +// http://www.ibiblio.org/e-notes/webcl/mandelbrot.html + +load(libdir + "parallelarray-helpers.js"); + +var nc = 30, maxCol = nc*3; + +// this is the actual mandelbrot computation, ported to JavaScript +// from the WebCL / OpenCL example at +// http://www.ibiblio.org/e-notes/webcl/mandelbrot.html +function computeSetByRow(x, y) { + var Cr = (x - 256) / scale + 0.407476; + var Ci = (y - 256) / scale + 0.234204; + var I = 0, R = 0, I2 = 0, R2 = 0; + var n = 0; + while ((R2+I2 < 2.0) && (n < 512)) { + I = (R+R)*I+Ci; + R = R2-I2+Cr; + R2 = R*R; + I2 = I*I; + n++; + } + return n; +} + +function computeSequentially() { + result = []; + for (var r = 0; r < rows; r++) { + for (var c = 0; c < cols; c++) { + result.push(computeSetByRow(r, c)); + } + } + return result; +} + +var scale = 10000*300; +var rows = 4; +var cols = 4; + +// check that we get correct result +assertParallelArrayModesEq(["seq", "par"], computeSequentially(), function(m) { + r = new ParallelArray([rows, cols], computeSetByRow); + return r.flatten(); +}); diff --git a/js/src/jit-test/tests/parallelarray/map-1.js b/js/src/jit-test/tests/parallelarray/map-1.js deleted file mode 100644 index 207caafb2e1..00000000000 --- a/js/src/jit-test/tests/parallelarray/map-1.js +++ /dev/null @@ -1,10 +0,0 @@ -load(libdir + "parallelarray-helpers.js"); - -function testMap() { - var p = new ParallelArray([0,1,2,3,4]); - var m = p.map(function (v) { return v+1; }); - var p2 = new ParallelArray([1,2,3,4,5]); - assertEqParallelArray(m, p2); -} - -testMap(); diff --git a/js/src/jit-test/tests/parallelarray/map-2.js b/js/src/jit-test/tests/parallelarray/map-2.js index 88fb3272972..c313023621a 100644 --- a/js/src/jit-test/tests/parallelarray/map-2.js +++ b/js/src/jit-test/tests/parallelarray/map-2.js @@ -2,7 +2,7 @@ function testMap() { // Test overflow var p = new ParallelArray([1 << 30]); var m = p.map(function(x) { return x * 4; }); - assertEq(m[0], (1 << 30) * 4); + assertEq(m.get(0), (1 << 30) * 4); } testMap(); diff --git a/js/src/jit-test/tests/parallelarray/map-3.js b/js/src/jit-test/tests/parallelarray/map-3.js index d213ab63853..7ab684b521e 100644 --- a/js/src/jit-test/tests/parallelarray/map-3.js +++ b/js/src/jit-test/tests/parallelarray/map-3.js @@ -4,7 +4,7 @@ function testMap() { // Test mapping higher dimensional var p = new ParallelArray([2,2], function (i,j) { return i+j; }); var m = p.map(function(x) { return x; }); - var p2 = new ParallelArray(p); + var p2 = new ParallelArray(p.shape[0], function (i) { return p.get(i); }); assertEqParallelArray(m, p2); } diff --git a/js/src/jit-test/tests/parallelarray/map-add-from-upvar-field.js b/js/src/jit-test/tests/parallelarray/map-add-from-upvar-field.js new file mode 100644 index 00000000000..f1c68503ec2 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/map-add-from-upvar-field.js @@ -0,0 +1,12 @@ +load(libdir + "parallelarray-helpers.js"); + +var SIZE = 4096; + +function testMap() { + var q = {f: 22}; + compareAgainstArray(range(0, SIZE), "map", function(e) { + return e + q.f; + }); +} + +testMap(); diff --git a/js/src/jit-test/tests/parallelarray/map-factorial.js b/js/src/jit-test/tests/parallelarray/map-factorial.js new file mode 100644 index 00000000000..dfee853f92e --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/map-factorial.js @@ -0,0 +1,9 @@ +load(libdir + "parallelarray-helpers.js"); + +function factorial(n) { + if (n == 0) + return 1; + return n * factorial(n - 1); +} + +compareAgainstArray(range(0, 64), "map", factorial); diff --git a/js/src/jit-test/tests/parallelarray/map-inc.js b/js/src/jit-test/tests/parallelarray/map-inc.js new file mode 100644 index 00000000000..ef3a5843b9c --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/map-inc.js @@ -0,0 +1,3 @@ +load(libdir + "parallelarray-helpers.js"); + +compareAgainstArray(range(0, 512), "map", function(e) { return e+1; }); diff --git a/js/src/jit-test/tests/parallelarray/map-invalid-mode-specifier.js b/js/src/jit-test/tests/parallelarray/map-invalid-mode-specifier.js new file mode 100644 index 00000000000..93a6ec70ade --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/map-invalid-mode-specifier.js @@ -0,0 +1,16 @@ +// |jit-test| error: Error; + +// This test tests the mode assertions. It provides a function which +// should successfully execute and specifies that it should fail. We +// expect an exception to result. + +load(libdir + "parallelarray-helpers.js"); + +function testMap() { + var p = new ParallelArray(range(0, 64)); + var m = p.map(function (v) { return v+1; }, { mode: "par", expect: "fail" }); + assertEqParallelArray(m, new ParallelArray(range(1, 64))); +} + +testMap(); + diff --git a/js/src/jit-test/tests/parallelarray/map-nested.js b/js/src/jit-test/tests/parallelarray/map-nested.js new file mode 100644 index 00000000000..001b037dd64 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/map-nested.js @@ -0,0 +1,17 @@ +load(libdir + "parallelarray-helpers.js") + +function test() { + var pa0 = new ParallelArray(range(0, 256)); + var pa1 = new ParallelArray(256, function (x) { + return pa0.map(function(y) { return x * 1000 + y; }); + }, {mode: "par", expect: "success"}); + + for (var x = 0; x < 256; x++) { + var pax = pa1.get(x); + for (var y = 0; y < 256; y++) { + assertEq(pax.get(y), x * 1000 + y); + } + } +} + +test(); diff --git a/js/src/jit-test/tests/parallelarray/map-parallel-assign-to-def-prop.js b/js/src/jit-test/tests/parallelarray/map-parallel-assign-to-def-prop.js new file mode 100644 index 00000000000..e7890c61e7a --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/map-parallel-assign-to-def-prop.js @@ -0,0 +1,9 @@ +load(libdir + "parallelarray-helpers.js"); + +function wrapInObject(v) { + var obj = {f: 2}; + obj.f += v; + return obj; +} + +compareAgainstArray(range(0, 64), "map", wrapInObject); diff --git a/js/src/jit-test/tests/parallelarray/map-short.js b/js/src/jit-test/tests/parallelarray/map-short.js new file mode 100644 index 00000000000..481a90fc50e --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/map-short.js @@ -0,0 +1,11 @@ +load(libdir + "parallelarray-helpers.js"); + +function test() { + // Test what happens if the length of the array is very short (i.e., + // less than the number of cores). There used to be a bug in this + // case that led to crashes or other undefined behavior. + var makeadd1 = function (v) { return [v]; } + compareAgainstArray(range(1, 3), "map", makeadd1); +} + +test(); diff --git a/js/src/jit-test/tests/parallelarray/nested-sum-each-row.js b/js/src/jit-test/tests/parallelarray/nested-sum-each-row.js new file mode 100644 index 00000000000..2d14e75cc85 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/nested-sum-each-row.js @@ -0,0 +1,20 @@ +load(libdir + "parallelarray-helpers.js"); + +function test() { + var array1 = range(0, 512); + var array2 = build(512, function(i) { + return i*1000000 + array1.reduce(sum); + }); + + + var parray1 = new ParallelArray(array1); + var parray2 = new ParallelArray(512, function(i) { + return i*1000000 + parray1.reduce(sum); + }); + + assertStructuralEq(array2, parray2); + + function sum(a, b) { return a+b; } +} + +test(); diff --git a/js/src/jit-test/tests/parallelarray/partition-1.js b/js/src/jit-test/tests/parallelarray/partition-1.js index 65208de7a63..36a9b677a3a 100644 --- a/js/src/jit-test/tests/parallelarray/partition-1.js +++ b/js/src/jit-test/tests/parallelarray/partition-1.js @@ -7,9 +7,9 @@ function testPartition() { var ppShape = [p.shape[0] / 2, 2].concat(p.shape.slice(1)); var pppShape = [pp.shape[0] / 2, 2].concat(pp.shape.slice(1)); assertEqArray(pp.shape, ppShape); - assertEq(pp.toString(), "<<1,2>,<3,4>,<5,6>,<7,8>>"); + assertEq(pp.toString(), "<1,2>,<3,4>,<5,6>,<7,8>"); assertEqArray(ppp.shape, pppShape); - assertEq(ppp.toString(), "<<<1,2>,<3,4>>,<<5,6>,<7,8>>>"); + assertEq(ppp.toString(), "<<1,2>,<3,4>>,<<5,6>,<7,8>>"); } testPartition(); diff --git a/js/src/jit-test/tests/parallelarray/reduce-1.js b/js/src/jit-test/tests/parallelarray/reduce-1.js deleted file mode 100644 index 802b5042ba5..00000000000 --- a/js/src/jit-test/tests/parallelarray/reduce-1.js +++ /dev/null @@ -1,8 +0,0 @@ - -function testReduce() { - var p = new ParallelArray([1,2,3,4,5]); - var r = p.reduce(function (v, p) { return v*p; }); - assertEq(r, 120); -} - -testReduce(); diff --git a/js/src/jit-test/tests/parallelarray/reduce-2.js b/js/src/jit-test/tests/parallelarray/reduce-2.js deleted file mode 100644 index d0d4030d2ec..00000000000 --- a/js/src/jit-test/tests/parallelarray/reduce-2.js +++ /dev/null @@ -1,8 +0,0 @@ - -function testReduceOne() { - var p = new ParallelArray([1]); - var r = p.reduce(function (v, p) { return v*p; }); - assertEq(r, 1); -} - -testReduceOne(); diff --git a/js/src/jit-test/tests/parallelarray/reduce-bail.js b/js/src/jit-test/tests/parallelarray/reduce-bail.js new file mode 100644 index 00000000000..1d4d25a9197 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/reduce-bail.js @@ -0,0 +1,31 @@ +load(libdir + "parallelarray-helpers.js"); + +// Tests that reduce saves its intermediate state correctly by +// inducing a bailout in the middle of the reduction. This test may +// fail to test what it is intended to test if the wrong number of +// worker threads etc are present. It reproduced an existing bug when +// used with 8 worker threads. + +function testReduce() { + var aCounter = 0; + function sum(a, b) { + var r = a + b; + if (r == 234) // occurs once per slice + aCounter++; + return r; + } + + var array = build(4096, function() { return 1; }); + var seqResult = array.reduce(sum); + var seqCounter = aCounter; + + aCounter = 0; + var parray = new ParallelArray(array); + var parResult = parray.reduce(sum); + var parCounter = aCounter; + + assertEq(true, parCounter >= seqCounter); + assertStructuralEq(parResult, seqResult); +} + +testReduce(); diff --git a/js/src/jit-test/tests/parallelarray/reduce-fn-args.js b/js/src/jit-test/tests/parallelarray/reduce-fn-args.js index 74c7848b189..e22082588d2 100644 --- a/js/src/jit-test/tests/parallelarray/reduce-fn-args.js +++ b/js/src/jit-test/tests/parallelarray/reduce-fn-args.js @@ -1,13 +1,15 @@ +load(libdir + "parallelarray-helpers.js"); function testReduce() { // Test reduce elemental fun args - var p = new ParallelArray([1,2,3,4]); + var N = 64; + var p = new ParallelArray(range(1, N+1)); var r = p.reduce(function (a, b) { - assertEq(a >= 1 && a <= 4, true); - assertEq(b >= 1 && b <= 4, true); + assertEq(a >= 1 && a <= N, true); + assertEq(b >= 1 && b <= N, true); return a; }); - assertEq(r >= 1 && r <= 4, true); + assertEq(r >= 1 && r <= N, true); } testReduce(); diff --git a/js/src/jit-test/tests/parallelarray/reduce-3.js b/js/src/jit-test/tests/parallelarray/reduce-higher-dim.js similarity index 100% rename from js/src/jit-test/tests/parallelarray/reduce-3.js rename to js/src/jit-test/tests/parallelarray/reduce-higher-dim.js diff --git a/js/src/jit-test/tests/parallelarray/reduce-length-one.js b/js/src/jit-test/tests/parallelarray/reduce-length-one.js new file mode 100644 index 00000000000..7e31963dde9 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/reduce-length-one.js @@ -0,0 +1,9 @@ +function testReduceOne() { + // Note: parallel execution with only 1 element will generally + // bailout, so don't use assertParallelArrayModesEq() here. + var p = new ParallelArray([1]); + var r = p.reduce(function (v, p) { return v*p; }); + assertEq(r, 1); +} + +testReduceOne(); diff --git a/js/src/jit-test/tests/parallelarray/reduce-mul-short.js b/js/src/jit-test/tests/parallelarray/reduce-mul-short.js new file mode 100644 index 00000000000..e4fa8ab80cd --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/reduce-mul-short.js @@ -0,0 +1,26 @@ +load(libdir + "parallelarray-helpers.js"); + +function testReduce() { + // This test is interesting because during warmup v*p remains an + // integer but this ceases to be true once real execution proceeds. + // By the end, it will just be some double value. + + function mul(v, p) { return v*p; } + + // Ensure that the array only contains values between 1 and 4. + var array = range(1, 513).map(function(v) { return (v % 4) + 1; }); + var expected = array.reduce(mul); + print(expected); + + var parray = new ParallelArray(array); + var results = ["success", "mixed", "success", "success"]; + var actual; + for (var i = 0; i < results.length; i++) { + print("Iteration ", i, " expects ", results[i]); + actual = parray.reduce(mul, {mode: "par", expect: results[i]}); + } + + assertAlmostEq(actual, expected); +} + +testReduce(); diff --git a/js/src/jit-test/tests/parallelarray/reduce-mul.js b/js/src/jit-test/tests/parallelarray/reduce-mul.js new file mode 100644 index 00000000000..227fffdf7f0 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/reduce-mul.js @@ -0,0 +1,22 @@ +load(libdir + "parallelarray-helpers.js"); + +function testReduce() { + // This test is interesting because during warmup v*p remains an + // integer but this ceases to be true once real execution proceeds. + // By the end, it will just be infinity. + function mul(v, p) { return v*p; } + + var array = range(1, 513); + var expected = array.reduce(mul); + var parray = new ParallelArray(array); + var modes = ["seq", "par"]; + for (var i = 0; i < 2; i++) { + assertAlmostEq(expected, parray.reduce(mul, {mode: modes[i], expect: "success"})); + } + // assertParallelArrayModesEq(["seq", "par"], expected, function(m) { + // d = parray.reduce(sum, m); + // return d; + // }); +} + +testReduce(); diff --git a/js/src/jit-test/tests/parallelarray/reduce-sum.js b/js/src/jit-test/tests/parallelarray/reduce-sum.js new file mode 100644 index 00000000000..ac032f40d3c --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/reduce-sum.js @@ -0,0 +1,8 @@ +load(libdir + "parallelarray-helpers.js"); + +function testReduce() { + function sum(v, p) { return v+p; } + compareAgainstArray(range(1, 513), "reduce", sum); +} + +testReduce(); diff --git a/js/src/jit-test/tests/parallelarray/reduce-throws.js b/js/src/jit-test/tests/parallelarray/reduce-throws.js index 5afe9337c9a..784f8bbabd1 100644 --- a/js/src/jit-test/tests/parallelarray/reduce-throws.js +++ b/js/src/jit-test/tests/parallelarray/reduce-throws.js @@ -13,4 +13,5 @@ function testReduceThrows() { }, TypeError); } -testReduceThrows(); +// FIXME(bug 844886) sanity check argument types +// testReduceThrows(); diff --git a/js/src/jit-test/tests/parallelarray/scan-1.js b/js/src/jit-test/tests/parallelarray/scan-1.js index cf6a4ee86a8..79c5528289a 100644 --- a/js/src/jit-test/tests/parallelarray/scan-1.js +++ b/js/src/jit-test/tests/parallelarray/scan-1.js @@ -1,13 +1,3 @@ - -function testScan() { - function f(v, p) { return v*p; } - var a = [1,2,3,4,5]; - var p = new ParallelArray(a); - var s = p.scan(f); - for (var i = 0; i < p.length; i++) { - var p2 = new ParallelArray(a.slice(0, i+1)); - assertEq(s[i], p2.reduce(f)); - } -} - -testScan(); +load(libdir + "parallelarray-helpers.js"); +function sum(a, b) { return a+b; } +testScan(range(1, 1024), sum); diff --git a/js/src/jit-test/tests/parallelarray/scan-2.js b/js/src/jit-test/tests/parallelarray/scan-2.js index f134dd2a303..f63d4a86c65 100644 --- a/js/src/jit-test/tests/parallelarray/scan-2.js +++ b/js/src/jit-test/tests/parallelarray/scan-2.js @@ -1,9 +1,9 @@ - function testScanOne() { - function f(v, p) { return v*p; } + function f(v, p) { throw "This should never be called."; } var p = new ParallelArray([1]); var s = p.scan(f); - assertEq(s[0], p.reduce(f)); + assertEq(s.get(0), 1); + assertEq(s.get(0), p.reduce(f)); } testScanOne(); diff --git a/js/src/jit-test/tests/parallelarray/scan-3.js b/js/src/jit-test/tests/parallelarray/scan-3.js index 34b80af1769..380c778bcda 100644 --- a/js/src/jit-test/tests/parallelarray/scan-3.js +++ b/js/src/jit-test/tests/parallelarray/scan-3.js @@ -1,4 +1,3 @@ - function testScan() { // Test reduce on higher dimensional // XXX Can we test this better? @@ -10,7 +9,7 @@ function testScan() { var r = p.reduce(f); var s = p.scan(f) for (var j = 0; j < s.length; j++) - assertEq(s[j].shape.length, i + 1); + assertEq(s.get(j).shape.length, i + 1); assertEq(r.shape.length, i + 1); } } diff --git a/js/src/jit-test/tests/parallelarray/scan-fn-args.js b/js/src/jit-test/tests/parallelarray/scan-fn-args.js index baf1ec464ac..b1d3c374309 100644 --- a/js/src/jit-test/tests/parallelarray/scan-fn-args.js +++ b/js/src/jit-test/tests/parallelarray/scan-fn-args.js @@ -1,4 +1,3 @@ - function testScan() { // Test reduce elemental fun args var p = new ParallelArray([1,2,3,4]); diff --git a/js/src/jit-test/tests/parallelarray/scan-sorted.js b/js/src/jit-test/tests/parallelarray/scan-sorted.js new file mode 100644 index 00000000000..6f597c60e7d --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/scan-sorted.js @@ -0,0 +1,37 @@ +// |jit-test| slow; + +load(libdir + "parallelarray-helpers.js"); + +function mergeSorted(l1, l2) { + var result = []; + var i1 = 0, i2 = 0, j = 0; + while (i1 < l1.length && i2 < l2.length) { + if (l1[i1] < l2[i2]) + result[j++] = l1[i1++]; + else + result[j++] = l2[i2++]; + } + while (i1 < l1.length) { + result[j++] = l1[i1++]; + } + while (i2 < l2.length) { + result[j++] = l2[i2++]; + } + return result; +} + +function test() { + var elts = []; + var ints = range(1, 5), c = 0; + + // Using 2048 as the length of elts induces bailouts due to GC. + // This exposed various bugs. + for (var i = 0; i < 2048; i++) + elts[i] = ints; + + var scanned1 = seq_scan(elts, mergeSorted); + var scanned2 = new ParallelArray(elts).scan(mergeSorted); + assertStructuralEq(scanned1, scanned2); +} + +test(); diff --git a/js/src/jit-test/tests/parallelarray/scan-throws.js b/js/src/jit-test/tests/parallelarray/scan-throws.js index cb701af0237..9c85864ed7e 100644 --- a/js/src/jit-test/tests/parallelarray/scan-throws.js +++ b/js/src/jit-test/tests/parallelarray/scan-throws.js @@ -6,6 +6,7 @@ function testScanThrows() { var p = new ParallelArray([]); p.scan(function (v, p) { return v*p; }); }, Error); + // Throw on not function assertThrowsInstanceOf(function () { var p = new ParallelArray([1]); @@ -13,4 +14,5 @@ function testScanThrows() { }, TypeError); } -testScanThrows(); +// FIXME(bug 844886) sanity check argument types +// testScanThrows(); diff --git a/js/src/jit-test/tests/parallelarray/scatter-10.js b/js/src/jit-test/tests/parallelarray/scatter-10.js new file mode 100644 index 00000000000..1e88bc5f202 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/scatter-10.js @@ -0,0 +1,28 @@ +load(libdir + "parallelarray-helpers.js"); + +// Test specific scatter implementation strategies, and compare them +// each against the sequential version. +// +// This is just a simple reverse permutation of the input: +// [A, B, ..., Y, Z] ==> [Z, Y, ..., B, A] + +function testDivideScatterVector() { + var len = 1024; + function add1(x) { return x+1; } + function id(x) { return x; } + var p = new ParallelArray(len, add1); + var revidx = build(len, id).reverse(); + var p2 = new ParallelArray(revidx.map(add1)); + var modes = [["seq", ""], + ["par", "divide-scatter-vector"], + ["par", "divide-output-range"]]; + for (var i = 0; i < 3; i++) { + print(modes[i][1]); + var r = p.scatter(revidx, 0, undefined, len, + {mode: modes[i][0], strategy: modes[i][1], + expect: "success"}); + assertEqParallelArray(r, p2); + } +} + +testDivideScatterVector(); diff --git a/js/src/jit-test/tests/parallelarray/scatter-11.js b/js/src/jit-test/tests/parallelarray/scatter-11.js new file mode 100644 index 00000000000..f170879b9a6 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/scatter-11.js @@ -0,0 +1,28 @@ +load(libdir + "parallelarray-helpers.js"); + +// Test specific scatter implementation strategies, and compare them +// each against the sequential version. +// +// This is a reverse permutation that has a gap at the end. +// [A, B, ..., Y, Z] ==> [Z, Y, ..., B, A, 0] + +function testDivideScatterVector() { + var len = 1024; + function add1(x) { return x+1; } + function id(x) { return x; } + var p = new ParallelArray(len, add1); + var revidx = build(len, id).reverse(); + var p2 = new ParallelArray(revidx.map(add1).concat([0])); + var modes = [["seq", ""], + ["par", "divide-scatter-vector"], + ["par", "divide-output-range"]]; + for (var i = 0; i < 3; i++) { + var r = p.scatter(revidx, 0, undefined, len+1, + {mode: modes[i][0], strategy: modes[i][1], + expect: "success"}); + print(modes[i][1], r.buffer); + assertEqParallelArray(r, p2); + } +} + +testDivideScatterVector(); diff --git a/js/src/jit-test/tests/parallelarray/scatter-12.js b/js/src/jit-test/tests/parallelarray/scatter-12.js new file mode 100644 index 00000000000..b0e00839093 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/scatter-12.js @@ -0,0 +1,27 @@ +load(libdir + "parallelarray-helpers.js"); + +// Test specific scatter implementation strategies, and compare them +// each against the sequential version. +// +// This is a reverse permutation that has a gap at the start and at the end. +// [A, B, ..., Y, Z] ==> [0, Z, Y, ..., B, A, 0] + +function testDivideScatterVector() { + var len = 1024; + function add1(x) { return x+1; } + function id(x) { return x; } + var p = new ParallelArray(len, add1); + var revidx = build(len, add1).reverse(); + var p2 = new ParallelArray([0].concat(revidx).concat([0])); + var modes = [["seq", ""], + ["par", "divide-scatter-vector"], + ["par", "divide-output-range"]]; + for (var i = 0; i < 3; i++) { + var r = p.scatter(revidx, 0, undefined, len+2, + {mode: modes[i][0], strategy: modes[i][1], + expect: "success"}); + assertEqParallelArray(r, p2); + } +} + +testDivideScatterVector(); diff --git a/js/src/jit-test/tests/parallelarray/scatter-13.js b/js/src/jit-test/tests/parallelarray/scatter-13.js new file mode 100644 index 00000000000..e47bb5b1650 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/scatter-13.js @@ -0,0 +1,34 @@ +load(libdir + "parallelarray-helpers.js"); + +// Test specific scatter implementation strategies, and compare them +// each against the sequential version. +// +// This is a reverse permutation of the input with collisions at front and end +// [A, B, C, D, ..., W, X, Y, Z] ==> [Z+Y, X, W, ..., D, C, B+A] + +function testDivideScatterVector() { + var len = 1024; + function add1(x) { return x+1; } + function add3(x) { return x+3; } + function id(x) { return x; } + var p = new ParallelArray(len, add1); + var idx = [0,0].concat(build(len-4, add1)).concat([len-3,len-3]); + var revidx = idx.reverse(); + var p2 = [3].concat(build(len-4, add3)).concat([2*len-1]); + var expect = new ParallelArray(p2.reverse()); + var modes = [["seq", ""], + ["par", "divide-scatter-vector"], + ["par", "divide-output-range"]]; + for (var i = 0; i < 3; i++) { + print("i:"+i+" p :"+p); + print("i:"+i+" revidx :"+revidx); + var r = p.scatter(revidx, 0, function (x,y) { return x+y; }, len-2, + {mode: modes[i][0], strategy: modes[i][1], + expect: "success", print:print}); + print("i:"+i+" r :"+r); + print("i:"+i+" expect:"+expect); + assertEqParallelArray(r, expect); + } +} + +testDivideScatterVector(); diff --git a/js/src/jit-test/tests/parallelarray/scatter-7.js b/js/src/jit-test/tests/parallelarray/scatter-7.js index e149071287f..f3ddd91ba05 100644 --- a/js/src/jit-test/tests/parallelarray/scatter-7.js +++ b/js/src/jit-test/tests/parallelarray/scatter-7.js @@ -3,10 +3,10 @@ load(libdir + "parallelarray-helpers.js"); function testScatterIdentity() { var shape = [5]; for (var i = 0; i < 7; i++) { - shape.push(i+1); + shape.push(2); var p = new ParallelArray(shape, function(k) { return k; }); var r = p.scatter([0,1,2,3,4]); - var p2 = new ParallelArray([p[0], p[1], p[2], p[3], p[4]]); + var p2 = new ParallelArray([p.get(0), p.get(1), p.get(2), p.get(3), p.get(4)]); assertEqParallelArray(p2, r); } } diff --git a/js/src/jit-test/tests/parallelarray/scatter-8.js b/js/src/jit-test/tests/parallelarray/scatter-8.js index 6ab34d7934b..a4458990832 100644 --- a/js/src/jit-test/tests/parallelarray/scatter-8.js +++ b/js/src/jit-test/tests/parallelarray/scatter-8.js @@ -3,10 +3,10 @@ load(libdir + "parallelarray-helpers.js"); function testScatter() { var shape = [5]; for (var i = 0; i < 7; i++) { - shape.push(i+1); + shape.push(2); var p = new ParallelArray(shape, function(k) { return k; }); var r = p.scatter([1,0,3,2,4]); - var p2 = new ParallelArray([p[1], p[0], p[3], p[2], p[4]]); + var p2 = new ParallelArray([p.get(1), p.get(0), p.get(3), p.get(2), p.get(4)]); assertEqParallelArray(p2, r); } } diff --git a/js/src/jit-test/tests/parallelarray/scatter-9.js b/js/src/jit-test/tests/parallelarray/scatter-9.js index dcfe4d65d69..146b3ef8fc4 100644 --- a/js/src/jit-test/tests/parallelarray/scatter-9.js +++ b/js/src/jit-test/tests/parallelarray/scatter-9.js @@ -7,3 +7,5 @@ function testScatter() { var p2 = new ParallelArray([2,1,4,3,5]); assertEqParallelArray(r, p2); } + +testScatter(); diff --git a/js/src/jit-test/tests/parallelarray/scatter-regression-1.js b/js/src/jit-test/tests/parallelarray/scatter-regression-1.js new file mode 100644 index 00000000000..22b59c8045f --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/scatter-regression-1.js @@ -0,0 +1,17 @@ +load(libdir + "parallelarray-helpers.js"); + +// The bug this is testing for: +// the input p and the scatter vector have length 4 +// and the output p2 has length 3. Even though p2 +// is shorter than the input lengths, we still need +// to scan the entirety of the scatter vector, because +// it may hold valid targets at distant indices. +function testScatter() { + var p = new ParallelArray([2,3,5,17]); + var r = p.scatter([0,0,2,1], 9, function (x,y) { return x * y; }, 3); + var p2 = new ParallelArray([6,17,5]); + assertEqParallelArray(r, p2); +} + +testScatter(); + diff --git a/js/src/jit-test/tests/parallelarray/scatter-throws.js b/js/src/jit-test/tests/parallelarray/scatter-throws.js index e3543b682e8..dd8119eb6c7 100644 --- a/js/src/jit-test/tests/parallelarray/scatter-throws.js +++ b/js/src/jit-test/tests/parallelarray/scatter-throws.js @@ -27,4 +27,5 @@ function testScatterThrows() { } -testScatterThrows(); +// FIXME(bug 844886) sanity check argument types +// testScatterThrows(); diff --git a/js/src/jit-test/tests/parallelarray/shape-3.js b/js/src/jit-test/tests/parallelarray/shape-3.js new file mode 100644 index 00000000000..b597eb73515 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/shape-3.js @@ -0,0 +1,16 @@ +load(libdir + "eqArrayHelper.js"); + +function testInfer() { + // Test that shapes are inferred + var p0 = new ParallelArray([0,1]); + var p1 = new ParallelArray([2,3]); + var p = new ParallelArray([p0, p1]); + assertEqArray(p.shape, [2,2]); + var p0 = new ParallelArray([0,1]); + var p1 = new ParallelArray([2]); + var p = new ParallelArray([p0, p1]); + assertEqArray(p.shape, [2]); +} + +// FIXME(bug 844991) logical shape not implemented +// testInfer(); diff --git a/js/src/jit-test/tests/parallelarray/shape-4.js b/js/src/jit-test/tests/parallelarray/shape-4.js index 8f163276017..7114852956d 100644 --- a/js/src/jit-test/tests/parallelarray/shape-4.js +++ b/js/src/jit-test/tests/parallelarray/shape-4.js @@ -9,4 +9,5 @@ function testShape() { assertEqArray(p.shape, [4]); } -testShape(); +// FIXME(bug 844988) immutability not enforced +// testShape(); diff --git a/js/src/jit-test/tests/parallelarray/shape-5.js b/js/src/jit-test/tests/parallelarray/shape-5.js new file mode 100644 index 00000000000..7114852956d --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/shape-5.js @@ -0,0 +1,13 @@ +load(libdir + "eqArrayHelper.js"); + +function testShape() { + // Test shape immutability. + var p = new ParallelArray([1,2,3,4]); + p.shape[0] = 0; + p.shape[1] = 42; + assertEq(p[0], 1); + assertEqArray(p.shape, [4]); +} + +// FIXME(bug 844988) immutability not enforced +// testShape(); diff --git a/js/src/jit-test/tests/parallelarray/stack-overflow.js b/js/src/jit-test/tests/parallelarray/stack-overflow.js new file mode 100644 index 00000000000..e605913ab5d --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/stack-overflow.js @@ -0,0 +1,17 @@ +load(libdir + "parallelarray-helpers.js"); + +function kernel(n) { + // in parallel mode just recurse infinitely. + if (n > 10 && inParallelSection()) + return kernel(n); + + return n+1; +} + +function testMap() { + var p = new ParallelArray(range(0, 2048)); + p.map(kernel, { mode: "par", expect: "disqualified" }); +} + +testMap(); + diff --git a/js/src/jit-test/tests/parallelarray/strict-equals-1.js b/js/src/jit-test/tests/parallelarray/strict-equals-1.js new file mode 100644 index 00000000000..a1f3499dc3d --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/strict-equals-1.js @@ -0,0 +1,13 @@ +function testEquals() { + var p = new ParallelArray; + assertEq(p, p); + // Test we always rewrap shape-internal PAs + var p2 = new ParallelArray([2,2], function (i,j) { return i+j; }); + assertEq(p2[0] !== p2[0], true); + assertEq(p2[1] !== p2[1], true); + var p3 = new ParallelArray([new ParallelArray([0]), new ParallelArray([1])]); + assertEq(p3[0] !== p3[0], true); +} + +// FIXME(bug 844991) logical shape not implemented +// testEquals(); diff --git a/js/src/jit-test/tests/parallelarray/surfaces-1.js b/js/src/jit-test/tests/parallelarray/surfaces-1.js index be0acc4f0c5..b786082a335 100644 --- a/js/src/jit-test/tests/parallelarray/surfaces-1.js +++ b/js/src/jit-test/tests/parallelarray/surfaces-1.js @@ -2,24 +2,25 @@ load(libdir + "eqArrayHelper.js"); // ParallelArray surfaces -var desc = Object.getOwnPropertyDescriptor(this, "ParallelArray"); -assertEq(desc.enumerable, false); -assertEq(desc.configurable, true); -assertEq(desc.writable, true); +function test() { + var desc = Object.getOwnPropertyDescriptor(this, "ParallelArray"); + assertEq(desc.enumerable, false); + assertEq(desc.configurable, true); + assertEq(desc.writable, true); -assertEq(typeof ParallelArray, 'function'); -assertEq(Object.keys(ParallelArray).length, 0); -assertEq(ParallelArray.length, 0); -assertEq(ParallelArray.name, "ParallelArray"); + assertEq(typeof ParallelArray, 'function'); + assertEq(Object.keys(ParallelArray).length, 0); + assertEq(ParallelArray.length, 0); + assertEq(ParallelArray.name, "ParallelArray"); -assertEq(Object.getPrototypeOf(ParallelArray.prototype), Object.prototype); -assertEq(Object.prototype.toString.call(ParallelArray.prototype), "[object ParallelArray]"); -assertEq(Object.prototype.toString.call(new ParallelArray), "[object ParallelArray]"); -assertEq(Object.prototype.toString.call(ParallelArray()), "[object ParallelArray]"); -assertEq(Object.keys(ParallelArray.prototype).join(), ""); -assertEq(ParallelArray.prototype.constructor, ParallelArray); + assertEq(Object.getPrototypeOf(ParallelArray.prototype), Object.prototype); + assertEq(Object.prototype.toString.call(ParallelArray.prototype), "[object ParallelArray]"); + assertEq(Object.prototype.toString.call(new ParallelArray), "[object ParallelArray]"); + assertEq(Object.prototype.toString.call(ParallelArray()), "[object ParallelArray]"); + assertEq(Object.keys(ParallelArray.prototype).join(), ""); + assertEq(ParallelArray.prototype.constructor, ParallelArray); -function checkMethod(name, arity) { + function checkMethod(name, arity) { var desc = Object.getOwnPropertyDescriptor(ParallelArray.prototype, name); assertEq(desc.enumerable, false); assertEq(desc.configurable, true); @@ -27,24 +28,28 @@ function checkMethod(name, arity) { assertEq(typeof desc.value, 'function'); assertEq(desc.value.name, name); assertEq(desc.value.length, arity); + } + + checkMethod("map", 1); + checkMethod("reduce", 1); + checkMethod("scan", 1); + checkMethod("scatter", 1); + checkMethod("filter", 1); + checkMethod("flatten", 0); + checkMethod("partition", 1); + checkMethod("get", 1); + + function checkAccessor(name) { + var desc = Object.getOwnPropertyDescriptor(ParallelArray.prototype, name); + assertEq(desc.enumerable, false); + assertEq(desc.configurable, false); + assertEq(typeof desc.get, 'function'); + assertEq(desc.set, undefined); + } + + checkAccessor("length"); + checkAccessor("shape"); } -checkMethod("map", 1); -checkMethod("reduce", 1); -checkMethod("scan", 1); -checkMethod("scatter", 1); -checkMethod("filter", 1); -checkMethod("flatten", 0); -checkMethod("partition", 1); -checkMethod("get", 1); - -function checkAccessor(name) { - var desc = Object.getOwnPropertyDescriptor(ParallelArray.prototype, name); - assertEq(desc.enumerable, false); - assertEq(desc.configurable, false); - assertEq(typeof desc.get, 'function'); - assertEq(desc.set, undefined); -} - -checkAccessor("length"); -checkAccessor("shape"); +// FIXME(bug 844882) self-hosted object not array-like, exposes internal properties +// test(); diff --git a/js/src/jit-test/tests/parallelarray/surfaces-2.js b/js/src/jit-test/tests/parallelarray/surfaces-2.js index b9cb3722f6d..309f26cd9e5 100644 --- a/js/src/jit-test/tests/parallelarray/surfaces-2.js +++ b/js/src/jit-test/tests/parallelarray/surfaces-2.js @@ -20,8 +20,9 @@ function test(obj) { testcase(obj, ParallelArray.prototype.get, [1]); } -test(ParallelArray.prototype); -test(Object.create(new ParallelArray)); -test({}); -test(null); -test(undefined); +// FIXME(bug 844887) check type of this +// test(ParallelArray.prototype); +// test(Object.create(new ParallelArray)); +// test({}); +// test(null); +// test(undefined); diff --git a/js/src/jit-test/tests/parallelarray/surfaces-3.js b/js/src/jit-test/tests/parallelarray/surfaces-3.js index f1f0d198aea..972abbc50a9 100644 --- a/js/src/jit-test/tests/parallelarray/surfaces-3.js +++ b/js/src/jit-test/tests/parallelarray/surfaces-3.js @@ -1,3 +1,4 @@ // ParallelArray objects are frozen. -assertEq(Object.isFrozen(new ParallelArray), true); +// FIXME(bug 844988) immutability not enforced +// assertEq(Object.isFrozen(new ParallelArray), true); diff --git a/js/src/jit-test/tests/parallelarray/throw-executed.js b/js/src/jit-test/tests/parallelarray/throw-executed.js new file mode 100644 index 00000000000..14d5de25adb --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/throw-executed.js @@ -0,0 +1,17 @@ +load(libdir + "parallelarray-helpers.js"); + +function test() { + var x = new Error(); + function inc(n) { + if (inParallelSection()) // wait until par execution, then throw + throw x; + return n + 1; + } + var x = new ParallelArray(range(0, 2048)); + + // the disqualification occurs because all parallel executions throw + // exceptions: + x.map(inc, {mode: "par", expect: "disqualified"}); +} +test(); + diff --git a/js/src/jit-test/tests/parallelarray/throw-never-executed.js b/js/src/jit-test/tests/parallelarray/throw-never-executed.js new file mode 100644 index 00000000000..ad8c828774d --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/throw-never-executed.js @@ -0,0 +1,9 @@ +load(libdir + "parallelarray-helpers.js"); + +function inc(n) { + if (n > 1024) + throw n; + return n + 1; +} + +compareAgainstArray(range(0, 512), "map", inc); diff --git a/js/src/jit-test/tests/parallelarray/timeout-gc.js b/js/src/jit-test/tests/parallelarray/timeout-gc.js new file mode 100644 index 00000000000..f56c8514936 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/timeout-gc.js @@ -0,0 +1,23 @@ +// |jit-test| exitstatus: 6; + +// One sneaky way to run GC during a parallel section is to invoke the +// gc() function during the parallel timeout! + +load(libdir + "parallelarray-helpers.js"); + +function iterate(x) { + while (x == 2046) { + // for exactly one index, this infinitely loops! + // this ensures that the warmup doesn't loop. + } + return 22; +} + +function timeoutfunc() { + print("Timed out, invoking the GC"); + gc(); + return false; +} + +timeout(1, timeoutfunc); +new ParallelArray([2048], iterate); diff --git a/js/src/jit-test/tests/parallelarray/timeout.js b/js/src/jit-test/tests/parallelarray/timeout.js new file mode 100644 index 00000000000..25be6326372 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/timeout.js @@ -0,0 +1,14 @@ +// |jit-test| exitstatus: 6; + +load(libdir + "parallelarray-helpers.js"); + +function iterate(x) { + while (x == 2046) { + // for exactly one index, this infinitely loops! + // this ensures that the warmup doesn't loop. + } + return 22; +} + +timeout(1); +new ParallelArray([2048], iterate); diff --git a/js/src/jit-test/tests/parallelarray/write-array.js b/js/src/jit-test/tests/parallelarray/write-array.js new file mode 100644 index 00000000000..db46d34699b --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/write-array.js @@ -0,0 +1,17 @@ +load(libdir + "parallelarray-helpers.js"); + +function buildSimple() { + + assertParallelArrayModesCommute(["seq", "par"], function(m) { + return new ParallelArray(256, function(i) { + let obj = [0, 1, 2]; + obj[0] += 1; + obj[1] += 1; + obj[2] += 1; + return obj; + }, m); + }); + +} + +buildSimple(); diff --git a/js/src/jit-test/tests/parallelarray/write-illegal-array-elt.js b/js/src/jit-test/tests/parallelarray/write-illegal-array-elt.js new file mode 100644 index 00000000000..20fc54ccbfa --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/write-illegal-array-elt.js @@ -0,0 +1,16 @@ +load(libdir + "parallelarray-helpers.js"); + +function testMap() { + var p = new ParallelArray(range(0, minItemsTestingThreshold)); + var v = [1]; + var func = function (e) { + v[0] = e; + return 0; + }; + + // this will compile, but fail at runtime + p.map(func, {mode: "par", expect: "disqualified"}); +} + +testMap(); + diff --git a/js/src/jit-test/tests/parallelarray/write-illegal-obj.js b/js/src/jit-test/tests/parallelarray/write-illegal-obj.js new file mode 100644 index 00000000000..21da43a57f9 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/write-illegal-obj.js @@ -0,0 +1,33 @@ +load(libdir + "parallelarray-helpers.js"); + +function buildSimple() { + + let obj = { x: 1, y: 2, z: 3 }; + + new ParallelArray(minItemsTestingThreshold, function(i) { + obj.x += 1; + obj.y += 1; + obj.z += 1; + return obj; + }, {mode: "par", expect: "disqualified"}); + + print(obj.x); + print(obj.y); + print(obj.z); + + // new ParallelArray(256, function(i) { + // var o; + // if ((i % 2) == 0) { + // o = obj; + // } else { + // o = {x: 1, y: 2, z: 3}; + // } + // o.x += 1; + // o.y += 1; + // o.z += 1; + // return o; + // }, {mode: "par", expect: "bail"}); + +} + +buildSimple(); diff --git a/js/src/jit-test/tests/parallelarray/write-obj.js b/js/src/jit-test/tests/parallelarray/write-obj.js new file mode 100644 index 00000000000..2403e2c2e68 --- /dev/null +++ b/js/src/jit-test/tests/parallelarray/write-obj.js @@ -0,0 +1,17 @@ +load(libdir + "parallelarray-helpers.js"); + +function buildSimple() { + + assertParallelArrayModesCommute(["seq", "par"], function(m) { + return new ParallelArray(256, function(i) { + let obj = { x: i, y: i + 1, z: i + 2 }; + obj.x += 1; + obj.y += 1; + obj.z += 1; + return obj; + }, m); + }); + +} + +buildSimple(); diff --git a/js/src/js.msg b/js/src/js.msg index c72d0952188..838ad5fbc8e 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -354,9 +354,9 @@ MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 300, 0, JSEXN_SYNTAXERR, "paramet MSG_DEF(JSMSG_YIELD_IN_DEFAULT, 301, 0, JSEXN_SYNTAXERR, "yield in default expression") MSG_DEF(JSMSG_INTRINSIC_NOT_DEFINED, 302, 1, JSEXN_REFERENCEERR, "no intrinsic function {0}") MSG_DEF(JSMSG_ALREADY_HAS_SOURCEMAP, 303, 1, JSEXN_ERR, "{0} is being assigned a source map, yet already has one") -MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG, 304, 1, JSEXN_TYPEERR, "invalid ParallelArray{0} argument") +MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG, 304, 1, JSEXN_RANGEERR, "invalid ParallelArray{0} argument") MSG_DEF(JSMSG_PAR_ARRAY_BAD_PARTITION, 305, 0, JSEXN_ERR, "argument must be divisible by outermost dimension") -MSG_DEF(JSMSG_PAR_ARRAY_REDUCE_EMPTY, 306, 0, JSEXN_ERR, "cannot reduce empty ParallelArray object") +MSG_DEF(JSMSG_PAR_ARRAY_REDUCE_EMPTY, 306, 0, JSEXN_ERR, "cannot reduce ParallelArray object whose outermost dimension is empty") MSG_DEF(JSMSG_PAR_ARRAY_ALREADY_FLAT, 307, 0, JSEXN_ERR, "cannot flatten 1-dimensional ParallelArray object") MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_CONFLICT, 308, 0, JSEXN_ERR, "no conflict resolution function provided") MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BOUNDS, 309, 0, JSEXN_ERR, "index in scatter vector out of bounds") @@ -391,3 +391,5 @@ MSG_DEF(JSMSG_DATE_NOT_FINITE, 337, 0, JSEXN_RANGEERR, "date value is not MSG_DEF(JSMSG_MODULE_STATEMENT, 338, 0, JSEXN_SYNTAXERR, "module declarations may only appear at the top level of a program or module body") MSG_DEF(JSMSG_CURLY_BEFORE_MODULE, 339, 0, JSEXN_SYNTAXERR, "missing { before module body") MSG_DEF(JSMSG_CURLY_AFTER_MODULE, 340, 0, JSEXN_SYNTAXERR, "missing } after module body") +MSG_DEF(JSMSG_WRONG_VALUE, 341, 2, JSEXN_ERR, "expected {0} but found {1}") +MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BAD_TARGET, 342, 1, JSEXN_ERR, "target for index {0} is not an integer") diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index c555029b764..582706aef93 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -2262,6 +2262,7 @@ JSBool intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp); JSBool intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp); JSBool intrinsic_UnsafeSetElement(JSContext *cx, unsigned argc, Value *vp); JSBool intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp); +JSBool intrinsic_NewParallelArray(JSContext *cx, unsigned argc, Value *vp); #ifdef DEBUG JSBool intrinsic_Dump(JSContext *cx, unsigned argc, Value *vp); diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index 9eff1c2e5f8..feb56bc2182 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -12,6 +12,7 @@ #include "jsinfer.h" #include "jsprf.h" +#include "builtin/ParallelArray.h" #include "gc/Root.h" #include "vm/GlobalObject.h" #ifdef JS_ION @@ -521,6 +522,9 @@ GetClassForProtoKey(JSProtoKey key) case JSProto_DataView: return &DataViewClass; + case JSProto_ParallelArray: + return &ParallelArrayObject::class_; + default: JS_NOT_REACHED("Bad proto key"); return NULL; diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 25e9c283b66..5d8e0b12a6c 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -30,7 +30,6 @@ #include "jsproxy.h" #include "jsscript.h" -#include "builtin/ParallelArray.h" #include "ds/Sort.h" #include "frontend/TokenStream.h" #include "gc/Marking.h" @@ -41,7 +40,6 @@ #include "jsobjinlines.h" #include "builtin/Iterator-inl.h" -#include "builtin/ParallelArray-inl.h" #include "vm/Stack-inl.h" #include "vm/String-inl.h" @@ -203,14 +201,6 @@ Snapshot(JSContext *cx, RawObject pobj_, unsigned flags, AutoIdVector *props) return false; if (!EnumerateNativeProperties(cx, pobj, flags, ht, props)) return false; - } else if (ParallelArrayObject::is(pobj)) { - if (!ParallelArrayObject::enumerate(cx, pobj, flags, props)) - return false; - /* - * ParallelArray objects enumerate the prototype on their own, so - * we are done here. - */ - break; } else { if (pobj->isProxy()) { AutoIdVector proxyProps(cx); diff --git a/js/src/parjs-benchmarks/README.txt b/js/src/parjs-benchmarks/README.txt new file mode 100644 index 00000000000..b0f50ceaf9e --- /dev/null +++ b/js/src/parjs-benchmarks/README.txt @@ -0,0 +1,18 @@ +# Parallel JS Benchmarks + +This is a preliminary benchmark suite for Parallel JS. Each test is +intended to test various aspects of the engine, as described below. +The set of tests is very preliminary and is expected to grow and +eventually include more real-world examples. + +To run the tests, do something like: + + parjs-benchmarks/run.sh build-opt/js parjs-benchmarks/*.js + +Where `build-opt/js` is the path to your shell. + +# The tests + +- Mandelbrot: A test of embarassingly parallel arithmetic. Exercises + the comprehension form of 2D parallel arrays. +- Allocator: A test of parallel allocation. diff --git a/js/src/parjs-benchmarks/allocator.js b/js/src/parjs-benchmarks/allocator.js new file mode 100644 index 00000000000..e8600de3486 --- /dev/null +++ b/js/src/parjs-benchmarks/allocator.js @@ -0,0 +1,30 @@ +var nc = 30, maxCol = nc*3, cr,cg,cb; + +load(libdir + "util.js"); + +function allocateSomeStuff() { + return [{a: 1, b: 2}, {c: 3, d: 4}]; +} + +function computeSequentially() { + result = []; + for (var r = 0; r < ROWS; r++) { + for (var c = 0; c < COLS; c++) { + result.push(allocateSomeStuff()); + } + } + return result; +} + +function computeParallel() { + return new ParallelArray([ROWS, COLS], function(r, c) { + return allocateSomeStuff(); + }).flatten(); +} + +var ROWS = 1024; +var COLS = 1024; +var DEPTH = 2; + +benchmark("ALLOCATOR", 2, 6, + computeSequentially, computeParallel); diff --git a/js/src/parjs-benchmarks/mandelbrot.js b/js/src/parjs-benchmarks/mandelbrot.js new file mode 100644 index 00000000000..47eb1be8846 --- /dev/null +++ b/js/src/parjs-benchmarks/mandelbrot.js @@ -0,0 +1,61 @@ +// Adapted from +// +// https://github.com/RiverTrail/RiverTrail/blob/master/examples/mandelbrot/mandelbrot.js +// +// which in turn is adapted from a WebCL implementation available at +// +// http://www.ibiblio.org/e-notes/webcl/mandelbrot.html + +var nc = 30, maxCol = nc*3, cr,cg,cb; + +load(libdir + "util.js"); + +// this is the actual mandelbrot computation, ported to JavaScript +// from the WebCL / OpenCL example at +// http://www.ibiblio.org/e-notes/webcl/mandelbrot.html +function computeSetByRow(x, y) { + var Cr = (x - 256) / scale + 0.407476; + var Ci = (y - 256) / scale + 0.234204; + var I = 0, R = 0, I2 = 0, R2 = 0; + var n = 0; + while ((R2+I2 < 2.0) && (n < 512)) { + I = (R+R)*I+Ci; + R = R2-I2+Cr; + R2 = R*R; + I2 = I*I; + n++; + } + return n; +} + +function computeSequentially() { + result = []; + for (var r = 0; r < rows; r++) { + for (var c = 0; c < cols; c++) { + result.push(computeSetByRow(c, r)); + } + } + return result; +} + +function computeParallel() { + return new ParallelArray([rows, cols], function(r, c) { + return computeSetByRow(c, r); + }).flatten(); +} + +function compare(arrs, pas) { + for (var r = 0; r < rows; r++) { + for (var c = 0; c < cols; c++) { + assertEq(seq[c + r * cols], par.get(r, c)); + } + } +} + +var scale = 10000*300; +var rows = 1024; +var cols = 1024; + +// Experimentally, warmup doesn't seem to be necessary: +benchmark("MANDELBROT", 1, DEFAULT_MEASURE, + computeSequentially, computeParallel); diff --git a/js/src/parjs-benchmarks/run.sh b/js/src/parjs-benchmarks/run.sh new file mode 100755 index 00000000000..d78924db228 --- /dev/null +++ b/js/src/parjs-benchmarks/run.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +MODE=compare +if [[ "$1" == "--seq" ]]; then + MODE=seq + shift +elif [[ "$1" == "--par" ]]; then + MODE=par + shift +fi + +if [[ -z "$1" ]] || [[ "$1" == "--help" ]]; then + echo "Usage: run.sh [--seq | --par] path-to-shell paths-to-test" + echo "" + echo "Runs the given benchmark(s) using the given shell and " + echo "prints the results. If -seq or -par is supplied, " + echo "only runs sequentially or in parallel. Otherwise, runs both" + echo "and compares the performance." +fi + +D="$(dirname $0)" +S="$1" +shift +for T in "$@"; do + echo "$S" -e "'"'var libdir="'$D'/"; var MODE="'$MODE'";'"'" "$T" + "$S" -e 'var libdir="'$D'/"; var MODE="'$MODE'";' "$T" +done diff --git a/js/src/parjs-benchmarks/util.js b/js/src/parjs-benchmarks/util.js new file mode 100644 index 00000000000..073dce69385 --- /dev/null +++ b/js/src/parjs-benchmarks/util.js @@ -0,0 +1,155 @@ +DEFAULT_WARMUP = 10 +DEFAULT_MEASURE = 3 +MODE = MODE || "compare" // MODE is often set on the command-line by run.sh + +function benchmark(label, w, m, seq, par) { + var SEQ = 1 + var PAR = 2 + var bits = 0 + if (MODE === "seq") { bits = SEQ; } + else if (MODE === "par") { bits = PAR; } + else { + if (MODE !== "compare") { + print("Invalid MODE, expected seq|par|compare: ", MODE); + } + bits = SEQ|PAR; + } + + if (mode(SEQ)) { + print("Warming up sequential runs"); + warmup(w, seq); + + print("Measuring sequential runs"); + var [seqTimes, seqResult] = measureN(m, seq); + } + + if (mode(PAR)) { + print("Measuring parallel runs"); + var [parTimes, parResult] = measureN(m, par); + } + + if (mode(SEQ|PAR)) { + // Check correctness + print("Checking correctness"); + assertStructuralEq(seqResult, parResult); + } + + if (mode(SEQ)) { + var seqAvg = average(seqTimes); + for (var i = 0; i < seqTimes.length; i++) + print(label + " SEQUENTIAL MEASUREMENT " + i + ": " + seqTimes[i]); + print(label + " SEQUENTIAL AVERAGE: " + seqAvg); + } + + if (mode(PAR)) { + var parAvg = average(parTimes); + for (var i = 0; i < parTimes.length; i++) + print(label + " PARALLEL MEASUREMENT " + i + ": " + parTimes[i]); + print(label + " PARALLEL AVERAGE : " + parAvg); + } + + if (mode(SEQ|PAR)) { + print(label + " SEQ/PAR RATIO : " + seqAvg/parAvg); + print(label + " PAR/SEQ RATIO : " + parAvg/seqAvg); + print(label + " SPEEDUP : " + + (((seqAvg - parAvg) / seqAvg * 100.0) | 0) + "%"); + } + + function mode(m) { + return (bits & m) === m; + } +} + +function measure1(f) { + var start = new Date(); + result = f(); + var end = new Date(); + return [end.getTime() - start.getTime(), result]; +} + +function warmup(iters, f) { + for (var i = 0; i < iters; i++) { + print("."); + f(); + } +} + +function average(measurements) { + var sum = measurements.reduce(function (x, y) { return x + y; }); + return sum / measurements.length; +} + +function measureN(iters, f) { + var measurement, measurements = []; + var result; + + for (var i = 0; i < iters; i++) { + [measurement, result] = measure1(f); + measurements.push(measurement); + } + + return [measurements, result]; +} + +function assertStructuralEq(e1, e2) { + if (e1 instanceof ParallelArray && e2 instanceof ParallelArray) { + assertEqParallelArray(e1, e2); + } else if (e1 instanceof Array && e2 instanceof ParallelArray) { + assertEqParallelArrayArray(e2, e1); + } else if (e1 instanceof ParallelArray && e2 instanceof Array) { + assertEqParallelArrayArray(e1, e2); + } else if (e1 instanceof Array && e2 instanceof Array) { + assertEqArray(e1, e2); + } else if (e1 instanceof Object && e2 instanceof Object) { + assertEq(e1.__proto__, e2.__proto__); + for (prop in e1) { + if (e1.hasOwnProperty(prop)) { + assertEq(e2.hasOwnProperty(prop), true); + assertStructuralEq(e1[prop], e2[prop]); + } + } + } else { + assertEq(e1, e2); + } +} + +function assertEqParallelArrayArray(a, b) { + assertEq(a.shape.length, 1); + assertEq(a.length, b.length); + for (var i = 0, l = a.shape[0]; i < l; i++) { + assertStructuralEq(a.get(i), b[i]); + } +} + +function assertEqArray(a, b) { + assertEq(a.length, b.length); + for (var i = 0, l = a.length; i < l; i++) { + assertStructuralEq(a[i], b[i]); + } +} + +function assertEqParallelArray(a, b) { + assertEq(a instanceof ParallelArray, true); + assertEq(b instanceof ParallelArray, true); + + var shape = a.shape; + assertEqArray(shape, b.shape); + + function bump(indices) { + var d = indices.length - 1; + while (d >= 0) { + if (++indices[d] < shape[d]) + break; + indices[d] = 0; + d--; + } + return d >= 0; + } + + var iv = shape.map(function () { return 0; }); + do { + var e1 = a.get.apply(a, iv); + var e2 = b.get.apply(b, iv); + assertStructuralEq(e1, e2); + } while (bump(iv)); +} diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 4923c8803a7..4150ca69521 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -269,6 +269,27 @@ intrinsic_ParallelSlices(JSContext *cx, unsigned argc, Value *vp) return true; } +/* + * NewParallelArray(init, ...args): Creates a new parallel array using + * an initialization function |init|. All subsequent arguments are + * passed to |init|. The new instance will be passed as the |this| + * argument. + */ +JSBool +js::intrinsic_NewParallelArray(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + JS_ASSERT(args[0].isObject() && args[0].toObject().isFunction()); + + RootedFunction init(cx, args[0].toObject().toFunction()); + CallArgs args0 = CallArgsFromVp(argc - 1, vp + 1); + if (!js::ParallelArrayObject::constructHelper(cx, &init, args0)) + return false; + args.rval().set(args0.rval()); + return true; +} + /* * NewDenseArray(length): Allocates and returns a new dense array with * the given length where all values are initialized to holes. @@ -443,6 +464,7 @@ JSFunctionSpec intrinsic_functions[] = { JS_FN("ParallelDo", intrinsic_ParallelDo, 2,0), JS_FN("ParallelSlices", intrinsic_ParallelSlices, 0,0), + JS_FN("NewParallelArray", intrinsic_NewParallelArray, 3,0), JS_FN("NewDenseArray", intrinsic_NewDenseArray, 1,0), JS_FN("UnsafeSetElement", intrinsic_UnsafeSetElement, 3,0), JS_FN("ShouldForceSequential", intrinsic_ShouldForceSequential, 0,0), From 153987170e17e42b519ca09c34e707d6e197faa8 Mon Sep 17 00:00:00 2001 From: Mark Capella Date: Fri, 1 Mar 2013 22:19:14 -0500 Subject: [PATCH 059/140] Bug 811905 - Make bookmarks and history buttons in awesomescreen look more clickable, r=wesj --- mobile/android/base/AwesomeBarTabs.java | 11 ++++---- mobile/android/base/Makefile.in | 12 ++++++++ .../drawable-hdpi/awesomebar_sep_left.9.png | Bin 0 -> 216 bytes .../drawable-hdpi/awesomebar_sep_right.9.png | Bin 0 -> 206 bytes .../drawable-mdpi/awesomebar_sep_left.9.png | Bin 0 -> 208 bytes .../drawable-mdpi/awesomebar_sep_right.9.png | Bin 0 -> 202 bytes .../drawable-xhdpi/awesomebar_sep_left.9.png | Bin 0 -> 220 bytes .../drawable-xhdpi/awesomebar_sep_right.9.png | Bin 0 -> 207 bytes .../awesomebar_sep_left.9.png | Bin 0 -> 221 bytes .../awesomebar_sep_right.9.png | Bin 0 -> 208 bytes .../awesomebar_sep_left.9.png | Bin 0 -> 213 bytes .../awesomebar_sep_right.9.png | Bin 0 -> 205 bytes .../awesomebar_sep_left.9.png | Bin 0 -> 225 bytes .../awesomebar_sep_right.9.png | Bin 0 -> 212 bytes .../drawable/awesomebar_tab_unselected.xml | 26 ++++++++++++++++++ 15 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 mobile/android/base/resources/drawable-hdpi/awesomebar_sep_left.9.png create mode 100644 mobile/android/base/resources/drawable-hdpi/awesomebar_sep_right.9.png create mode 100644 mobile/android/base/resources/drawable-mdpi/awesomebar_sep_left.9.png create mode 100644 mobile/android/base/resources/drawable-mdpi/awesomebar_sep_right.9.png create mode 100644 mobile/android/base/resources/drawable-xhdpi/awesomebar_sep_left.9.png create mode 100644 mobile/android/base/resources/drawable-xhdpi/awesomebar_sep_right.9.png create mode 100644 mobile/android/base/resources/drawable-xlarge-hdpi-v11/awesomebar_sep_left.9.png create mode 100644 mobile/android/base/resources/drawable-xlarge-hdpi-v11/awesomebar_sep_right.9.png create mode 100644 mobile/android/base/resources/drawable-xlarge-mdpi-v11/awesomebar_sep_left.9.png create mode 100644 mobile/android/base/resources/drawable-xlarge-mdpi-v11/awesomebar_sep_right.9.png create mode 100644 mobile/android/base/resources/drawable-xlarge-xhdpi-v11/awesomebar_sep_left.9.png create mode 100644 mobile/android/base/resources/drawable-xlarge-xhdpi-v11/awesomebar_sep_right.9.png diff --git a/mobile/android/base/AwesomeBarTabs.java b/mobile/android/base/AwesomeBarTabs.java index 69a25bfae59..2248b189484 100644 --- a/mobile/android/base/AwesomeBarTabs.java +++ b/mobile/android/base/AwesomeBarTabs.java @@ -234,15 +234,14 @@ public class AwesomeBarTabs extends TabHost view.resetTheme(); } - if (i == selIndex) - continue; - - if (i == (selIndex - 1)) + if (i < (selIndex - 1)) + view.getBackground().setLevel(3); + else if (i == (selIndex - 1)) view.getBackground().setLevel(1); else if (i == (selIndex + 1)) view.getBackground().setLevel(2); - else - view.getBackground().setLevel(0); + else if (i > (selIndex + 1)) + view.getBackground().setLevel(4); } if (selIndex == 0) diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 045a24d69ed..2ad362ba772 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -546,6 +546,8 @@ RES_DRAWABLE_MDPI = \ res/drawable-mdpi/awesomebar_tab_center.9.png \ res/drawable-mdpi/awesomebar_tab_left.9.png \ res/drawable-mdpi/awesomebar_tab_right.9.png \ + res/drawable-mdpi/awesomebar_sep_left.9.png \ + res/drawable-mdpi/awesomebar_sep_right.9.png \ res/drawable-mdpi/desktop_notification.png \ res/drawable-mdpi/ic_addons_empty.png \ res/drawable-mdpi/ic_awesomebar_go.png \ @@ -659,6 +661,8 @@ RES_DRAWABLE_HDPI = \ res/drawable-hdpi/awesomebar_tab_center.9.png \ res/drawable-hdpi/awesomebar_tab_left.9.png \ res/drawable-hdpi/awesomebar_tab_right.9.png \ + res/drawable-hdpi/awesomebar_sep_left.9.png \ + res/drawable-hdpi/awesomebar_sep_right.9.png \ res/drawable-hdpi/ic_addons_empty.png \ res/drawable-hdpi/ic_awesomebar_go.png \ res/drawable-hdpi/ic_awesomebar_reader.png \ @@ -751,6 +755,8 @@ RES_DRAWABLE_XHDPI = \ res/drawable-xhdpi/awesomebar_tab_center.9.png \ res/drawable-xhdpi/awesomebar_tab_left.9.png \ res/drawable-xhdpi/awesomebar_tab_right.9.png \ + res/drawable-xhdpi/awesomebar_sep_left.9.png \ + res/drawable-xhdpi/awesomebar_sep_right.9.png \ res/drawable-xhdpi/ic_addons_empty.png \ res/drawable-xhdpi/ic_awesomebar_go.png \ res/drawable-xhdpi/ic_awesomebar_reader.png \ @@ -935,6 +941,8 @@ RES_DRAWABLE_XLARGE_MDPI_V11 = \ res/drawable-xlarge-mdpi-v11/awesomebar_tab_center.9.png \ res/drawable-xlarge-mdpi-v11/awesomebar_tab_left.9.png \ res/drawable-xlarge-mdpi-v11/awesomebar_tab_right.9.png \ + res/drawable-xlarge-mdpi-v11/awesomebar_sep_left.9.png \ + res/drawable-xlarge-mdpi-v11/awesomebar_sep_right.9.png \ res/drawable-xlarge-mdpi-v11/ic_menu_bookmark_add.png \ res/drawable-xlarge-mdpi-v11/ic_menu_bookmark_remove.png \ $(NULL) @@ -943,6 +951,8 @@ RES_DRAWABLE_XLARGE_HDPI_V11 = \ res/drawable-xlarge-hdpi-v11/awesomebar_tab_center.9.png \ res/drawable-xlarge-hdpi-v11/awesomebar_tab_left.9.png \ res/drawable-xlarge-hdpi-v11/awesomebar_tab_right.9.png \ + res/drawable-xlarge-hdpi-v11/awesomebar_sep_left.9.png \ + res/drawable-xlarge-hdpi-v11/awesomebar_sep_right.9.png \ res/drawable-xlarge-hdpi-v11/ic_menu_bookmark_add.png \ res/drawable-xlarge-hdpi-v11/ic_menu_bookmark_remove.png \ $(NULL) @@ -951,6 +961,8 @@ RES_DRAWABLE_XLARGE_XHDPI_V11 = \ res/drawable-xlarge-xhdpi-v11/awesomebar_tab_center.9.png \ res/drawable-xlarge-xhdpi-v11/awesomebar_tab_left.9.png \ res/drawable-xlarge-xhdpi-v11/awesomebar_tab_right.9.png \ + res/drawable-xlarge-xhdpi-v11/awesomebar_sep_left.9.png \ + res/drawable-xlarge-xhdpi-v11/awesomebar_sep_right.9.png \ res/drawable-xlarge-xhdpi-v11/ic_menu_bookmark_add.png \ res/drawable-xlarge-xhdpi-v11/ic_menu_bookmark_remove.png \ $(NULL) diff --git a/mobile/android/base/resources/drawable-hdpi/awesomebar_sep_left.9.png b/mobile/android/base/resources/drawable-hdpi/awesomebar_sep_left.9.png new file mode 100644 index 0000000000000000000000000000000000000000..9efdd76be3eee2d49c62b9d6a8559412afa0bb83 GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^EI@3^!3HGLu6vb%1kxRS9T^xl_H+M9WCij$3p^r= z85sBuf-vKbiP>*~g6t)pzOL+d8F^TY%-z|teglQ1N?apKg7ec#$`gxH83GbB^Gfvm zTtgJfjP*?Q3=Isv6fFd*a`bd@46*P}PH<@WZ=g1ZQG{VLC%3_$|NYD6sxg(8mKtO$ zd|7cIqoP5WsqcxymdKI;Vst E0O{gCIRF3v literal 0 HcmV?d00001 diff --git a/mobile/android/base/resources/drawable-hdpi/awesomebar_sep_right.9.png b/mobile/android/base/resources/drawable-hdpi/awesomebar_sep_right.9.png new file mode 100644 index 0000000000000000000000000000000000000000..ce36b7407eda5dc0c954d1526c1f70c25c58c4e3 GIT binary patch literal 206 zcmeAS@N?(olHy`uVBq!ia0vp^EI@3^!3HGLu6vb%1kxRS9T^xl_H+M9WCij$3p^r= z85sBufG}g$wN6f;AbW|YuPggqMjjR;b9c6^-#{U$64!{5;QX|b^2DN4hJeJ(yb?V> z*ARs=V?9$nLj!{^MGJwd%spKkLoEE08=C$HNbyXtOfZ|s^#8x|kxcb~@NjlP~7J3O*yW>|l9vqeFP=1ZUv44$rjF6*2UngFGVI5_|S literal 0 HcmV?d00001 diff --git a/mobile/android/base/resources/drawable-mdpi/awesomebar_sep_left.9.png b/mobile/android/base/resources/drawable-mdpi/awesomebar_sep_left.9.png new file mode 100644 index 0000000000000000000000000000000000000000..707c0ed921bc3827bfef2265276cb3df3ff49321 GIT binary patch literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^EI_Qn!3HGP#m@Es38XvvIx;Y9?C1WI$O`0h7I;J! zGcfQS1YyP<6SLm}1=&kHeO=k_GV-wK=^d4tp9U0?Dshb{3C>R|DNig)We7;j%q!9J za}7}_GuAWJGc+*xQnV1L%F@%tF+?LcIl-afzk%8uMiGY1hAa~Q{<{bJwmHPc%AOJ6 wu+?&C|Dd3}$Y4u?w@Jql<{-YSOyBq!UOuZ*W~;fF12lxe)78&qol`;+0F=r)U;qFB literal 0 HcmV?d00001 diff --git a/mobile/android/base/resources/drawable-mdpi/awesomebar_sep_right.9.png b/mobile/android/base/resources/drawable-mdpi/awesomebar_sep_right.9.png new file mode 100644 index 0000000000000000000000000000000000000000..716ad8e91c5e680cde9d76119a925c7efc63251e GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^EI_Qn!3HGP#m@Es38XvvIx;Y9?C1WI$O`0h7I;J! zGcfQS0Aa?gYn_}xLG}_)Usv|Kj65uQdPk+^rvZheN?apKg7ec#$`gxH83GbB^Gfvm zTtgJfjP*?Q3=Isv6fFd*GWK+F4ABTqZfN=+AjLDmGQq5@@&A9pM^Ct}T)leG)s6Lq pszdJ#XEq5Y1%@jh@9rIQjqp literal 0 HcmV?d00001 diff --git a/mobile/android/base/resources/drawable-xhdpi/awesomebar_sep_left.9.png b/mobile/android/base/resources/drawable-xhdpi/awesomebar_sep_left.9.png new file mode 100644 index 0000000000000000000000000000000000000000..df1b602a22b9fc628151cf01787b1db48b0bd1e1 GIT binary patch literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^EI{nW!3HE>Cz?uv1kxRS9T^xl_H+M9WCij$3p^r= z85sBuf-vKbiP>*~g6t)pzOL+d8F^StMOG>F`T>QcN?apKg7ec#$`gxH83GbB^Gfvm zTtgJfjP*?Q3=Isv6fFd*a`kj^46*P}PH<@WZ=g1ZQG{XhNr8kP|K-o_VH8@idUdlU zi&H;WPaC_1BG3LO1`d%thof>@xwai*mEu+StC6O_*`UnHpp*T{J@47JK%jvPp00i_ I>zopr0Cy5TrvLx| literal 0 HcmV?d00001 diff --git a/mobile/android/base/resources/drawable-xhdpi/awesomebar_sep_right.9.png b/mobile/android/base/resources/drawable-xhdpi/awesomebar_sep_right.9.png new file mode 100644 index 0000000000000000000000000000000000000000..0a9aa330ddb611d01cf5da1b16a2a68321b68e23 GIT binary patch literal 207 zcmeAS@N?(olHy`uVBq!ia0vp^EI{nW!3HE>Cz?uv1kxRS9T^xl_H+M9WCij$3p^r= z85sBufG}g$wN6f;AbW|YuPggqMjjSZkyQ%4en26q64!{5;QX|b^2DN4hJeJ(yb?V> z*ARs=V?9$nLj!{^MGJwdEIeHtLoEE08=C$HNbyXtOfcKY^#8x|5%!=3>(=q<8EF}4 v8O&kLnACVez=6$$tKp1;A-mFt`HT$l!JphE8ft$4&0z3!^>bP0l+XkK(*HQ} literal 0 HcmV?d00001 diff --git a/mobile/android/base/resources/drawable-xlarge-hdpi-v11/awesomebar_sep_left.9.png b/mobile/android/base/resources/drawable-xlarge-hdpi-v11/awesomebar_sep_left.9.png new file mode 100644 index 0000000000000000000000000000000000000000..697c696a7aa7e8a3d1372d36ceba0e30db1432af GIT binary patch literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^EI{nV!3HGfEdH7V5=eLSb!1@J*w6hZkrl}2Ebxdd zW?cwv8A!=JXfe1l>F|QHNfLaA!}t=l`zI`7+%GnPHz4}JMm7e;)*oK?-s{EyO=R$N L^>bP0l+XkKR%k=p literal 0 HcmV?d00001 diff --git a/mobile/android/base/resources/drawable-xlarge-hdpi-v11/awesomebar_sep_right.9.png b/mobile/android/base/resources/drawable-xlarge-hdpi-v11/awesomebar_sep_right.9.png new file mode 100644 index 0000000000000000000000000000000000000000..2469120423dbb5b8f1726c0b067bb1bd2bd0112c GIT binary patch literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^EI{nV!3HGfEdH7V5=eLSb!1@J*w6hZkrl}2Ebxdd zW?v2N(Y=6-^%JjZZmpESYt_$HmM_Bbn*};o~_5+&0;OXk;vd$@?2>=|rIu8H< literal 0 HcmV?d00001 diff --git a/mobile/android/base/resources/drawable-xlarge-xhdpi-v11/awesomebar_sep_left.9.png b/mobile/android/base/resources/drawable-xlarge-xhdpi-v11/awesomebar_sep_left.9.png new file mode 100644 index 0000000000000000000000000000000000000000..163e955eb851af5b44b2d2824fb9fbe8ebb331c4 GIT binary patch literal 225 zcmeAS@N?(olHy`uVBq!ia0vp^EI^#Z!3HGDV~Q+60_l#vjtmSN`?>!lvI6;>1s;*b z3=Dh+K$tP>S|=w^kiEpy*OmP)BM*x;V`QC+Hc&{a#5JNMI6tkVJh3R1As{g`uSCz! zHAJDzSkF|?(7@nJ(L$gqFHaZ85DWk0AOHW`GjlLA&TvRP!1B;2MA%`ISkhww@fE9A zH?s;I(O=B8_#xw2pT#E=E1t7ue6-X#Wf&pKpg;MffP$4x1J{3J28NsYsVla7Z*c*d O%HZkh=d#Wzp$Pz5u0cis literal 0 HcmV?d00001 diff --git a/mobile/android/base/resources/drawable-xlarge-xhdpi-v11/awesomebar_sep_right.9.png b/mobile/android/base/resources/drawable-xlarge-xhdpi-v11/awesomebar_sep_right.9.png new file mode 100644 index 0000000000000000000000000000000000000000..cb5d678fa1c880c284c667369bdfad28792a4992 GIT binary patch literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^EI^#Z!3HGDV~Q+60_l#vjtmSN`?>!lvI6;>1s;*b z3=DkxL735kHCP2G$X?><>&kwYk%z^aF|y7@8z>}I;u=vBoS#-wo>-L15RjOeSEA?V z8lq5UtY@lcXkhTAXdzIQt*47)h=qT0L(_i;1| + + + + + + + + + + + + + + + + + + + + + + + + From cfe687e13fd37f027e82f92a273fe8e9200c515b Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Fri, 1 Mar 2013 19:40:43 -0800 Subject: [PATCH 060/140] Bug 671976: Tweak timing in the hopes of forcing the assertion to stay before TEST-END on Linux32; add additional leakage of expected assertions for Windows. --- layout/base/tests/chrome/test_printpreview.xul | 10 ++++++---- .../base/tests/chrome/test_printpreview_bug396024.xul | 10 ++++++---- .../base/tests/chrome/test_printpreview_bug482976.xul | 10 ++++++---- layout/forms/test/test_bug536567_perwindowpb.html | 4 ++++ 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/layout/base/tests/chrome/test_printpreview.xul b/layout/base/tests/chrome/test_printpreview.xul index ba45f2d874f..64a2365ab04 100644 --- a/layout/base/tests/chrome/test_printpreview.xul +++ b/layout/base/tests/chrome/test_printpreview.xul @@ -19,15 +19,17 @@ if (navigator.platform.startsWith("Linux")) { function parentFinish() { // This is called while the helper window is still open. Call - // doFinish after it closes. - setTimeout(doFinish, 0); + // doGC after it closes. + setTimeout(doGC, 0); } -function doFinish() { +function doGC() { // Garbage collecting the windows created in this test can cause // assertions, so GC now to blame those assertions to this test. // ("Destroying a currently-showing document", bug 671976) SpecialPowers.gc(); - + setTimeout(doFinish, 0); +} +function doFinish() { SimpleTest.finish(); } SimpleTest.waitForExplicitFinish(); diff --git a/layout/base/tests/chrome/test_printpreview_bug396024.xul b/layout/base/tests/chrome/test_printpreview_bug396024.xul index a3fdf7e3830..651f494b126 100644 --- a/layout/base/tests/chrome/test_printpreview_bug396024.xul +++ b/layout/base/tests/chrome/test_printpreview_bug396024.xul @@ -25,15 +25,17 @@ if (navigator.platform.startsWith("Linux")) { function parentFinish() { // This is called while the helper window is still open. Call - // doFinish after it closes. - setTimeout(doFinish, 0); + // doGC after it closes. + setTimeout(doGC, 0); } -function doFinish() { +function doGC() { // Garbage collecting the windows created in this test can cause // assertions, so GC now to blame those assertions to this test. // ("Destroying a currently-showing document", bug 671976) SpecialPowers.gc(); - + setTimeout(doFinish, 0); +} +function doFinish() { SimpleTest.finish(); } SimpleTest.waitForExplicitFinish(); diff --git a/layout/base/tests/chrome/test_printpreview_bug482976.xul b/layout/base/tests/chrome/test_printpreview_bug482976.xul index af8da793e24..4e478ef6016 100644 --- a/layout/base/tests/chrome/test_printpreview_bug482976.xul +++ b/layout/base/tests/chrome/test_printpreview_bug482976.xul @@ -26,15 +26,17 @@ if (navigator.platform.startsWith("Linux")) { SimpleTest.waitForExplicitFinish(); function parentFinish() { // This is called while the helper window is still open. Call - // doFinish after it closes. - setTimeout(doFinish, 0); + // doGC after it closes. + setTimeout(doGC, 0); } -function doFinish() { +function doGC() { // Garbage collecting the windows created in this test can cause // assertions, so GC now to blame those assertions to this test. // ("Destroying a currently-showing document", bug 671976) SpecialPowers.gc(); - + setTimeout(doFinish, 0); +} +function doFinish() { SimpleTest.finish(); } window.open("printpreview_bug482976_helper.xul", "bug482976", "chrome,width=100,height=100"); diff --git a/layout/forms/test/test_bug536567_perwindowpb.html b/layout/forms/test/test_bug536567_perwindowpb.html index 28f9b17f9f3..61e2b04f908 100644 --- a/layout/forms/test/test_bug536567_perwindowpb.html +++ b/layout/forms/test/test_bug536567_perwindowpb.html @@ -15,6 +15,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=536567
 
+  
+  
+  
+
+
+Mozilla Bug 843725
+

+
+
+
+
+
+ + diff --git a/layout/style/forms.css b/layout/style/forms.css index 0b49e90fb53..a7ad31fe76f 100644 --- a/layout/style/forms.css +++ b/layout/style/forms.css @@ -720,12 +720,14 @@ meter { input[type="range"] { -moz-appearance: none; display: inline-block !important; - cursor: default; width: 12em; height: 1.3em; + margin: 0 0.7em; + /* Override some rules that apply on all input types: */ + cursor: default; background: none; border: none; - margin: 0 0.7em; + -moz-binding: none; /* we don't want any of platformHTMLBindings.xml#inputFields */ } /** diff --git a/toolkit/components/satchel/test/test_form_autocomplete.html b/toolkit/components/satchel/test/test_form_autocomplete.html index 96b3dfb030b..e18b860cf0a 100644 --- a/toolkit/components/satchel/test/test_form_autocomplete.html +++ b/toolkit/components/satchel/test/test_form_autocomplete.html @@ -775,7 +775,7 @@ function runTest(testNum) { checkMenuEntries([]); // type=range does not have a drop down menu doKey("down"); doKey("return"); - checkForm("32"); // default (midway between minimum (0) and maximum (64)) + checkForm("30"); // default (midway between minimum (0) and maximum (64)) - step // Go to test 500. fh.addEntry("field1", "value1"); From 54de207ca3608581f3dc252f0e976da89e438cb1 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Sat, 2 Mar 2013 07:51:42 -0500 Subject: [PATCH 084/140] Bug 822666 - Robustify zoom comparison warning against floating point rounding. r=Cwiiis --- mobile/android/chrome/content/browser.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index dccfa33a66e..5b8899b9d15 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -3081,8 +3081,8 @@ Tab.prototype = { this._drawZoom = resolution; cwu.setResolution(resolution, resolution); } - } else if (resolution != zoom) { - dump("Warning: setDisplayPort resolution did not match zoom for background tab!"); + } else if (Math.abs(resolution - zoom) >= 1e-6) { + dump("Warning: setDisplayPort resolution did not match zoom for background tab! (" + resolution + " != " + zoom + ")"); } // Finally, we set the display port, taking care to convert everything into the CSS-pixel From c021459c0c4291354f6ec86076a93887bb0c01cb Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Sat, 2 Mar 2013 07:51:54 -0500 Subject: [PATCH 085/140] Bug 787001 - Don't set the global compositor first-paint flag when a background tab receives the before-first-paint event. r=Cwiiis --- mobile/android/chrome/content/browser.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 5b8899b9d15..88f3433639a 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -3947,7 +3947,9 @@ Tab.prototype = { // Is it on the top level? let contentDocument = aSubject; if (contentDocument == this.browser.contentDocument) { - BrowserApp.displayedDocumentChanged(); + if (BrowserApp.selectedTab == this) { + BrowserApp.displayedDocumentChanged(); + } this.contentDocumentIsDisplayed = true; // reset CSS viewport and zoom to default on new page, and then calculate From 5793f05bf0aa160e3c8a7908dc5af3c4acc3daae Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Sat, 2 Mar 2013 07:52:04 -0500 Subject: [PATCH 086/140] Bug 846786 - Allow GfxInfoThread.hasData() to be called after GfxInfoThread.getData(). r=Cwiiis --- mobile/android/base/gfx/GLController.java | 4 +--- mobile/android/base/gfx/GfxInfoThread.java | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/mobile/android/base/gfx/GLController.java b/mobile/android/base/gfx/GLController.java index c4fbd05f5fd..e3ba46f72e9 100644 --- a/mobile/android/base/gfx/GLController.java +++ b/mobile/android/base/gfx/GLController.java @@ -123,9 +123,7 @@ public class GLController { // If we haven't yet created the compositor, and the GfxInfoThread // isn't done it's data gathering activities, then postpone creating // the compositor a little bit more. Don't block though, since this is - // the UI thread we're running on. This conditional also ensures that - // we don't call GfxInfoThread.hasData() once we have created the - // compositor, as that is not allowed (see GfxInfoThread). + // the UI thread we're running on. if (!mCompositorCreated && !GfxInfoThread.hasData()) { mView.postDelayed(this, 1); return; diff --git a/mobile/android/base/gfx/GfxInfoThread.java b/mobile/android/base/gfx/GfxInfoThread.java index b3abe067b63..8f5f080329a 100644 --- a/mobile/android/base/gfx/GfxInfoThread.java +++ b/mobile/android/base/gfx/GfxInfoThread.java @@ -33,10 +33,19 @@ public class GfxInfoThread extends Thread { } public static boolean hasData() { - // This should never be called before startThread() or after getData() - // so we know sInstance will be non-null here - synchronized (sInstance) { - return sInstance.mData != null; + // This should never be called before startThread(), so if + // sInstance is null here, then we know the thread was created, + // ran to completion, and getData() was called. Therefore hasData() + // should return true. If sInstance is not null, then we need to + // check if the mData field on it is null or not and return accordingly. + // Note that we keep a local copy of sInstance to avoid race conditions + // as getData() may be called concurrently. + GfxInfoThread instance = sInstance; + if (instance == null) { + return true; + } + synchronized (instance) { + return instance.mData != null; } } From a3485b59141b4534b6493c3d2c149c6d9b7a05d0 Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Sat, 2 Mar 2013 08:04:22 -0500 Subject: [PATCH 087/140] Bug 843536 - Ctrl-o doesn't open file picker. r=sfoster --- browser/metro/base/content/browser-ui.js | 26 +++++++++++++++++++ browser/metro/base/content/browser.xul | 2 ++ .../locales/en-US/chrome/browser.properties | 1 + 3 files changed, 29 insertions(+) diff --git a/browser/metro/base/content/browser-ui.js b/browser/metro/base/content/browser-ui.js index 23dbe6ffc32..8311620b2e2 100644 --- a/browser/metro/base/content/browser-ui.js +++ b/browser/metro/base/content/browser-ui.js @@ -758,6 +758,28 @@ var BrowserUI = { } }, + openFile: function() { + try { + const nsIFilePicker = Ci.nsIFilePicker; + let fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); + let self = this; + let fpCallback = function fpCallback_done(aResult) { + if (aResult == nsIFilePicker.returnOK) { + self.goToURI(fp.fileURL.spec); + } + }; + + let windowTitle = Strings.browser.GetStringFromName("browserForOpenLocation"); + fp.init(window, windowTitle, nsIFilePicker.modeOpen); + fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText | + nsIFilePicker.filterImages | nsIFilePicker.filterXML | + nsIFilePicker.filterHTML); + fp.open(fpCallback); + } catch (ex) { + dump ('BrowserUI openFile exception: ' + ex + '\n'); + } + }, + receiveMessage: function receiveMessage(aMessage) { let browser = aMessage.target; let json = aMessage.json; @@ -812,6 +834,7 @@ var BrowserUI = { case "cmd_zoomout": case "cmd_volumeLeft": case "cmd_volumeRight": + case "cmd_openFile": isSupported = true; break; default: @@ -935,6 +958,9 @@ var BrowserUI = { // Zoom out (portrait) or in (landscape) Browser.zoom(Util.isPortrait() ? 1 : -1); break; + case "cmd_openFile": + this.openFile(); + break; } } }; diff --git a/browser/metro/base/content/browser.xul b/browser/metro/base/content/browser.xul index e8db2dbbd92..574c2f77d7c 100644 --- a/browser/metro/base/content/browser.xul +++ b/browser/metro/base/content/browser.xul @@ -67,6 +67,7 @@ + @@ -133,6 +134,7 @@ + diff --git a/browser/metro/locales/en-US/chrome/browser.properties b/browser/metro/locales/en-US/chrome/browser.properties index e856861de74..672799e417c 100644 --- a/browser/metro/locales/en-US/chrome/browser.properties +++ b/browser/metro/locales/en-US/chrome/browser.properties @@ -21,6 +21,7 @@ helpOnlineCharm=Help (online) # General browserForSaveLocation=Save Location +browserForOpenLocation=Open Location # Download Manager downloadsUnknownSize=Unknown size From 6d81a1946dcee14af69c2dd0b9838dcabe587dd6 Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Sat, 2 Mar 2013 08:25:19 -0500 Subject: [PATCH 088/140] Bug 845011 - Ctrl-s doesn't save current document. r=sfoster --- browser/metro/base/content/browser-ui.js | 8 ++++++++ browser/metro/base/content/browser.js | 4 ++++ browser/metro/base/content/browser.xul | 2 ++ 3 files changed, 14 insertions(+) diff --git a/browser/metro/base/content/browser-ui.js b/browser/metro/base/content/browser-ui.js index 8311620b2e2..5e5442323ee 100644 --- a/browser/metro/base/content/browser-ui.js +++ b/browser/metro/base/content/browser-ui.js @@ -780,6 +780,10 @@ var BrowserUI = { } }, + savePage: function() { + Browser.savePage(); + }, + receiveMessage: function receiveMessage(aMessage) { let browser = aMessage.target; let json = aMessage.json; @@ -835,6 +839,7 @@ var BrowserUI = { case "cmd_volumeLeft": case "cmd_volumeRight": case "cmd_openFile": + case "cmd_savePage": isSupported = true; break; default: @@ -961,6 +966,9 @@ var BrowserUI = { case "cmd_openFile": this.openFile(); break; + case "cmd_savePage": + this.savePage(); + break; } } }; diff --git a/browser/metro/base/content/browser.js b/browser/metro/base/content/browser.js index 39fe360ed8e..4f307d695d4 100644 --- a/browser/metro/base/content/browser.js +++ b/browser/metro/base/content/browser.js @@ -476,6 +476,10 @@ var Browser = { tab.browser.messageManager.sendAsyncMessage("Browser:CanUnload", {}); }, + savePage: function() { + ContentAreaUtils.saveDocument(this.selectedBrowser.contentWindow.document); + }, + _doCloseTab: function _doCloseTab(aTab) { let nextTab = this._getNextTab(aTab); if (!nextTab) diff --git a/browser/metro/base/content/browser.xul b/browser/metro/base/content/browser.xul index 574c2f77d7c..94157a71649 100644 --- a/browser/metro/base/content/browser.xul +++ b/browser/metro/base/content/browser.xul @@ -68,6 +68,7 @@ + @@ -135,6 +136,7 @@ + From 2bb307775ebaeb984bedd4c67881d681e03c6d88 Mon Sep 17 00:00:00 2001 From: Robert Longson Date: Sat, 2 Mar 2013 13:28:04 +0000 Subject: [PATCH 089/140] Bug 843072 - Crash with getExtentOfChar(0) if there is no text. r=dholbert --- layout/svg/crashtests/843072-1.svg | 11 +++++++++++ layout/svg/crashtests/crashtests.list | 1 + layout/svg/nsSVGTextFrame2.cpp | 15 ++++++++++----- 3 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 layout/svg/crashtests/843072-1.svg diff --git a/layout/svg/crashtests/843072-1.svg b/layout/svg/crashtests/843072-1.svg new file mode 100644 index 00000000000..590721f058d --- /dev/null +++ b/layout/svg/crashtests/843072-1.svg @@ -0,0 +1,11 @@ + + + + + + + diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index d50de1fc732..571ea985d64 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -154,3 +154,4 @@ load 813420-1.svg load 841163-1.svg load 841812-1.svg load 842009-1.svg +load 843072-1.svg diff --git a/layout/svg/nsSVGTextFrame2.cpp b/layout/svg/nsSVGTextFrame2.cpp index 46333f0d469..d972e4add3b 100644 --- a/layout/svg/nsSVGTextFrame2.cpp +++ b/layout/svg/nsSVGTextFrame2.cpp @@ -3655,7 +3655,8 @@ nsSVGTextFrame2::GetSubStringLength(nsIContent* aContent, CharIterator chit(this, CharIterator::eAddressable, aContent); if (!chit.AdvanceToSubtree() || !chit.Next(charnum) || - chit.IsAfterSubtree()) { + chit.IsAfterSubtree() || + chit.AtEnd()) { return 0.0f; } charnum = chit.TextElementCharIndex(); @@ -3754,7 +3755,8 @@ nsSVGTextFrame2::GetStartPositionOfChar(nsIContent* aContent, CharIterator it(this, CharIterator::eAddressable, aContent); if (!it.AdvanceToSubtree() || - !it.Next(aCharNum)) { + !it.Next(aCharNum) || + it.AtEnd()) { return NS_ERROR_DOM_INDEX_SIZE_ERR; } @@ -3778,7 +3780,8 @@ nsSVGTextFrame2::GetEndPositionOfChar(nsIContent* aContent, CharIterator it(this, CharIterator::eAddressable, aContent); if (!it.AdvanceToSubtree() || - !it.Next(aCharNum)) { + !it.Next(aCharNum) || + it.AtEnd()) { return NS_ERROR_DOM_INDEX_SIZE_ERR; } @@ -3815,7 +3818,8 @@ nsSVGTextFrame2::GetExtentOfChar(nsIContent* aContent, CharIterator it(this, CharIterator::eAddressable, aContent); if (!it.AdvanceToSubtree() || - !it.Next(aCharNum)) { + !it.Next(aCharNum) || + it.AtEnd()) { return NS_ERROR_DOM_INDEX_SIZE_ERR; } @@ -3866,7 +3870,8 @@ nsSVGTextFrame2::GetRotationOfChar(nsIContent* aContent, CharIterator it(this, CharIterator::eAddressable, aContent); if (!it.AdvanceToSubtree() || - !it.Next(aCharNum)) { + !it.Next(aCharNum) || + it.AtEnd()) { return NS_ERROR_DOM_INDEX_SIZE_ERR; } From 704cbe505fd4047f7213482c5033aa051059d36a Mon Sep 17 00:00:00 2001 From: Neil Rashbrook Date: Sat, 2 Mar 2013 14:23:20 +0000 Subject: [PATCH 090/140] Bug 738952 Make it possible for a stream converter to propagate the actual MIME type to the document r=bz --HG-- rename : build/mach_bootstrap.py => mach extra : rebase_source : fdfacff52e126440966a4b989a240eb3517c29d5 --- content/base/src/nsDocument.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 5c48dc8d9ce..a33f65f2938 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -2260,7 +2260,10 @@ nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, } nsAutoCString contentType; - if (NS_SUCCEEDED(aChannel->GetContentType(contentType))) { + nsCOMPtr bag = do_QueryInterface(aChannel); + if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString( + NS_LITERAL_STRING("contentType"), contentType))) || + NS_SUCCEEDED(aChannel->GetContentType(contentType))) { // XXX this is only necessary for viewsource: nsACString::const_iterator start, end, semicolon; contentType.BeginReading(start); From fc26ef8819fcf508bc258118e25d75a74b488119 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Mon, 25 Feb 2013 00:16:02 +0000 Subject: [PATCH 091/140] Bug 658829 part 1 - preliminary cleanup r=bbondy --HG-- extra : rebase_source : d2c506e0a64a4686dbf7d18b2c89bafcd83ea015 --- widget/windows/WinUtils.cpp | 72 +++--- widget/windows/WinUtils.h | 23 +- widget/windows/nsNativeThemeWin.cpp | 330 +++++++++++++++------------- widget/windows/nsUXThemeConstants.h | 24 +- widget/xpwidgets/nsNativeTheme.cpp | 11 + widget/xpwidgets/nsNativeTheme.h | 6 +- 6 files changed, 257 insertions(+), 209 deletions(-) diff --git a/widget/windows/WinUtils.cpp b/widget/windows/WinUtils.cpp index 711ace6823b..472449ebc87 100644 --- a/widget/windows/WinUtils.cpp +++ b/widget/windows/WinUtils.cpp @@ -42,8 +42,12 @@ namespace widget { const char FaviconHelper::kJumpListCacheDir[] = "jumpListCache"; const char FaviconHelper::kShortcutCacheDir[] = "shortcutCache"; -// SHCreateItemFromParsingName is only available on vista and up. -WinUtils::SHCreateItemFromParsingNamePtr WinUtils::sCreateItemFromParsingName = nullptr; +// apis available on vista and up. +WinUtils::SHCreateItemFromParsingNamePtr WinUtils::sCreateItemFromParsingName = NULL; +WinUtils::SHGetKnownFolderPathPtr WinUtils::sGetKnownFolderPath = NULL; + +static const PRUnichar kSehllLibraryName[] = L"shell32.dll"; +static HMODULE sShellDll = NULL; /* static */ WinUtils::WinVersion @@ -363,38 +367,56 @@ WinUtils::InitMSG(UINT aMessage, WPARAM wParam, LPARAM lParam) return msg; } -/* static */ -bool -WinUtils::VistaCreateItemFromParsingNameInit() -{ - // Load and store Vista+ SHCreateItemFromParsingName - if (sCreateItemFromParsingName) { - return true; - } - static HMODULE sShellDll = nullptr; - if (sShellDll) { - return false; - } - static const PRUnichar kSehllLibraryName[] = L"shell32.dll"; - sShellDll = ::LoadLibraryW(kSehllLibraryName); - if (!sShellDll) { - return false; - } - sCreateItemFromParsingName = (SHCreateItemFromParsingNamePtr) - GetProcAddress(sShellDll, "SHCreateItemFromParsingName"); - return sCreateItemFromParsingName != nullptr; -} - /* static */ HRESULT WinUtils::SHCreateItemFromParsingName(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv) { - if (!VistaCreateItemFromParsingNameInit()) + if (sCreateItemFromParsingName) { + return sCreateItemFromParsingName(pszPath, pbc, riid, ppv); + } + + if (!sShellDll) { + sShellDll = ::LoadLibraryW(kSehllLibraryName); + if (!sShellDll) { + return false; + } + } + + sCreateItemFromParsingName = (SHCreateItemFromParsingNamePtr) + GetProcAddress(sShellDll, "SHCreateItemFromParsingName"); + if (!sCreateItemFromParsingName) return E_FAIL; + return sCreateItemFromParsingName(pszPath, pbc, riid, ppv); } +/* static */ +HRESULT +WinUtils::SHGetKnownFolderPath(REFKNOWNFOLDERID rfid, + DWORD dwFlags, + HANDLE hToken, + PWSTR *ppszPath) +{ + if (sGetKnownFolderPath) { + return sGetKnownFolderPath(rfid, dwFlags, hToken, ppszPath); + } + + if (!sShellDll) { + sShellDll = ::LoadLibraryW(kSehllLibraryName); + if (!sShellDll) { + return false; + } + } + + sGetKnownFolderPath = (SHGetKnownFolderPathPtr) + GetProcAddress(sShellDll, "SHGetKnownFolderPath"); + if (!sGetKnownFolderPath) + return E_FAIL; + + return sGetKnownFolderPath(rfid, dwFlags, hToken, ppszPath); +} + #ifdef MOZ_PLACES /************************************************************************/ /* Constructs as AsyncFaviconDataReady Object diff --git a/widget/windows/WinUtils.h b/widget/windows/WinUtils.h index ecc57d5c549..b5e52d3bd31 100644 --- a/widget/windows/WinUtils.h +++ b/widget/windows/WinUtils.h @@ -190,12 +190,19 @@ public: /** * SHCreateItemFromParsingName() calls native SHCreateItemFromParsingName() - * API. Note that you must call VistaCreateItemFromParsingNameInit() before - * calling this. And the result must be TRUE. Otherwise, returns E_FAIL. + * API which is available on Vista and up. */ static HRESULT SHCreateItemFromParsingName(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv); + /** + * SHGetKnownFolderPath() calls native SHGetKnownFolderPath() + * API which is available on Vista and up. + */ + static HRESULT SHGetKnownFolderPath(REFKNOWNFOLDERID rfid, + DWORD dwFlags, + HANDLE hToken, + PWSTR *ppszPath); /** * GetShellItemPath return the file or directory path of a shell item. * Internally calls IShellItem's GetDisplayName. @@ -229,13 +236,11 @@ private: REFIID riid, void **ppv); static SHCreateItemFromParsingNamePtr sCreateItemFromParsingName; - - /** - * VistaCreateItemFromParsingNameInit() initializes the static pointer for - * SHCreateItemFromParsingName() API which is usable only on Vista and later. - * This returns TRUE if the API is available. Otherwise, FALSE. - */ - static bool VistaCreateItemFromParsingNameInit(); + typedef HRESULT (WINAPI * SHGetKnownFolderPathPtr)(REFKNOWNFOLDERID rfid, + DWORD dwFlags, + HANDLE hToken, + PWSTR *ppszPath); + static SHGetKnownFolderPathPtr sGetKnownFolderPath; }; #ifdef MOZ_PLACES diff --git a/widget/windows/nsNativeThemeWin.cpp b/widget/windows/nsNativeThemeWin.cpp index a222e747c93..6f0fe5bf328 100644 --- a/widget/windows/nsNativeThemeWin.cpp +++ b/widget/windows/nsNativeThemeWin.cpp @@ -44,13 +44,20 @@ extern PRLogModuleInfo* gWindowsLog; NS_IMPL_ISUPPORTS_INHERITED1(nsNativeThemeWin, nsNativeTheme, nsITheme) -static inline bool IsHTMLContent(nsIFrame *frame) +nsNativeThemeWin::nsNativeThemeWin() { - nsIContent* content = frame->GetContent(); - return content && content->IsHTML(); + // If there is a relevant change in forms.css for windows platform, + // static widget style variables (e.g. sButtonBorderSize) should be + // reinitialized here. } -static int32_t GetTopLevelWindowActiveState(nsIFrame *aFrame) +nsNativeThemeWin::~nsNativeThemeWin() +{ + nsUXThemeData::Invalidate(); +} + +static int32_t +GetTopLevelWindowActiveState(nsIFrame *aFrame) { // Get the widget. nsIFrame's GetNearestWidget walks up the view chain // until it finds a real window. @@ -67,7 +74,8 @@ static int32_t GetTopLevelWindowActiveState(nsIFrame *aFrame) return mozilla::widget::themeconst::FS_INACTIVE; } -static int32_t GetWindowFrameButtonState(nsIFrame *aFrame, nsEventStates eventState) +static int32_t +GetWindowFrameButtonState(nsIFrame *aFrame, nsEventStates eventState) { if (GetTopLevelWindowActiveState(aFrame) == mozilla::widget::themeconst::FS_INACTIVE) { @@ -84,7 +92,8 @@ static int32_t GetWindowFrameButtonState(nsIFrame *aFrame, nsEventStates eventSt return mozilla::widget::themeconst::BS_NORMAL; } -static int32_t GetClassicWindowFrameButtonState(nsEventStates eventState) +static int32_t +GetClassicWindowFrameButtonState(nsEventStates eventState) { if (eventState.HasState(NS_EVENT_STATE_ACTIVE) && eventState.HasState(NS_EVENT_STATE_HOVER)) @@ -92,7 +101,8 @@ static int32_t GetClassicWindowFrameButtonState(nsEventStates eventState) return DFCS_BUTTONPUSH; } -static void QueryForButtonData(nsIFrame *aFrame) +static void +QueryForButtonData(nsIFrame *aFrame) { if (nsUXThemeData::sTitlebarInfoPopulatedThemed && nsUXThemeData::sTitlebarInfoPopulatedAero) return; @@ -107,17 +117,8 @@ static void QueryForButtonData(nsIFrame *aFrame) nsUXThemeData::UpdateTitlebarInfo(window->GetWindowHandle()); } -nsNativeThemeWin::nsNativeThemeWin() { - // If there is a relevant change in forms.css for windows platform, - // static widget style variables (e.g. sButtonBorderSize) should be - // reinitialized here. -} - -nsNativeThemeWin::~nsNativeThemeWin() { - nsUXThemeData::Invalidate(); -} - -static bool IsTopLevelMenu(nsIFrame *aFrame) +static bool +IsTopLevelMenu(nsIFrame *aFrame) { bool isTopLevel(false); nsMenuFrame *menuFrame = do_QueryFrame(aFrame); @@ -127,16 +128,21 @@ static bool IsTopLevelMenu(nsIFrame *aFrame) return isTopLevel; } -static MARGINS GetCheckboxMargins(HANDLE theme, HDC hdc) +static MARGINS +GetCheckboxMargins(HANDLE theme, HDC hdc) { MARGINS checkboxContent = {0}; - GetThemeMargins(theme, hdc, MENU_POPUPCHECK, MCB_NORMAL, TMT_CONTENTMARGINS, NULL, &checkboxContent); + GetThemeMargins(theme, hdc, MENU_POPUPCHECK, MCB_NORMAL, + TMT_CONTENTMARGINS, NULL, &checkboxContent); return checkboxContent; } -static SIZE GetCheckboxBGSize(HANDLE theme, HDC hdc) + +static SIZE +GetCheckboxBGSize(HANDLE theme, HDC hdc) { SIZE checkboxSize; - GetThemePartSize(theme, hdc, MENU_POPUPCHECK, MC_CHECKMARKNORMAL, NULL, TS_TRUE, &checkboxSize); + GetThemePartSize(theme, hdc, MENU_POPUPCHECK, MC_CHECKMARKNORMAL, + NULL, TS_TRUE, &checkboxSize); MARGINS checkboxMargins = GetCheckboxMargins(theme, hdc); @@ -152,17 +158,27 @@ static SIZE GetCheckboxBGSize(HANDLE theme, HDC hdc) ret.cy = height; return ret; } -static SIZE GetCheckboxBGBounds(HANDLE theme, HDC hdc) + +static SIZE +GetCheckboxBGBounds(HANDLE theme, HDC hdc) { MARGINS checkboxBGSizing = {0}; MARGINS checkboxBGContent = {0}; - GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL, TMT_SIZINGMARGINS, NULL, &checkboxBGSizing); - GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL, TMT_CONTENTMARGINS, NULL, &checkboxBGContent); + GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL, + TMT_SIZINGMARGINS, NULL, &checkboxBGSizing); + GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL, + TMT_CONTENTMARGINS, NULL, &checkboxBGContent); #define posdx(d) ((d) > 0 ? d : 0) - int dx = posdx(checkboxBGContent.cxRightWidth - checkboxBGSizing.cxRightWidth) + posdx(checkboxBGContent.cxLeftWidth - checkboxBGSizing.cxLeftWidth); - int dy = posdx(checkboxBGContent.cyTopHeight - checkboxBGSizing.cyTopHeight) + posdx(checkboxBGContent.cyBottomHeight - checkboxBGSizing.cyBottomHeight); + int dx = posdx(checkboxBGContent.cxRightWidth - + checkboxBGSizing.cxRightWidth) + + posdx(checkboxBGContent.cxLeftWidth - + checkboxBGSizing.cxLeftWidth); + int dy = posdx(checkboxBGContent.cyTopHeight - + checkboxBGSizing.cyTopHeight) + + posdx(checkboxBGContent.cyBottomHeight - + checkboxBGSizing.cyBottomHeight); #undef posdx @@ -171,7 +187,9 @@ static SIZE GetCheckboxBGBounds(HANDLE theme, HDC hdc) ret.cy += dy; return ret; } -static SIZE GetGutterSize(HANDLE theme, HDC hdc) + +static SIZE +GetGutterSize(HANDLE theme, HDC hdc) { SIZE gutterSize; GetThemePartSize(theme, hdc, MENU_POPUPGUTTER, 0, NULL, TS_TRUE, &gutterSize); @@ -189,103 +207,107 @@ static SIZE GetGutterSize(HANDLE theme, HDC hdc) return ret; } -static HRESULT DrawThemeBGRTLAware(HANDLE theme, HDC hdc, int part, int state, - const RECT *widgetRect, const RECT *clipRect, - bool isRTL) +/* DrawThemeBGRTLAware - render a theme part based on rtl state. + * Some widgets are not direction-neutral and need to be drawn reversed for + * RTL. Windows provides a way to do this with SetLayout, but this reverses + * the entire drawing area of a given device context, which means that its + * use will also affect the positioning of the widget. There are two ways + * to work around this: + * + * Option 1: Alter the position of the rect that we send so that we cancel + * out the positioning effects of SetLayout + * Option 2: Create a memory DC with the widgetRect's dimensions, draw onto + * that, and then transfer the results back to our DC + * + * This function tries to implement option 1, under the assumption that the + * correct way to reverse the effects of SetLayout is to translate the rect + * such that the offset from the DC bitmap's left edge to the old rect's + * left edge is equal to the offset from the DC bitmap's right edge to the + * new rect's right edge. In other words, + * (oldRect.left + vpOrg.x) == ((dcBMP.width - vpOrg.x) - newRect.right) + */ +static HRESULT +DrawThemeBGRTLAware(HANDLE aTheme, HDC aHdc, int aPart, int aState, + const RECT *aWidgetRect, const RECT *aClipRect, + bool aIsRtl) { - /* Some widgets are not direction-neutral and need to be drawn reversed for - * RTL. Windows provides a way to do this with SetLayout, but this reverses - * the entire drawing area of a given device context, which means that its - * use will also affect the positioning of the widget. There are two ways - * to work around this: - * - * Option 1: Alter the position of the rect that we send so that we cancel - * out the positioning effects of SetLayout - * Option 2: Create a memory DC with the widgetRect's dimensions, draw onto - * that, and then transfer the results back to our DC - * - * This function tries to implement option 1, under the assumption that the - * correct way to reverse the effects of SetLayout is to translate the rect - * such that the offset from the DC bitmap's left edge to the old rect's - * left edge is equal to the offset from the DC bitmap's right edge to the - * new rect's right edge. In other words, - * (oldRect.left + vpOrg.x) == ((dcBMP.width - vpOrg.x) - newRect.right) - * - * I am not 100% sure that this is the correct approach, but I have yet to - * find a problem with it. - */ + NS_ASSERTION(aTheme, "Bad theme handle."); + NS_ASSERTION(aHdc, "Bad hdc."); + NS_ASSERTION(aWidgetRect, "Bad rect."); + NS_ASSERTION(aClipRect, "Bad clip rect."); - if (isRTL) { - HGDIOBJ hObj = GetCurrentObject(hdc, OBJ_BITMAP); - BITMAP bitmap; - POINT vpOrg; - - if (hObj && - GetObject(hObj, sizeof(bitmap), &bitmap) && - GetViewportOrgEx(hdc, &vpOrg)) - { - RECT newWRect(*widgetRect); - newWRect.left = bitmap.bmWidth - (widgetRect->right + 2*vpOrg.x); - newWRect.right = bitmap.bmWidth - (widgetRect->left + 2*vpOrg.x); - - RECT newCRect; - RECT *newCRectPtr = NULL; - - if (clipRect) { - newCRect.top = clipRect->top; - newCRect.bottom = clipRect->bottom; - newCRect.left = bitmap.bmWidth - (clipRect->right + 2*vpOrg.x); - newCRect.right = bitmap.bmWidth - (clipRect->left + 2*vpOrg.x); - newCRectPtr = &newCRect; - } - - SetLayout(hdc, LAYOUT_RTL); - HRESULT hr = DrawThemeBackground(theme, hdc, part, state, &newWRect, newCRectPtr); - SetLayout(hdc, 0); - - if (hr == S_OK) - return hr; - } + if (!aIsRtl) { + return DrawThemeBackground(aTheme, aHdc, aPart, aState, + aWidgetRect, aClipRect); } - // Draw normally if LTR or if anything went wrong - return DrawThemeBackground(theme, hdc, part, state, widgetRect, clipRect); + HGDIOBJ hObj = GetCurrentObject(aHdc, OBJ_BITMAP); + BITMAP bitmap; + POINT vpOrg; + + if (hObj && GetObject(hObj, sizeof(bitmap), &bitmap) && + GetViewportOrgEx(aHdc, &vpOrg)) { + RECT newWRect(*aWidgetRect); + newWRect.left = bitmap.bmWidth - (aWidgetRect->right + 2*vpOrg.x); + newWRect.right = bitmap.bmWidth - (aWidgetRect->left + 2*vpOrg.x); + + RECT newCRect; + RECT *newCRectPtr = NULL; + + if (aClipRect) { + newCRect.top = aClipRect->top; + newCRect.bottom = aClipRect->bottom; + newCRect.left = bitmap.bmWidth - (aClipRect->right + 2*vpOrg.x); + newCRect.right = bitmap.bmWidth - (aClipRect->left + 2*vpOrg.x); + newCRectPtr = &newCRect; + } + + SetLayout(aHdc, LAYOUT_RTL); + HRESULT hr = DrawThemeBackground(aTheme, aHdc, aPart, aState, &newWRect, + newCRectPtr); + SetLayout(aHdc, 0); + if (SUCCEEDED(hr)) { + return hr; + } + } + return DrawThemeBackground(aTheme, aHdc, aPart, aState, + aWidgetRect, aClipRect); } /* - Caption button padding data - 'hot' button padding. - These areas are considered hot, in that they activate - a button when hovered or clicked. The button graphic - is drawn inside the padding border. Unrecognized themes - are treated as their recognized counterparts for now. - left top right bottom - classic min 1 2 0 1 - classic max 0 2 1 1 - classic close 1 2 2 1 - - aero basic min 1 2 0 2 - aero basic max 0 2 1 2 - aero basic close 1 2 1 2 - - xp theme min 0 2 0 2 - xp theme max 0 2 1 2 - xp theme close 1 2 2 2 - - 'cold' button padding - generic button padding, should - be handled in css. - left top right bottom - classic min 0 0 0 0 - classic max 0 0 0 0 - classic close 0 0 0 0 - - aero basic min 0 0 1 0 - aero basic max 1 0 0 0 - aero basic close 0 0 0 0 - - xp theme min 0 0 1 0 - xp theme max 1 0 0 0 - xp theme close 0 0 0 0 -*/ + * Caption button padding data - 'hot' button padding. + * These areas are considered hot, in that they activate + * a button when hovered or clicked. The button graphic + * is drawn inside the padding border. Unrecognized themes + * are treated as their recognized counterparts for now. + * left top right bottom + * classic min 1 2 0 1 + * classic max 0 2 1 1 + * classic close 1 2 2 1 + * + * aero basic min 1 2 0 2 + * aero basic max 0 2 1 2 + * aero basic close 1 2 1 2 + * + * xp theme min 0 2 0 2 + * xp theme max 0 2 1 2 + * xp theme close 1 2 2 2 + * + * 'cold' button padding - generic button padding, should + * be handled in css. + * left top right bottom + * classic min 0 0 0 0 + * classic max 0 0 0 0 + * classic close 0 0 0 0 + * + * aero basic min 0 0 1 0 + * aero basic max 1 0 0 0 + * aero basic close 0 0 0 0 + * + * xp theme min 0 0 1 0 + * xp theme max 1 0 0 0 + * xp theme close 0 0 0 0 + */ enum CaptionDesktopTheme { CAPTION_CLASSIC = 0, @@ -316,10 +338,43 @@ static CaptionButtonPadding buttonData[3] = { } }; -/** - * Progress bar related constants. - * These values are found by experimenting and comparing against native widgets - * used by the system. They are very unlikely exact but try to not be too wrong. +// Adds "hot" caption button padding to minimum widget size. +static void +AddPaddingRect(nsIntSize* aSize, CaptionButton button) { + if (!aSize) + return; + RECT offset; + if (!IsAppThemed()) + offset = buttonData[CAPTION_CLASSIC].hotPadding[button]; + else if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION) + offset = buttonData[CAPTION_XPTHEME].hotPadding[button]; + else + offset = buttonData[CAPTION_BASIC].hotPadding[button]; + aSize->width += offset.left + offset.right; + aSize->height += offset.top + offset.bottom; +} + +// If we've added padding to the minimum widget size, offset +// the area we draw into to compensate. +static void +OffsetBackgroundRect(RECT& rect, CaptionButton button) { + RECT offset; + if (!IsAppThemed()) + offset = buttonData[CAPTION_CLASSIC].hotPadding[button]; + else if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION) + offset = buttonData[CAPTION_XPTHEME].hotPadding[button]; + else + offset = buttonData[CAPTION_BASIC].hotPadding[button]; + rect.left += offset.left; + rect.top += offset.top; + rect.right -= offset.right; + rect.bottom -= offset.bottom; +} + +/* + * Progress bar related constants. These values are found by experimenting and + * comparing against native widgets used by the system. They are very unlikely + * exact but try to not be too wrong. */ // The width of the overlay used to animate the horizontal progress bar (Vista and later). static const int32_t kProgressHorizontalVistaOverlaySize = 120; @@ -342,37 +397,6 @@ static const int32_t kProgressIndeterminateDelay = 500; // Delay (in ms) between two determinate progress bar animation on Vista/7. static const int32_t kProgressDeterminedVistaDelay = 1000; -// Adds "hot" caption button padding to minimum widget size. -static void AddPaddingRect(nsIntSize* aSize, CaptionButton button) { - if (!aSize) - return; - RECT offset; - if (!IsAppThemed()) - offset = buttonData[CAPTION_CLASSIC].hotPadding[button]; - else if (WinUtils::GetWindowsVersion() == WinUtils::WINXP_VERSION) - offset = buttonData[CAPTION_XPTHEME].hotPadding[button]; - else - offset = buttonData[CAPTION_BASIC].hotPadding[button]; - aSize->width += offset.left + offset.right; - aSize->height += offset.top + offset.bottom; -} - -// If we've added padding to the minimum widget size, offset -// the area we draw into to compensate. -static void OffsetBackgroundRect(RECT& rect, CaptionButton button) { - RECT offset; - if (!IsAppThemed()) - offset = buttonData[CAPTION_CLASSIC].hotPadding[button]; - else if (WinUtils::GetWindowsVersion() == WinUtils::WINXP_VERSION) - offset = buttonData[CAPTION_XPTHEME].hotPadding[button]; - else - offset = buttonData[CAPTION_BASIC].hotPadding[button]; - rect.left += offset.left; - rect.top += offset.top; - rect.right -= offset.right; - rect.bottom -= offset.bottom; -} - HANDLE nsNativeThemeWin::GetTheme(uint8_t aWidgetType) { diff --git a/widget/windows/nsUXThemeConstants.h b/widget/windows/nsUXThemeConstants.h index f3336058518..535f100295d 100644 --- a/widget/windows/nsUXThemeConstants.h +++ b/widget/windows/nsUXThemeConstants.h @@ -10,6 +10,10 @@ * Windows' Theme API. For more information on theme parts and states see * http://msdn.microsoft.com/en-us/library/bb773210(VS.85).aspx */ + +#include +#include + #define THEME_COLOR 204 #define THEME_FONT 210 @@ -24,10 +28,6 @@ #define TKP_FOCUSED 4 #define TKP_DISABLED 5 -// Toolbar constants -#define TP_BUTTON 1 -#define TP_SEPARATOR 5 - // Toolbarbutton constants #define TB_CHECKED 5 #define TB_HOVER_CHECKED 6 @@ -78,16 +78,6 @@ #define SPNP_UP 1 #define SPNP_DOWN 2 -// Progress bar constants -#define PP_BAR 1 -#define PP_BARVERT 2 -#define PP_CHUNK 3 -#define PP_CHUNKVERT 4 -#define PP_FILL 5 -#define PP_FILLVERT 6 -#define PP_MOVEOVERLAY 8 -#define PP_MOVEOVERLAYVERT 9 - // Tab constants #define TABP_TAB 4 #define TABP_TAB_SELECTED 5 @@ -151,12 +141,6 @@ #define MSM_NORMAL 1 #define MSM_DISABLED 2 -// From tmschema.h in the Vista SDK -#define TMT_TEXTCOLOR 3803 -#define TMT_SIZINGMARGINS 3601 -#define TMT_CONTENTMARGINS 3602 -#define TMT_CAPTIONMARGINS 3603 - // Rebar constants #define RP_BAND 3 #define RP_BACKGROUND 6 diff --git a/widget/xpwidgets/nsNativeTheme.cpp b/widget/xpwidgets/nsNativeTheme.cpp index 14435b8de03..a1267476fff 100644 --- a/widget/xpwidgets/nsNativeTheme.cpp +++ b/widget/xpwidgets/nsNativeTheme.cpp @@ -303,6 +303,17 @@ nsNativeTheme::IsFrameRTL(nsIFrame* aFrame) return aFrame && aFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; } +bool +nsNativeTheme::IsHTMLContent(nsIFrame *aFrame) +{ + if (!aFrame) { + return false; + } + nsIContent* content = aFrame->GetContent(); + return content && content->IsHTML(); +} + + // scrollbar button: int32_t nsNativeTheme::GetScrollbarButtonType(nsIFrame* aFrame) diff --git a/widget/xpwidgets/nsNativeTheme.h b/widget/xpwidgets/nsNativeTheme.h index 65bb3087878..4029047f4fa 100644 --- a/widget/xpwidgets/nsNativeTheme.h +++ b/widget/xpwidgets/nsNativeTheme.h @@ -59,6 +59,8 @@ class nsNativeTheme : public nsITimerCallback // RTL chrome direction bool IsFrameRTL(nsIFrame* aFrame); + bool IsHTMLContent(nsIFrame *aFrame); + // button: bool IsDefaultButton(nsIFrame* aFrame) { return CheckBooleanAttr(aFrame, nsGkAtoms::_default); @@ -75,11 +77,11 @@ class nsNativeTheme : public nsITimerCallback bool IsSelected(nsIFrame* aFrame) { return GetCheckedOrSelected(aFrame, true); } - + bool IsFocused(nsIFrame* aFrame) { return CheckBooleanAttr(aFrame, nsGkAtoms::focused); } - + // scrollbar button: int32_t GetScrollbarButtonType(nsIFrame* aFrame); From 228cb58d15eb98c2324067c73ce77ff0d6879f95 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Mon, 25 Feb 2013 00:16:16 +0000 Subject: [PATCH 092/140] Bug 392672 Make progress meter corner pixels transparent r=bbondy --HG-- extra : rebase_source : 72aed4ae6b57d1f929d8825f2fe2cb10977f2eaa --- widget/windows/nsNativeThemeWin.cpp | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/widget/windows/nsNativeThemeWin.cpp b/widget/windows/nsNativeThemeWin.cpp index 6f0fe5bf328..a25e4cafff6 100644 --- a/widget/windows/nsNativeThemeWin.cpp +++ b/widget/windows/nsNativeThemeWin.cpp @@ -678,7 +678,12 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType, } case NS_THEME_PROGRESSBAR: { aPart = IsVerticalProgress(aFrame) ? PP_BARVERT : PP_BAR; - aState = TS_NORMAL; + aState = PBBS_NORMAL; + return NS_OK; + } + case NS_THEME_PROGRESSBAR_VERTICAL: { + aPart = PP_BARVERT; + aState = PBBS_NORMAL; return NS_OK; } case NS_THEME_PROGRESSBAR_CHUNK: { @@ -700,11 +705,6 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType, aState = TS_NORMAL; return NS_OK; } - case NS_THEME_PROGRESSBAR_VERTICAL: { - aPart = PP_BARVERT; - aState = TS_NORMAL; - return NS_OK; - } case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: { aPart = PP_CHUNKVERT; aState = TS_NORMAL; @@ -1462,6 +1462,19 @@ RENDER_AGAIN: DrawThemeBGRTLAware(theme, hdc, part, state, &widgetRect, &clipRect, IsFrameRTL(aFrame)); } + else if (aWidgetType == NS_THEME_PROGRESSBAR || + aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL) { + // DrawThemeBackground renders each corner with a solid white pixel. + // Restore these pixels to the underlying color. Tracks are rendered + // using alpha recovery, so this makes the corners transparent. + COLORREF color; + color = GetPixel(hdc, widgetRect.left, widgetRect.top); + DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect); + SetPixel(hdc, widgetRect.left, widgetRect.top, color); + SetPixel(hdc, widgetRect.right-1, widgetRect.top, color); + SetPixel(hdc, widgetRect.right-1, widgetRect.bottom-1, color); + SetPixel(hdc, widgetRect.left, widgetRect.bottom-1, color); + } // If part is negative, the element wishes us to not render a themed // background, instead opting to be drawn specially below. else if (part >= 0) { @@ -2329,6 +2342,10 @@ nsNativeThemeWin::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType) case NS_THEME_WIN_BORDERLESS_GLASS: case NS_THEME_SCALE_HORIZONTAL: case NS_THEME_SCALE_VERTICAL: + case NS_THEME_PROGRESSBAR: + case NS_THEME_PROGRESSBAR_VERTICAL: + case NS_THEME_PROGRESSBAR_CHUNK: + case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: return eTransparent; } From ab07dc454996c2279bd4d63153462ff8b1abaf41 Mon Sep 17 00:00:00 2001 From: Neil Rashbrook Date: Mon, 25 Feb 2013 00:16:35 +0000 Subject: [PATCH 093/140] Bug 658829 part 2 - stop using undetermined binding r=jimm --HG-- extra : rebase_source : e77917b9cdc758e1929cc65a9be2e53e3b039ec0 --- toolkit/themes/windows/global/global.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/toolkit/themes/windows/global/global.css b/toolkit/themes/windows/global/global.css index 141839f394b..b23f7a5c65d 100644 --- a/toolkit/themes/windows/global/global.css +++ b/toolkit/themes/windows/global/global.css @@ -26,10 +26,6 @@ menulist > menupopup, -moz-binding: url("chrome://global/skin/globalBindings.xml#menulist-compact"); } -progressmeter[mode="undetermined"] { - -moz-binding: url("chrome://global/content/bindings/progressmeter.xml#progressmeter-undetermined"); -} - /* ::::: root elements ::::: */ window, From 9980a170e68b583d4635c9682a8e4a1ae94f2f94 Mon Sep 17 00:00:00 2001 From: Neil Rashbrook Date: Mon, 25 Feb 2013 00:26:07 +0000 Subject: [PATCH 094/140] Bug 658829 part 3 - move GetProgress*Value to nsNativeTheme r=jimm --HG-- extra : rebase_source : 562107a9a653f352900073b096bce0b85aa423f4 --- widget/cocoa/nsNativeThemeCocoa.h | 4 --- widget/cocoa/nsNativeThemeCocoa.mm | 37 --------------------------- widget/xpwidgets/nsNativeTheme.cpp | 41 ++++++++++++++++++++++++++++++ widget/xpwidgets/nsNativeTheme.h | 8 ++++-- 4 files changed, 47 insertions(+), 43 deletions(-) diff --git a/widget/cocoa/nsNativeThemeCocoa.h b/widget/cocoa/nsNativeThemeCocoa.h index 1eb758f36ac..88b6e43b0f4 100644 --- a/widget/cocoa/nsNativeThemeCocoa.h +++ b/widget/cocoa/nsNativeThemeCocoa.h @@ -68,10 +68,6 @@ protected: CGRect SeparatorAdjustedRect(CGRect aRect, nsIFrame* aLeft, nsIFrame* aCurrent, nsIFrame* aRight); - // Helpers for progressbar. - double GetProgressValue(nsIFrame* aFrame); - double GetProgressMaxValue(nsIFrame* aFrame); - // HITheme drawing routines void DrawFrame(CGContextRef context, HIThemeFrameKind inKind, const HIRect& inBoxRect, bool inReadOnly, diff --git a/widget/cocoa/nsNativeThemeCocoa.mm b/widget/cocoa/nsNativeThemeCocoa.mm index c5c480dc16f..558adf794ea 100644 --- a/widget/cocoa/nsNativeThemeCocoa.mm +++ b/widget/cocoa/nsNativeThemeCocoa.mm @@ -23,7 +23,6 @@ #include "nsCocoaWindow.h" #include "nsNativeThemeColors.h" #include "nsIScrollableFrame.h" -#include "nsIDOMHTMLProgressElement.h" #include "nsIDOMHTMLMeterElement.h" #include "mozilla/dom/Element.h" @@ -2994,39 +2993,3 @@ nsNativeThemeCocoa::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType) return eUnknownTransparency; } } - -double -nsNativeThemeCocoa::GetProgressValue(nsIFrame* aFrame) -{ - // When we are using the HTML progress element, - // we can get the value from the IDL property. - if (aFrame) { - nsCOMPtr progress = - do_QueryInterface(aFrame->GetContent()); - if (progress) { - double value; - progress->GetValue(&value); - return value; - } - } - - return (double)CheckIntAttr(aFrame, nsGkAtoms::value, 0); -} - -double -nsNativeThemeCocoa::GetProgressMaxValue(nsIFrame* aFrame) -{ - // When we are using the HTML progress element, - // we can get the max from the IDL property. - if (aFrame) { - nsCOMPtr progress = - do_QueryInterface(aFrame->GetContent()); - if (progress) { - double max; - progress->GetMax(&max); - return max; - } - } - - return (double)std::max(CheckIntAttr(aFrame, nsGkAtoms::max, 100), 1); -} diff --git a/widget/xpwidgets/nsNativeTheme.cpp b/widget/xpwidgets/nsNativeTheme.cpp index a1267476fff..f13d1d42a05 100644 --- a/widget/xpwidgets/nsNativeTheme.cpp +++ b/widget/xpwidgets/nsNativeTheme.cpp @@ -14,6 +14,7 @@ #include "nsString.h" #include "nsINameSpaceManager.h" #include "nsIDOMHTMLInputElement.h" +#include "nsIDOMHTMLProgressElement.h" #include "nsIDOMXULMenuListElement.h" #include "nsThemeConstants.h" #include "nsIComponentManager.h" @@ -101,6 +102,7 @@ nsNativeTheme::GetContentState(nsIFrame* aFrame, uint8_t aWidgetType) return flags; } +/* static */ bool nsNativeTheme::CheckBooleanAttr(nsIFrame* aFrame, nsIAtom* aAtom) { @@ -121,6 +123,7 @@ nsNativeTheme::CheckBooleanAttr(nsIFrame* aFrame, nsIAtom* aAtom) NS_LITERAL_STRING("true"), eCaseMatters); } +/* static */ int32_t nsNativeTheme::CheckIntAttr(nsIFrame* aFrame, nsIAtom* aAtom, int32_t defaultValue) { @@ -137,6 +140,44 @@ nsNativeTheme::CheckIntAttr(nsIFrame* aFrame, nsIAtom* aAtom, int32_t defaultVal return value; } +/* static */ +double +nsNativeTheme::GetProgressValue(nsIFrame* aFrame) +{ + // When we are using the HTML progress element, + // we can get the value from the IDL property. + if (aFrame) { + nsCOMPtr progress = + do_QueryInterface(aFrame->GetContent()); + if (progress) { + double value; + progress->GetValue(&value); + return value; + } + } + + return (double)nsNativeTheme::CheckIntAttr(aFrame, nsGkAtoms::value, 0); +} + +/* static */ +double +nsNativeTheme::GetProgressMaxValue(nsIFrame* aFrame) +{ + // When we are using the HTML progress element, + // we can get the max from the IDL property. + if (aFrame) { + nsCOMPtr progress = + do_QueryInterface(aFrame->GetContent()); + if (progress) { + double max; + progress->GetMax(&max); + return max; + } + } + + return (double)std::max(nsNativeTheme::CheckIntAttr(aFrame, nsGkAtoms::max, 100), 1); +} + bool nsNativeTheme::GetCheckedOrSelected(nsIFrame* aFrame, bool aCheckSelected) { diff --git a/widget/xpwidgets/nsNativeTheme.h b/widget/xpwidgets/nsNativeTheme.h index 4029047f4fa..d4b64ef19b2 100644 --- a/widget/xpwidgets/nsNativeTheme.h +++ b/widget/xpwidgets/nsNativeTheme.h @@ -155,8 +155,12 @@ class nsNativeTheme : public nsITimerCallback bool IsMenuListEditable(nsIFrame *aFrame); nsIPresShell *GetPresShell(nsIFrame* aFrame); - int32_t CheckIntAttr(nsIFrame* aFrame, nsIAtom* aAtom, int32_t defaultValue); - bool CheckBooleanAttr(nsIFrame* aFrame, nsIAtom* aAtom); + static bool CheckBooleanAttr(nsIFrame* aFrame, nsIAtom* aAtom); + static int32_t CheckIntAttr(nsIFrame* aFrame, nsIAtom* aAtom, int32_t defaultValue); + + // Helpers for progressbar. + static double GetProgressValue(nsIFrame* aFrame); + static double GetProgressMaxValue(nsIFrame* aFrame); bool GetCheckedOrSelected(nsIFrame* aFrame, bool aCheckSelected); bool GetIndeterminate(nsIFrame* aFrame); From 5f72cfb6c1880020eb42ff04e14c9bbb80986bcb Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Mon, 25 Feb 2013 00:27:10 +0000 Subject: [PATCH 095/140] Bug 658829 part 4 - add helper methods r=Neil --HG-- extra : rebase_source : 3c31d3ef56ffd2e026c8d774c98aded2c40a4470 --- widget/windows/nsNativeThemeWin.cpp | 168 ++++++++++++++++++++++++++-- widget/windows/nsNativeThemeWin.h | 48 ++++---- 2 files changed, 183 insertions(+), 33 deletions(-) diff --git a/widget/windows/nsNativeThemeWin.cpp b/widget/windows/nsNativeThemeWin.cpp index a25e4cafff6..bc8a36da179 100644 --- a/widget/windows/nsNativeThemeWin.cpp +++ b/widget/windows/nsNativeThemeWin.cpp @@ -44,7 +44,9 @@ extern PRLogModuleInfo* gWindowsLog; NS_IMPL_ISUPPORTS_INHERITED1(nsNativeThemeWin, nsNativeTheme, nsITheme) -nsNativeThemeWin::nsNativeThemeWin() +nsNativeThemeWin::nsNativeThemeWin() : + mProgressDeterminateTimeStamp(TimeStamp::Now()), + mProgressIndeterminateTimeStamp(TimeStamp::Now()) { // If there is a relevant change in forms.css for windows platform, // static widget style variables (e.g. sButtonBorderSize) should be @@ -371,11 +373,41 @@ OffsetBackgroundRect(RECT& rect, CaptionButton button) { rect.bottom -= offset.bottom; } +/* + * Notes on progress track and meter part constants: + * xp and up: + * PP_BAR(_VERT) - base progress track + * PP_TRANSPARENTBAR(_VERT) - transparent progress track. this only works if + * the underlying surface supports alpha. otherwise + * theme lib's DrawThemeBackground falls back on + * opaque PP_BAR. we currently don't use this. + * PP_CHUNK(_VERT) - xp progress meter. this does not draw an xp style + * progress w/chunks, it draws fill using the chunk + * graphic. + * vista and up: + * PP_FILL(_VERT) - progress meter. these have four states/colors. + * PP_PULSEOVERLAY(_VERT) - white reflection - an overlay, not sure what this + * is used for. + * PP_MOVEOVERLAY(_VERT) - green pulse - the pulse effect overlay on + * determined progress bars. we also use this for + * indeterminate chunk. + * + * Notes on state constants: + * PBBS_NORMAL - green progress + * PBBVS_PARTIAL/PBFVS_ERROR - red error progress + * PBFS_PAUSED - yellow paused progress + * + * There is no common controls style indeterminate part on vista and up. + */ + /* * Progress bar related constants. These values are found by experimenting and * comparing against native widgets used by the system. They are very unlikely * exact but try to not be too wrong. */ +// The amount of time we animate progress meters parts across the frame. +static const double kProgressDeterminateTimeSpan = 3.0; +static const double kProgressIndeterminateTimeSpan = 5.0; // The width of the overlay used to animate the horizontal progress bar (Vista and later). static const int32_t kProgressHorizontalVistaOverlaySize = 120; // The width of the overlay used for the horizontal indeterminate progress bars on XP. @@ -386,16 +418,130 @@ static const int32_t kProgressVerticalOverlaySize = 45; static const int32_t kProgressVerticalIndeterminateOverlaySize = 60; // The width of the overlay used to animate the indeterminate progress bar (Windows Classic). static const int32_t kProgressClassicOverlaySize = 40; -// Speed (px per ms) of the animation for determined Vista and later progress bars. -static const double kProgressDeterminedVistaSpeed = 0.225; -// Speed (px per ms) of the animation for indeterminate progress bars. -static const double kProgressIndeterminateSpeed = 0.175; -// Speed (px per ms) of the animation for indeterminate progress bars (Windows Classic). -static const double kProgressClassicIndeterminateSpeed = 0.0875; -// Delay (in ms) between two indeterminate progress bar cycles. -static const int32_t kProgressIndeterminateDelay = 500; -// Delay (in ms) between two determinate progress bar animation on Vista/7. -static const int32_t kProgressDeterminedVistaDelay = 1000; + +/* + * GetProgressOverlayStyle - returns the proper overlay part for themed + * progress bars based on os and orientation. + */ +static int32_t +GetProgressOverlayStyle(bool aIsVertical) +{ + if (aIsVertical) { + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { + return PP_MOVEOVERLAYVERT; + } + return PP_CHUNKVERT; + } else { + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { + return PP_MOVEOVERLAY; + } + return PP_CHUNK; + } +} + +/* + * GetProgressOverlaySize - returns the minimum width or height for themed + * progress bar overlays. This includes the width of indeterminate chunks + * and vista pulse overlays. + */ +static int32_t +GetProgressOverlaySize(bool aIsVertical, bool aIsIndeterminate) +{ + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { + if (aIsVertical) { + return aIsIndeterminate ? kProgressVerticalIndeterminateOverlaySize + : kProgressVerticalOverlaySize; + } + return kProgressHorizontalVistaOverlaySize; + } + return kProgressHorizontalXPOverlaySize; +} + +/* + * IsProgressMeterFilled - Determines if a progress meter is at 100% fill based + * on a comparison of the current value and maximum. + */ +static bool +IsProgressMeterFilled(nsIFrame* aFrame) +{ + NS_ENSURE_TRUE(aFrame, false); + nsIFrame* parentFrame = aFrame->GetParent(); + NS_ENSURE_TRUE(parentFrame, false); + return nsNativeTheme::GetProgressValue(parentFrame) == + nsNativeTheme::GetProgressMaxValue(parentFrame); +} + +/* + * CalculateProgressOverlayRect - returns the padded overlay animation rect + * used in rendering progress bars. Resulting rects are used in rendering + * vista+ pulse overlays and indeterminate progress meters. Graphics should + * be rendered at the origin. + */ +RECT +nsNativeThemeWin::CalculateProgressOverlayRect(nsIFrame* aFrame, + RECT* aWidgetRect, + bool aIsVertical, + bool aIsIndeterminate, + bool aIsClassic) +{ + NS_ASSERTION(aFrame, "bad frame pointer"); + NS_ASSERTION(aWidgetRect, "bad rect pointer"); + + int32_t frameSize = aIsVertical ? aWidgetRect->bottom - aWidgetRect->top + : aWidgetRect->right - aWidgetRect->left; + + // Recycle a set of progress pulse timers - these timers control the position + // of all progress overlays and indeterminate chunks that get rendered. + double span = aIsIndeterminate ? kProgressIndeterminateTimeSpan + : kProgressDeterminateTimeSpan; + TimeDuration period; + if (!aIsIndeterminate) { + if (TimeStamp::Now() > (mProgressDeterminateTimeStamp + + TimeDuration::FromSeconds(span))) { + mProgressDeterminateTimeStamp = TimeStamp::Now(); + } + period = TimeStamp::Now() - mProgressDeterminateTimeStamp; + } else { + if (TimeStamp::Now() > (mProgressIndeterminateTimeStamp + + TimeDuration::FromSeconds(span))) { + mProgressIndeterminateTimeStamp = TimeStamp::Now(); + } + period = TimeStamp::Now() - mProgressIndeterminateTimeStamp; + } + + double percent = period / TimeDuration::FromSeconds(span); + + if (!aIsVertical && IsFrameRTL(aFrame)) + percent = 1 - percent; + + RECT overlayRect = *aWidgetRect; + int32_t overlaySize; + if (!aIsClassic) { + overlaySize = GetProgressOverlaySize(aIsVertical, aIsIndeterminate); + } else { + overlaySize = kProgressClassicOverlaySize; + } + + // Calculate a bounds that is larger than the meters frame such that the + // overlay starts and ends completely off the edge of the frame: + // [overlay][frame][overlay] + // This also yields a nice delay on rotation. Use overlaySize as the minimum + // size for [overlay] based on the graphics dims. If [frame] is larger, use + // the frame size instead. + int trackWidth = frameSize > overlaySize ? frameSize : overlaySize; + if (!aIsVertical) { + int xPos = aWidgetRect->left - trackWidth; + xPos += (int)ceil(((double)(trackWidth*2) * percent)); + overlayRect.left = xPos; + overlayRect.right = xPos + overlaySize; + } else { + int yPos = aWidgetRect->bottom + trackWidth; + yPos -= (int)ceil(((double)(trackWidth*2) * percent)); + overlayRect.bottom = yPos; + overlayRect.top = yPos - overlaySize; + } + return overlayRect; +} HANDLE nsNativeThemeWin::GetTheme(uint8_t aWidgetType) diff --git a/widget/windows/nsNativeThemeWin.h b/widget/windows/nsNativeThemeWin.h index 28edc72c9eb..bdb5be8b797 100644 --- a/widget/windows/nsNativeThemeWin.h +++ b/widget/windows/nsNativeThemeWin.h @@ -9,6 +9,7 @@ #include "nsIAtom.h" #include "nsNativeTheme.h" #include +#include "mozilla/TimeStamp.h" struct nsIntRect; struct nsIntSize; @@ -16,6 +17,9 @@ struct nsIntSize; class nsNativeThemeWin : private nsNativeTheme, public nsITheme { public: + typedef mozilla::TimeStamp TimeStamp; + typedef mozilla::TimeDuration TimeDuration; + NS_DECL_ISUPPORTS_INHERITED // The nsITheme interface. @@ -70,37 +74,37 @@ protected: nsresult GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType, int32_t& aPart, int32_t& aState); nsresult ClassicGetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType, - int32_t& aPart, int32_t& aState, bool& aFocused); + int32_t& aPart, int32_t& aState, bool& aFocused); nsresult ClassicDrawWidgetBackground(nsRenderingContext* aContext, + nsIFrame* aFrame, + uint8_t aWidgetType, + const nsRect& aRect, + const nsRect& aClipRect); + nsresult ClassicGetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame, uint8_t aWidgetType, - const nsRect& aRect, - const nsRect& aClipRect); - nsresult ClassicGetWidgetBorder(nsDeviceContext* aContext, - nsIFrame* aFrame, - uint8_t aWidgetType, - nsIntMargin* aResult); - + nsIntMargin* aResult); bool ClassicGetWidgetPadding(nsDeviceContext* aContext, - nsIFrame* aFrame, - uint8_t aWidgetType, - nsIntMargin* aResult); - + nsIFrame* aFrame, + uint8_t aWidgetType, + nsIntMargin* aResult); nsresult ClassicGetMinimumWidgetSize(nsRenderingContext* aContext, nsIFrame* aFrame, - uint8_t aWidgetType, - nsIntSize* aResult, - bool* aIsOverridable); - + uint8_t aWidgetType, + nsIntSize* aResult, + bool* aIsOverridable); bool ClassicThemeSupportsWidget(nsPresContext* aPresContext, - nsIFrame* aFrame, - uint8_t aWidgetType); - + nsIFrame* aFrame, + uint8_t aWidgetType); void DrawCheckedRect(HDC hdc, const RECT& rc, int32_t fore, int32_t back, HBRUSH defaultBack); - uint32_t GetWidgetNativeDrawingFlags(uint8_t aWidgetType); - int32_t StandardGetState(nsIFrame* aFrame, uint8_t aWidgetType, bool wantFocused); - bool IsMenuActive(nsIFrame* aFrame, uint8_t aWidgetType); + RECT CalculateProgressOverlayRect(nsIFrame* aFrame, RECT* aWidgetRect, + bool aIsVertical, bool aIsIndeterminate, + bool aIsClassic); + +private: + TimeStamp mProgressDeterminateTimeStamp; + TimeStamp mProgressIndeterminateTimeStamp; }; From d37f2d953af9c480265a1dd34114670565aa758c Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Mon, 25 Feb 2013 00:27:22 +0000 Subject: [PATCH 096/140] Bug 658829 part 5 - render themed progress meters r=Neil --HG-- extra : rebase_source : b26efa08d043384be6f588d48857e381ba86e9ad --- widget/windows/nsNativeThemeWin.cpp | 206 +++++++++++++--------------- widget/windows/nsNativeThemeWin.h | 6 + 2 files changed, 104 insertions(+), 108 deletions(-) diff --git a/widget/windows/nsNativeThemeWin.cpp b/widget/windows/nsNativeThemeWin.cpp index bc8a36da179..b9a140eb96e 100644 --- a/widget/windows/nsNativeThemeWin.cpp +++ b/widget/windows/nsNativeThemeWin.cpp @@ -543,6 +543,85 @@ nsNativeThemeWin::CalculateProgressOverlayRect(nsIFrame* aFrame, return overlayRect; } +/* + * DrawProgressMeter - renders an appropriate progress meter based on progress + * meter style, orientation, and os. Note, this does not render the underlying + * progress track. + * + * @param aFrame the widget frame + * @param aWidgetType type of widget + * @param aTheme progress theme handle + * @param aHdc hdc returned by gfxWindowsNativeDrawing + * @param aPart the PP_X progress part + * @param aState the theme state + * @param aWidgetRect bounding rect for the widget + * @param aClipRect dirty rect that needs drawing. + * @param aAppUnits app units per device pixel + */ +void +nsNativeThemeWin::DrawThemedProgressMeter(nsIFrame* aFrame, int aWidgetType, + HANDLE aTheme, HDC aHdc, + int aPart, int aState, + RECT* aWidgetRect, RECT* aClipRect, + gfxFloat aAppUnits) +{ + if (!aFrame || !aTheme || !aHdc) + return; + + NS_ASSERTION(aWidgetRect, "bad rect pointer"); + NS_ASSERTION(aClipRect, "bad clip rect pointer"); + + RECT adjWidgetRect, adjClipRect; + adjWidgetRect = *aWidgetRect; + adjClipRect = *aClipRect; + if (WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION) { + // Adjust clipping out by one pixel. XP progress meters are inset, + // Vista+ are not. + InflateRect(&adjWidgetRect, 1, 1); + InflateRect(&adjClipRect, 1, 1); + } + + nsIFrame* parentFrame = aFrame->GetParent(); + if (!parentFrame) { + // We have no parent to work with, just bail. + NS_WARNING("No parent frame for progress rendering. Can't paint."); + return; + } + + nsEventStates eventStates = GetContentState(parentFrame, aWidgetType); + bool vertical = IsVerticalProgress(parentFrame) || + aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL; + bool indeterminate = IsIndeterminateProgress(parentFrame, eventStates); + bool animate = indeterminate; + + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { + // Vista and up progress meter is fill style, rendered here. We render + // the pulse overlay in the follow up section below. + DrawThemeBackground(aTheme, aHdc, aPart, aState, + &adjWidgetRect, &adjClipRect); + if (!IsProgressMeterFilled(aFrame)) { + animate = true; + } + } else if (!indeterminate) { + DrawThemeBackground(aTheme, aHdc, aPart, aState, + &adjWidgetRect, &adjClipRect); + } + + if (animate) { + // Indeterminate rendering + int32_t overlayPart = GetProgressOverlayStyle(vertical); + RECT overlayRect = + CalculateProgressOverlayRect(aFrame, &adjWidgetRect, vertical, + indeterminate, false); + DrawThemeBackground(aTheme, aHdc, overlayPart, aState, &overlayRect, + &adjClipRect); + + if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 60)) { + NS_WARNING("unable to animate progress widget!"); + } + } +} + HANDLE nsNativeThemeWin::GetTheme(uint8_t aWidgetType) { @@ -822,25 +901,23 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType, aState = TS_NORMAL; return NS_OK; } - case NS_THEME_PROGRESSBAR: { - aPart = IsVerticalProgress(aFrame) ? PP_BARVERT : PP_BAR; - aState = PBBS_NORMAL; - return NS_OK; - } + case NS_THEME_PROGRESSBAR: case NS_THEME_PROGRESSBAR_VERTICAL: { - aPart = PP_BARVERT; + // Note IsVerticalProgress only tests for orient css attrribute, + // NS_THEME_PROGRESSBAR_VERTICAL is dedicated to -moz-appearance: + // progressbar-vertical. + bool vertical = IsVerticalProgress(aFrame) || + aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL; + aPart = vertical ? PP_BARVERT : PP_BAR; aState = PBBS_NORMAL; return NS_OK; } - case NS_THEME_PROGRESSBAR_CHUNK: { - nsIFrame* stateFrame = aFrame->GetParent(); - nsEventStates eventStates = GetContentState(stateFrame, aWidgetType); - - if (IsIndeterminateProgress(stateFrame, eventStates)) { - // If the element is indeterminate, we are going to render it ourself so - // we have to return aPart = -1. - aPart = -1; - } else if (IsVerticalProgress(stateFrame)) { + case NS_THEME_PROGRESSBAR_CHUNK: + case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: { + nsIFrame* parentFrame = aFrame->GetParent(); + nsEventStates eventStates = GetContentState(parentFrame, aWidgetType); + if (aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL || + IsVerticalProgress(parentFrame)) { aPart = WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ? PP_FILLVERT : PP_CHUNKVERT; } else { @@ -848,12 +925,7 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType, PP_FILL : PP_CHUNK; } - aState = TS_NORMAL; - return NS_OK; - } - case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: { - aPart = PP_CHUNKVERT; - aState = TS_NORMAL; + aState = PBBVS_NORMAL; return NS_OK; } case NS_THEME_TOOLBAR_BUTTON: { @@ -1621,6 +1693,11 @@ RENDER_AGAIN: SetPixel(hdc, widgetRect.right-1, widgetRect.bottom-1, color); SetPixel(hdc, widgetRect.left, widgetRect.bottom-1, color); } + else if (aWidgetType == NS_THEME_PROGRESSBAR_CHUNK || + aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL) { + DrawThemedProgressMeter(aFrame, aWidgetType, theme, hdc, part, state, + &widgetRect, &clipRect, p2a); + } // If part is negative, the element wishes us to not render a themed // background, instead opting to be drawn specially below. else if (part >= 0) { @@ -1741,95 +1818,8 @@ RENDER_AGAIN: ctx->Restore(); ctx->SetOperator(currentOp); - } else if (aWidgetType == NS_THEME_PROGRESSBAR_CHUNK) { - /** - * Here, we draw the animated part of the progress bar. - * A progress bar has always an animated part on Windows Vista and later. - * On Windows XP, a progress bar has an animated part when in an - * indeterminated state. - * When the progress bar is indeterminated, no background is painted so we - * only see the animated part. - * When the progress bar is determinated, the animated part is a glow draw - * on top of the background (PP_FILL). - */ - nsIFrame* stateFrame = aFrame->GetParent(); - nsEventStates eventStates = GetContentState(stateFrame, aWidgetType); - bool indeterminate = IsIndeterminateProgress(stateFrame, eventStates); - bool vertical = IsVerticalProgress(stateFrame); - - if (indeterminate || - WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { - if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) { - NS_WARNING("unable to animate progress widget!"); - } - - /** - * Unfortunately, vertical progress bar support on Windows seems weak and - * PP_MOVEOVERLAYRECT looks really different from PP_MOVEOVERLAY. - * Thus, we have to change the size and even don't use it for vertical - * indeterminate progress bars. - */ - int32_t overlaySize; - if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { - if (vertical) { - overlaySize = indeterminate ? kProgressVerticalIndeterminateOverlaySize - : kProgressVerticalOverlaySize; - } else { - overlaySize = kProgressHorizontalVistaOverlaySize; - } - } else { - overlaySize = kProgressHorizontalXPOverlaySize; - } - - const double pixelsPerMillisecond = indeterminate - ? kProgressIndeterminateSpeed - : kProgressDeterminedVistaSpeed; - const int32_t delay = indeterminate ? kProgressIndeterminateDelay - : kProgressDeterminedVistaDelay; - - const int32_t frameSize = vertical ? widgetRect.bottom - widgetRect.top - : widgetRect.right - widgetRect.left; - const int32_t animationSize = frameSize + overlaySize + - static_cast(pixelsPerMillisecond * delay); - const double interval = animationSize / pixelsPerMillisecond; - // We have to pass a double* to modf and we can't pass NULL. - double tempValue; - double ratio = modf(PR_IntervalToMilliseconds(PR_IntervalNow())/interval, - &tempValue); - // If the frame direction is RTL, we want to have the animation going RTL. - // ratio is in [0.0; 1.0[ range, inverting it reverse the animation. - if (!vertical && IsFrameRTL(aFrame)) { - ratio = 1.0 - ratio; - } - int32_t dx = static_cast(animationSize * ratio) - overlaySize; - - RECT overlayRect = widgetRect; - if (vertical) { - overlayRect.bottom -= dx; - overlayRect.top = overlayRect.bottom - overlaySize; - } else { - overlayRect.left += dx; - overlayRect.right = overlayRect.left + overlaySize; - } - - int32_t overlayPart; - if (vertical) { - if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { - overlayPart = indeterminate ? PP_MOVEOVERLAY : PP_MOVEOVERLAYVERT; - } else { - overlayPart = PP_CHUNKVERT; - } - } else { - overlayPart = WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ? - PP_MOVEOVERLAY : PP_CHUNK; - } - - DrawThemeBackground(theme, hdc, overlayPart, state, &overlayRect, - &clipRect); - } } - nativeDrawing.EndNativeDrawing(); if (nativeDrawing.ShouldRenderAgain()) diff --git a/widget/windows/nsNativeThemeWin.h b/widget/windows/nsNativeThemeWin.h index bdb5be8b797..809a96bb0c6 100644 --- a/widget/windows/nsNativeThemeWin.h +++ b/widget/windows/nsNativeThemeWin.h @@ -8,6 +8,7 @@ #include "nsCOMPtr.h" #include "nsIAtom.h" #include "nsNativeTheme.h" +#include "gfxTypes.h" #include #include "mozilla/TimeStamp.h" @@ -103,6 +104,11 @@ protected: RECT CalculateProgressOverlayRect(nsIFrame* aFrame, RECT* aWidgetRect, bool aIsVertical, bool aIsIndeterminate, bool aIsClassic); + void DrawThemedProgressMeter(nsIFrame* aFrame, int aWidgetType, + HANDLE aTheme, HDC aHdc, + int aPart, int aState, + RECT* aWidgetRect, RECT* aClipRect, + gfxFloat aAppUnits); private: TimeStamp mProgressDeterminateTimeStamp; From 5159b442ef445d84a4760ba17410cb23119eec51 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Mon, 25 Feb 2013 00:27:26 +0000 Subject: [PATCH 097/140] Bug 658829 part 5 - render classic progress meters r=Neil --HG-- extra : rebase_source : 17c054b7b996a2cae077dd672c1fddd2d7181a41 --- widget/windows/nsNativeThemeWin.cpp | 74 +++++------------------------ 1 file changed, 13 insertions(+), 61 deletions(-) diff --git a/widget/windows/nsNativeThemeWin.cpp b/widget/windows/nsNativeThemeWin.cpp index b9a140eb96e..1e4ea50bcca 100644 --- a/widget/windows/nsNativeThemeWin.cpp +++ b/widget/windows/nsNativeThemeWin.cpp @@ -3542,75 +3542,27 @@ RENDER_AGAIN: case NS_THEME_PROGRESSBAR_CHUNK: { nsIFrame* stateFrame = aFrame->GetParent(); nsEventStates eventStates = GetContentState(stateFrame, aWidgetType); - const bool indeterminate = IsIndeterminateProgress(stateFrame, eventStates); - if (!indeterminate) { + bool indeterminate = IsIndeterminateProgress(stateFrame, eventStates); + bool vertical = IsVerticalProgress(stateFrame) || + aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL; + int32_t overlayPart = GetProgressOverlayStyle(vertical); + + nsIContent* content = aFrame->GetContent(); + if (!indeterminate || !content) { ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_HIGHLIGHT+1)); break; } - /** - * The indeterminate rendering is animated: the bar goes from one side to - * another. - */ + RECT overlayRect = + CalculateProgressOverlayRect(aFrame, &widgetRect, vertical, + indeterminate, true); + + ::FillRect(hdc, &overlayRect, (HBRUSH) (COLOR_HIGHLIGHT+1)); + if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) { NS_WARNING("unable to animate progress widget!"); } - - const bool vertical = IsVerticalProgress(stateFrame); - const int32_t overlaySize = kProgressClassicOverlaySize; - const double pixelsPerMillisecond = kProgressClassicIndeterminateSpeed; - const int32_t frameSize = vertical ? widgetRect.bottom - widgetRect.top - : widgetRect.right - widgetRect.left; - const double interval = frameSize / pixelsPerMillisecond; - // We have to pass a double* to modf and we can't pass NULL. - double tempValue; - double ratio = modf(PR_IntervalToMilliseconds(PR_IntervalNow())/interval, - &tempValue); - int32_t dx = 0; - - // If the frame direction is RTL, we want to have the animation going RTL. - // ratio is in [0.0; 1.0[ range, inverting it reverse the animation. - if (!vertical && IsFrameRTL(aFrame)) { - ratio = 1.0 - ratio; - dx -= overlaySize; - } - dx += static_cast(frameSize * ratio); - - RECT overlayRect = widgetRect; - if (vertical) { - overlayRect.bottom -= dx; - overlayRect.top = overlayRect.bottom - overlaySize; - } else { - overlayRect.left += dx; - overlayRect.right = overlayRect.left + overlaySize; - } - - // Compute the leftover part. - RECT leftoverRect = widgetRect; - if (vertical) { - if (overlayRect.top < widgetRect.top) { - leftoverRect.bottom = widgetRect.bottom; - leftoverRect.top = leftoverRect.bottom + overlayRect.top - widgetRect.top; - } - } else if (IsFrameRTL(aFrame)) { - if (overlayRect.left < widgetRect.left) { - leftoverRect.right = widgetRect.right; - leftoverRect.left = leftoverRect.right + overlayRect.left - widgetRect.left; - } - } else if (overlayRect.right > widgetRect.right) { - leftoverRect.left = widgetRect.left; - leftoverRect.right = leftoverRect.left + overlayRect.right - widgetRect.right; - } - - // Only show the leftover if the rect has been modified. - if (leftoverRect.top != widgetRect.top || - leftoverRect.left != widgetRect.left || - leftoverRect.right != widgetRect.right) { - ::FillRect(hdc, &leftoverRect, (HBRUSH) (COLOR_HIGHLIGHT+1)); - } - - ::FillRect(hdc, &overlayRect, (HBRUSH) (COLOR_HIGHLIGHT+1)); break; } From 48ed13fa992daef71252b02367aa787c6540a376 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Mon, 25 Feb 2013 10:17:06 +0000 Subject: [PATCH 098/140] Bug 729549 Progress meters on XP should be chunk style r=Neil --HG-- extra : rebase_source : 16f5256264ca8bb7d6ecda25f4a61b9605e7056a --- widget/windows/nsNativeThemeWin.cpp | 99 +++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/widget/windows/nsNativeThemeWin.cpp b/widget/windows/nsNativeThemeWin.cpp index 1e4ea50bcca..284c9e58044 100644 --- a/widget/windows/nsNativeThemeWin.cpp +++ b/widget/windows/nsNativeThemeWin.cpp @@ -544,7 +544,88 @@ nsNativeThemeWin::CalculateProgressOverlayRect(nsIFrame* aFrame, } /* - * DrawProgressMeter - renders an appropriate progress meter based on progress + * DrawChunkProgressMeter - renders an xp style chunked progress meter. Called + * by DrawProgressMeter. + * + * @param aTheme progress theme handle + * @param aHdc hdc returned by gfxWindowsNativeDrawing + * @param aPart the PP_X progress part + * @param aState the theme state + * @param aFrame the elements frame + * @param aWidgetRect bounding rect for the widget + * @param aClipRect dirty rect that needs drawing. + * @param aAppUnits app units per device pixel + * @param aIsIndeterm is an indeterminate progress? + * @param aIsVertical render a vertical progress? + * @param aIsRtl direction is rtl + */ +static void +DrawChunkProgressMeter(HTHEME aTheme, HDC aHdc, int aPart, + int aState, nsIFrame* aFrame, RECT* aWidgetRect, + RECT* aClipRect, gfxFloat aAppUnits, bool aIsIndeterm, + bool aIsVertical, bool aIsRtl) +{ + NS_ASSERTION(aTheme, "Bad theme."); + NS_ASSERTION(aHdc, "Bad hdc."); + NS_ASSERTION(aWidgetRect, "Bad rect."); + NS_ASSERTION(aClipRect, "Bad clip rect."); + NS_ASSERTION(aFrame, "Bad frame."); + + // For horizontal meters, the theme lib paints the right graphic but doesn't + // paint the chunks, so we do that manually. For vertical meters, the theme + // library draws everything correctly. + if (aIsVertical) { + DrawThemeBackground(aTheme, aHdc, aPart, aState, aWidgetRect, aClipRect); + return; + } + + // query for the proper chunk metrics + int chunkSize, spaceSize; + if (FAILED(GetThemeMetric(aTheme, aHdc, aPart, aState, + TMT_PROGRESSCHUNKSIZE, &chunkSize)) || + FAILED(GetThemeMetric(aTheme, aHdc, aPart, aState, + TMT_PROGRESSSPACESIZE, &spaceSize))) { + DrawThemeBackground(aTheme, aHdc, aPart, aState, aWidgetRect, aClipRect); + return; + } + + // render chunks + if (!aIsRtl || aIsIndeterm) { + for (int chunk = aWidgetRect->left; chunk <= aWidgetRect->right; + chunk += (chunkSize+spaceSize)) { + if (!aIsIndeterm && ((chunk + chunkSize) > aWidgetRect->right)) { + // aWidgetRect->right represents the end of the meter. Partial blocks + // don't get rendered with one exception, so exit here if we don't have + // a full chunk to draw. + // The above is true *except* when the meter is at 100% fill, in which + // case Windows renders any remaining partial block. Query the parent + // frame to find out if we're at 100%. + if (!IsProgressMeterFilled(aFrame)) { + break; + } + } + RECT bounds = + { chunk, aWidgetRect->top, chunk + chunkSize, aWidgetRect->bottom }; + DrawThemeBackground(aTheme, aHdc, aPart, aState, &bounds, aClipRect); + } + } else { + // rtl needs to grow in the opposite direction to look right. + for (int chunk = aWidgetRect->right; chunk >= aWidgetRect->left; + chunk -= (chunkSize+spaceSize)) { + if ((chunk - chunkSize) < aWidgetRect->left) { + if (!IsProgressMeterFilled(aFrame)) { + break; + } + } + RECT bounds = + { chunk - chunkSize, aWidgetRect->top, chunk, aWidgetRect->bottom }; + DrawThemeBackground(aTheme, aHdc, aPart, aState, &bounds, aClipRect); + } + } +} + +/* + * DrawProgressMeter - render an appropriate progress meter based on progress * meter style, orientation, and os. Note, this does not render the underlying * progress track. * @@ -603,8 +684,10 @@ nsNativeThemeWin::DrawThemedProgressMeter(nsIFrame* aFrame, int aWidgetType, animate = true; } } else if (!indeterminate) { - DrawThemeBackground(aTheme, aHdc, aPart, aState, - &adjWidgetRect, &adjClipRect); + // XP progress meters are 'chunk' style. + DrawChunkProgressMeter(aTheme, aHdc, aPart, aState, aFrame, + &adjWidgetRect, &adjClipRect, aAppUnits, + indeterminate, vertical, IsFrameRTL(aFrame)); } if (animate) { @@ -613,8 +696,14 @@ nsNativeThemeWin::DrawThemedProgressMeter(nsIFrame* aFrame, int aWidgetType, RECT overlayRect = CalculateProgressOverlayRect(aFrame, &adjWidgetRect, vertical, indeterminate, false); - DrawThemeBackground(aTheme, aHdc, overlayPart, aState, &overlayRect, - &adjClipRect); + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { + DrawThemeBackground(aTheme, aHdc, overlayPart, aState, &overlayRect, + &adjClipRect); + } else { + DrawChunkProgressMeter(aTheme, aHdc, overlayPart, aState, aFrame, + &overlayRect, &adjClipRect, aAppUnits, + indeterminate, vertical, IsFrameRTL(aFrame)); + } if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 60)) { NS_WARNING("unable to animate progress widget!"); From 4ae83c2e4b25f4be1251efaa851b0133cdd5d701 Mon Sep 17 00:00:00 2001 From: Simon Montagu Date: Fri, 1 Mar 2013 03:56:44 -0800 Subject: [PATCH 099/140] New algorithm for when to display IDNs as Unicode. Bug 722299, r=honza.b --- modules/libpref/src/init/all.js | 16 +- netwerk/dns/nsIDNService.cpp | 277 +++++++++++++++++++++++++++++--- netwerk/dns/nsIDNService.h | 22 ++- 3 files changed, 279 insertions(+), 36 deletions(-) diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 2ff4112dc6b..a5b14baef12 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1052,10 +1052,18 @@ pref("network.IDN_show_punycode", false); // TLDs with "network.IDN.whitelist.tld" explicitly set to true are treated as // IDN-safe. Otherwise, they're treated as unsafe and punycode will be used -// for displaying them in the UI (e.g. URL bar). Note that these preferences -// are referred to ONLY when "network.IDN_show_punycode" is false. In other -// words, all IDNs will be shown in punycode if "network.IDN_show_punycode" -// is true. +// for displaying them in the UI (e.g. URL bar), unless they conform to one of +// the profiles specified in +// http://www.unicode.org/reports/tr36/proposed.html#Security_Levels_and_Alerts +// If "network.IDN.restriction_profile" is "high", the Highly Restrictive +// profile is used. +// If "network.IDN.restriction_profile" is "moderate", the Moderately +// Restrictive profile is used. +// In all other cases, the ASCII-Only profile is used. +// Note that these preferences are referred to ONLY when +// "network.IDN_show_punycode" is false. In other words, all IDNs will be shown +// in punycode if "network.IDN_show_punycode" is true. +pref("network.IDN.restriction_profile", "moderate"); // ccTLDs pref("network.IDN.whitelist.ac", true); diff --git a/netwerk/dns/nsIDNService.cpp b/netwerk/dns/nsIDNService.cpp index ad23d1e9a06..eb3450ed765 100644 --- a/netwerk/dns/nsIDNService.cpp +++ b/netwerk/dns/nsIDNService.cpp @@ -7,6 +7,9 @@ #include "nsReadableUtils.h" #include "nsCRT.h" #include "nsUnicharUtils.h" +#include "nsUnicodeProperties.h" +#include "nsUnicodeScriptCodes.h" +#include "harfbuzz/hb.h" #include "nsIServiceManager.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" @@ -15,6 +18,8 @@ #include "punycode.h" +using namespace mozilla::unicode; + //----------------------------------------------------------------------------- // RFC 1034 - 3.1. Name space specifications and terminology static const uint32_t kMaxDNSNodeLen = 63; @@ -26,6 +31,7 @@ static const uint32_t kMaxDNSNodeLen = 63; #define NS_NET_PREF_IDNBLACKLIST "network.IDN.blacklist_chars" #define NS_NET_PREF_SHOWPUNYCODE "network.IDN_show_punycode" #define NS_NET_PREF_IDNWHITELIST "network.IDN.whitelist." +#define NS_NET_PREF_IDNRESTRICTION "network.IDN.restriction_profile" inline bool isOnlySafeChars(const nsAFlatString& in, const nsAFlatString& blacklist) @@ -56,6 +62,7 @@ nsresult nsIDNService::Init() prefInternal->AddObserver(NS_NET_PREF_IDNPREFIX, this, true); prefInternal->AddObserver(NS_NET_PREF_IDNBLACKLIST, this, true); prefInternal->AddObserver(NS_NET_PREF_SHOWPUNYCODE, this, true); + prefInternal->AddObserver(NS_NET_PREF_IDNRESTRICTION, this, true); prefsChanged(prefInternal, nullptr); } @@ -102,6 +109,20 @@ void nsIDNService::prefsChanged(nsIPrefBranch *prefBranch, const PRUnichar *pref if (NS_SUCCEEDED(prefBranch->GetBoolPref(NS_NET_PREF_SHOWPUNYCODE, &val))) mShowPunycode = val; } + if (!pref || NS_LITERAL_STRING(NS_NET_PREF_IDNRESTRICTION).Equals(pref)) { + nsXPIDLCString profile; + if (NS_FAILED(prefBranch->GetCharPref(NS_NET_PREF_IDNRESTRICTION, + getter_Copies(profile)))) { + profile.Truncate(); + } + if (profile.Equals(NS_LITERAL_CSTRING("moderate"))) { + mRestrictionProfile = eModeratelyRestrictiveProfile; + } else if (profile.Equals(NS_LITERAL_CSTRING("high"))) { + mRestrictionProfile = eHighlyRestrictiveProfile; + } else { + mRestrictionProfile = eASCIIOnlyProfile; + } + } } nsIDNService::nsIDNService() @@ -127,10 +148,15 @@ nsIDNService::~nsIDNService() /* ACString ConvertUTF8toACE (in AUTF8String input); */ NS_IMETHODIMP nsIDNService::ConvertUTF8toACE(const nsACString & input, nsACString & ace) { - return UTF8toACE(input, ace, true); + return UTF8toACE(input, ace, true, true); } -nsresult nsIDNService::UTF8toACE(const nsACString & input, nsACString & ace, bool allowUnassigned) +nsresult nsIDNService::SelectiveUTF8toACE(const nsACString& input, nsACString& ace) +{ + return UTF8toACE(input, ace, true, false); +} + +nsresult nsIDNService::UTF8toACE(const nsACString & input, nsACString & ace, bool allowUnassigned, bool convertAllLabels) { nsresult rv; NS_ConvertUTF8toUTF16 ustr(input); @@ -154,7 +180,7 @@ nsresult nsIDNService::UTF8toACE(const nsACString & input, nsACString & ace, boo len++; if (*start++ == (PRUnichar)'.') { rv = stringPrepAndACE(Substring(ustr, offset, len - 1), encodedBuf, - allowUnassigned); + allowUnassigned, convertAllLabels); NS_ENSURE_SUCCESS(rv, rv); ace.Append(encodedBuf); @@ -170,7 +196,7 @@ nsresult nsIDNService::UTF8toACE(const nsACString & input, nsACString & ace, boo // encode the last node if non ASCII if (len) { rv = stringPrepAndACE(Substring(ustr, offset, len), encodedBuf, - allowUnassigned); + allowUnassigned, convertAllLabels); NS_ENSURE_SUCCESS(rv, rv); ace.Append(encodedBuf); @@ -182,21 +208,21 @@ nsresult nsIDNService::UTF8toACE(const nsACString & input, nsACString & ace, boo /* AUTF8String convertACEtoUTF8(in ACString input); */ NS_IMETHODIMP nsIDNService::ConvertACEtoUTF8(const nsACString & input, nsACString & _retval) { - return ACEtoUTF8(input, _retval, true); + return ACEtoUTF8(input, _retval, true, true); +} + +nsresult nsIDNService::SelectiveACEtoUTF8(const nsACString& input, nsACString& _retval) +{ + return ACEtoUTF8(input, _retval, false, false); } nsresult nsIDNService::ACEtoUTF8(const nsACString & input, nsACString & _retval, - bool allowUnassigned) + bool allowUnassigned, bool convertAllLabels) { // RFC 3490 - 4.2 ToUnicode // ToUnicode never fails. If any step fails, then the original input // sequence is returned immediately in that step. - if (!IsASCII(input)) { - _retval.Assign(input); - return NS_OK; - } - uint32_t len = 0, offset = 0; nsAutoCString decodedBuf; @@ -210,7 +236,7 @@ nsresult nsIDNService::ACEtoUTF8(const nsACString & input, nsACString & _retval, len++; if (*start++ == '.') { if (NS_FAILED(decodeACE(Substring(input, offset, len - 1), decodedBuf, - allowUnassigned))) { + allowUnassigned, convertAllLabels))) { _retval.Assign(input); return NS_OK; } @@ -224,7 +250,7 @@ nsresult nsIDNService::ACEtoUTF8(const nsACString & input, nsACString & _retval, // decode the last node if (len) { if (NS_FAILED(decodeACE(Substring(input, offset, len), decodedBuf, - allowUnassigned))) + allowUnassigned, convertAllLabels))) _retval.Assign(input); else _retval.Append(decodedBuf); @@ -301,20 +327,29 @@ NS_IMETHODIMP nsIDNService::ConvertToDisplayIDN(const nsACString & input, bool * // If host is ACE, then convert to UTF-8 if the host is in the IDN whitelist. // Else, if host is already UTF-8, then make sure it is normalized per IDN. - nsresult rv; + nsresult rv = NS_OK; + + // Even if the hostname is not ASCII, individual labels may still be ACE, so + // test IsACE before testing IsASCII + bool isACE; + IsACE(input, &isACE); if (IsASCII(input)) { // first, canonicalize the host to lowercase, for whitelist lookup _retval = input; ToLowerCase(_retval); - bool isACE; - IsACE(_retval, &isACE); - - if (isACE && !mShowPunycode && isInWhitelist(_retval)) { + if (isACE && !mShowPunycode) { // ACEtoUTF8() can't fail, but might return the original ACE string nsAutoCString temp(_retval); - ACEtoUTF8(temp, _retval, false); + if (isInWhitelist(temp)) { + // If the domain is in the whitelist, return the host in UTF-8 + ACEtoUTF8(temp, _retval, false, true); + } else { + // Otherwise convert from ACE to UTF8 only those labels which are + // considered safe for display + SelectiveACEtoUTF8(temp, _retval); + } *_isASCII = IsASCII(_retval); } else { *_isASCII = true; @@ -323,7 +358,16 @@ NS_IMETHODIMP nsIDNService::ConvertToDisplayIDN(const nsACString & input, bool * // We have to normalize the hostname before testing against the domain // whitelist (see bug 315411), and to ensure the entire string gets // normalized. - rv = Normalize(input, _retval); + // + // Normalization and the tests for safe display below, assume that the + // input is Unicode, so first convert any ACE labels to UTF8 + if (isACE) { + nsAutoCString temp; + ACEtoUTF8(input, temp, false, true); + rv = Normalize(temp, _retval); + } else { + rv = Normalize(input, _retval); + } if (NS_FAILED(rv)) return rv; if (mShowPunycode && NS_SUCCEEDED(ConvertUTF8toACE(_retval, _retval))) { @@ -336,8 +380,12 @@ NS_IMETHODIMP nsIDNService::ConvertToDisplayIDN(const nsACString & input, bool * // unsafe characters, so leave it ACE encoded. see bug 283016, bug 301694, and bug 309311. *_isASCII = IsASCII(_retval); if (!*_isASCII && !isInWhitelist(_retval)) { - *_isASCII = true; - return ConvertUTF8toACE(_retval, _retval); + // SelectiveUTF8toACE may return a domain name where some labels are in UTF-8 + // and some are in ACE, depending on whether they are considered safe for + // display + rv = SelectiveUTF8toACE(_retval, _retval); + *_isASCII = IsASCII(_retval); + return rv; } } @@ -542,7 +590,8 @@ nsresult nsIDNService::encodeToACE(const nsAString& in, nsACString& out) } nsresult nsIDNService::stringPrepAndACE(const nsAString& in, nsACString& out, - bool allowUnassigned) + bool allowUnassigned, + bool convertAllLabels) { nsresult rv = NS_OK; @@ -555,6 +604,8 @@ nsresult nsIDNService::stringPrepAndACE(const nsAString& in, nsACString& out, if (IsASCII(in)) LossyCopyUTF16toASCII(in, out); + else if (!convertAllLabels && isLabelSafe(in)) + CopyUTF16toUTF8(in, out); else { nsAutoString strPrep; rv = stringPrep(in, strPrep, allowUnassigned); @@ -603,7 +654,7 @@ void nsIDNService::normalizeFullStops(nsAString& s) } nsresult nsIDNService::decodeACE(const nsACString& in, nsACString& out, - bool allowUnassigned) + bool allowUnassigned, bool convertAllLabels) { bool isAce; IsACE(in, &isAce); @@ -633,13 +684,17 @@ nsresult nsIDNService::decodeACE(const nsACString& in, nsACString& out, nsAutoString utf16; ucs4toUtf16(output, utf16); delete [] output; + if (!convertAllLabels && !isLabelSafe(utf16)) { + out.Assign(in); + return NS_OK; + } if (!isOnlySafeChars(utf16, mIDNBlacklist)) return NS_ERROR_FAILURE; CopyUTF16toUTF8(utf16, out); // Validation: encode back to ACE and compare the strings nsAutoCString ace; - nsresult rv = UTF8toACE(out, ace, allowUnassigned); + nsresult rv = UTF8toACE(out, ace, allowUnassigned, true); NS_ENSURE_SUCCESS(rv, rv); if (!ace.Equals(in, nsCaseInsensitiveCStringComparator())) @@ -654,7 +709,7 @@ bool nsIDNService::isInWhitelist(const nsACString &host) nsAutoCString tld(host); // make sure the host is ACE for lookup and check that there are no // unassigned codepoints - if (!IsASCII(tld) && NS_FAILED(UTF8toACE(tld, tld, false))) { + if (!IsASCII(tld) && NS_FAILED(UTF8toACE(tld, tld, false, true))) { return false; } @@ -674,3 +729,173 @@ bool nsIDNService::isInWhitelist(const nsACString &host) return false; } +bool nsIDNService::isLabelSafe(const nsAString &label) +{ + // We should never get here if the label is ASCII + NS_ASSERTION(!IsASCII(label), "ASCII label in IDN checking"); + if (mRestrictionProfile == eASCIIOnlyProfile) { + return false; + } + + nsAString::const_iterator current, end; + label.BeginReading(current); + label.EndReading(end); + + int32_t lastScript = MOZ_SCRIPT_INVALID; + uint32_t previousChar = 0; + uint32_t savedNumberingSystem = 0; + HanVariantType savedHanVariant = HVT_NotHan; + + int32_t savedScript = -1; + + while (current != end) { + uint32_t ch = *current++; + + if (NS_IS_HIGH_SURROGATE(ch) && current != end && + NS_IS_LOW_SURROGATE(*current)) { + ch = SURROGATE_TO_UCS4(ch, *current++); + } + + // Check for restricted characters; aspirational scripts are permitted + XidmodType xm = GetIdentifierModification(ch); + int32_t script = GetScriptCode(ch); + if (xm > XIDMOD_RECOMMENDED && + !(xm == XIDMOD_LIMITED_USE && + (script == MOZ_SCRIPT_CANADIAN_ABORIGINAL || + script == MOZ_SCRIPT_MIAO || + script == MOZ_SCRIPT_MONGOLIAN || + script == MOZ_SCRIPT_TIFINAGH || + script == MOZ_SCRIPT_YI))) { + return false; + } + + // Check for mixed script + if (script != MOZ_SCRIPT_COMMON && + script != MOZ_SCRIPT_INHERITED && + script != lastScript) { + if (illegalScriptCombo(script, savedScript)) { + return false; + } + lastScript = script; + } + + // Check for mixed numbering systems + if (GetGeneralCategory(ch) == + HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) { + uint32_t zeroCharacter = ch - GetNumericValue(ch); + if (savedNumberingSystem == 0) { + // If we encounter a decimal number, save the zero character from that + // numbering system. + savedNumberingSystem = zeroCharacter; + } else if (zeroCharacter != savedNumberingSystem) { + return false; + } + } + + // Check for consecutive non-spacing marks + if (previousChar != 0 && + previousChar == ch && + GetGeneralCategory(ch) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) { + return false; + } + + // Check for both simplified-only and traditional-only Chinese characters + HanVariantType hanVariant = GetHanVariant(ch); + if (hanVariant == HVT_SimplifiedOnly || hanVariant == HVT_TraditionalOnly) { + if (savedHanVariant == HVT_NotHan) { + savedHanVariant = hanVariant; + } else if (hanVariant != savedHanVariant) { + return false; + } + } + + previousChar = ch; + } + return true; +} + +// Scripts that we care about in illegalScriptCombo +static const int32_t scriptTable[] = { + MOZ_SCRIPT_BOPOMOFO, MOZ_SCRIPT_CYRILLIC, MOZ_SCRIPT_GREEK, + MOZ_SCRIPT_HANGUL, MOZ_SCRIPT_HAN, MOZ_SCRIPT_HIRAGANA, + MOZ_SCRIPT_KATAKANA, MOZ_SCRIPT_LATIN }; + +#define BOPO 0 +#define CYRL 1 +#define GREK 2 +#define HANG 3 +#define HANI 4 +#define HIRA 5 +#define KATA 6 +#define LATN 7 +#define OTHR 8 +#define JPAN 9 // Latin + Han + Hiragana + Katakana +#define CHNA 10 // Latin + Han + Bopomofo +#define KORE 11 // Latin + Han + Hangul +#define HNLT 12 // Latin + Han (could be any of the above combinations) +#define FAIL 13 + +static inline int32_t findScriptIndex(int32_t aScript) +{ + int32_t tableLength = sizeof(scriptTable) / sizeof(int32_t); + for (int32_t index = 0; index < tableLength; ++index) { + if (aScript == scriptTable[index]) { + return index; + } + } + return OTHR; +} + +static const int32_t scriptComboTable[13][9] = { +/* thisScript: BOPO CYRL GREK HANG HANI HIRA KATA LATN OTHR + * savedScript */ + /* BOPO */ { BOPO, FAIL, FAIL, FAIL, CHNA, FAIL, FAIL, CHNA, FAIL }, + /* CYRL */ { FAIL, CYRL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL }, + /* GREK */ { FAIL, FAIL, GREK, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL }, + /* HANG */ { FAIL, FAIL, FAIL, HANG, KORE, FAIL, FAIL, KORE, FAIL }, + /* HANI */ { CHNA, FAIL, FAIL, KORE, HANI, JPAN, JPAN, HNLT, FAIL }, + /* HIRA */ { FAIL, FAIL, FAIL, FAIL, JPAN, HIRA, JPAN, JPAN, FAIL }, + /* KATA */ { FAIL, FAIL, FAIL, FAIL, JPAN, JPAN, KATA, JPAN, FAIL }, + /* LATN */ { CHNA, FAIL, FAIL, KORE, HNLT, JPAN, JPAN, LATN, OTHR }, + /* OTHR */ { FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, OTHR, FAIL }, + /* JPAN */ { FAIL, FAIL, FAIL, FAIL, JPAN, JPAN, JPAN, JPAN, FAIL }, + /* CHNA */ { CHNA, FAIL, FAIL, FAIL, CHNA, FAIL, FAIL, CHNA, FAIL }, + /* KORE */ { FAIL, FAIL, FAIL, KORE, KORE, FAIL, FAIL, KORE, FAIL }, + /* HNLT */ { CHNA, FAIL, FAIL, KORE, HNLT, JPAN, JPAN, HNLT, FAIL } +}; + +bool nsIDNService::illegalScriptCombo(int32_t script, int32_t& savedScript) +{ + if (savedScript == -1) { + savedScript = findScriptIndex(script); + return false; + } + + savedScript = scriptComboTable[savedScript] [findScriptIndex(script)]; + /* + * Special case combinations that depend on which profile is in use + * In the Highly Restrictive profile Latin is not allowed with any + * other script + * + * In the Moderately Restrictive profile Latin mixed with any other + * single script is allowed. + */ + return ((savedScript == OTHR && + mRestrictionProfile == eHighlyRestrictiveProfile) || + savedScript == FAIL); +} + +#undef BOPO +#undef CYRL +#undef GREK +#undef HANG +#undef HANI +#undef HIRA +#undef KATA +#undef LATN +#undef OTHR +#undef JPAN +#undef CHNA +#undef KORE +#undef HNLT +#undef FAIL diff --git a/netwerk/dns/nsIDNService.h b/netwerk/dns/nsIDNService.h index d0a57165402..4a7ad805b1f 100644 --- a/netwerk/dns/nsIDNService.h +++ b/netwerk/dns/nsIDNService.h @@ -39,18 +39,22 @@ public: private: void normalizeFullStops(nsAString& s); nsresult stringPrepAndACE(const nsAString& in, nsACString& out, - bool allowUnassigned); + bool allowUnassigned, bool convertAllLabels); nsresult encodeToACE(const nsAString& in, nsACString& out); nsresult stringPrep(const nsAString& in, nsAString& out, bool allowUnassigned); nsresult decodeACE(const nsACString& in, nsACString& out, - bool allowUnassigned); - nsresult UTF8toACE(const nsACString& in, nsACString& out, - bool allowUnassigned); - nsresult ACEtoUTF8(const nsACString& in, nsACString& out, - bool allowUnassigned); + bool allowUnassigned, bool convertAllLabels); + nsresult SelectiveUTF8toACE(const nsACString& input, nsACString& ace); + nsresult SelectiveACEtoUTF8(const nsACString& input, nsACString& _retval); + nsresult UTF8toACE(const nsACString& input, nsACString& ace, + bool allowUnassigned, bool convertAllLabels); + nsresult ACEtoUTF8(const nsACString& input, nsACString& _retval, + bool allowUnassigned, bool convertAllLabels); bool isInWhitelist(const nsACString &host); void prefsChanged(nsIPrefBranch *prefBranch, const PRUnichar *pref); + bool isLabelSafe(const nsAString &label); + bool illegalScriptCombo(int32_t script, int32_t& savedScript); bool mMultilingualTestBed; // if true generates extra node for multilingual testbed idn_nameprep_t mNamePrepHandle; @@ -58,6 +62,12 @@ private: char mACEPrefix[kACEPrefixLen+1]; nsXPIDLString mIDNBlacklist; bool mShowPunycode; + enum restrictionProfile { + eASCIIOnlyProfile, + eHighlyRestrictiveProfile, + eModeratelyRestrictiveProfile + }; + restrictionProfile mRestrictionProfile; nsCOMPtr mIDNWhitelistPrefBranch; }; From 2acaa0502ac7a078513b0bc4958e59e113dc9b1d Mon Sep 17 00:00:00 2001 From: Simon Montagu Date: Fri, 1 Mar 2013 03:56:44 -0800 Subject: [PATCH 100/140] Add a pref to disable the IDN whitelist. Bug 722299, r=honza.b --- modules/libpref/src/init/all.js | 6 ++++-- netwerk/dns/nsIDNService.cpp | 10 +++++++++- netwerk/dns/nsIDNService.h | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index a5b14baef12..f99ec71afd4 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1050,9 +1050,10 @@ pref("network.enableIDN", true); // generate them from punycode. pref("network.IDN_show_punycode", false); -// TLDs with "network.IDN.whitelist.tld" explicitly set to true are treated as +// If "network.IDN.use_whitelist" is set to true, TLDs with +// "network.IDN.whitelist.tld" explicitly set to true are treated as // IDN-safe. Otherwise, they're treated as unsafe and punycode will be used -// for displaying them in the UI (e.g. URL bar), unless they conform to one of +// for displaying them in the UI (e.g. URL bar), unless they conform to one of // the profiles specified in // http://www.unicode.org/reports/tr36/proposed.html#Security_Levels_and_Alerts // If "network.IDN.restriction_profile" is "high", the Highly Restrictive @@ -1064,6 +1065,7 @@ pref("network.IDN_show_punycode", false); // "network.IDN_show_punycode" is false. In other words, all IDNs will be shown // in punycode if "network.IDN_show_punycode" is true. pref("network.IDN.restriction_profile", "moderate"); +pref("network.IDN.use_whitelist", true); // ccTLDs pref("network.IDN.whitelist.ac", true); diff --git a/netwerk/dns/nsIDNService.cpp b/netwerk/dns/nsIDNService.cpp index eb3450ed765..f59629e4edf 100644 --- a/netwerk/dns/nsIDNService.cpp +++ b/netwerk/dns/nsIDNService.cpp @@ -31,6 +31,7 @@ static const uint32_t kMaxDNSNodeLen = 63; #define NS_NET_PREF_IDNBLACKLIST "network.IDN.blacklist_chars" #define NS_NET_PREF_SHOWPUNYCODE "network.IDN_show_punycode" #define NS_NET_PREF_IDNWHITELIST "network.IDN.whitelist." +#define NS_NET_PREF_IDNUSEWHITELIST "network.IDN.use_whitelist" #define NS_NET_PREF_IDNRESTRICTION "network.IDN.restriction_profile" inline bool isOnlySafeChars(const nsAFlatString& in, @@ -63,6 +64,7 @@ nsresult nsIDNService::Init() prefInternal->AddObserver(NS_NET_PREF_IDNBLACKLIST, this, true); prefInternal->AddObserver(NS_NET_PREF_SHOWPUNYCODE, this, true); prefInternal->AddObserver(NS_NET_PREF_IDNRESTRICTION, this, true); + prefInternal->AddObserver(NS_NET_PREF_IDNUSEWHITELIST, this, true); prefsChanged(prefInternal, nullptr); } @@ -109,6 +111,12 @@ void nsIDNService::prefsChanged(nsIPrefBranch *prefBranch, const PRUnichar *pref if (NS_SUCCEEDED(prefBranch->GetBoolPref(NS_NET_PREF_SHOWPUNYCODE, &val))) mShowPunycode = val; } + if (!pref || NS_LITERAL_STRING(NS_NET_PREF_IDNUSEWHITELIST).Equals(pref)) { + bool val; + if (NS_SUCCEEDED(prefBranch->GetBoolPref(NS_NET_PREF_IDNUSEWHITELIST, + &val))) + mIDNUseWhitelist = val; + } if (!pref || NS_LITERAL_STRING(NS_NET_PREF_IDNRESTRICTION).Equals(pref)) { nsXPIDLCString profile; if (NS_FAILED(prefBranch->GetCharPref(NS_NET_PREF_IDNRESTRICTION, @@ -705,7 +713,7 @@ nsresult nsIDNService::decodeACE(const nsACString& in, nsACString& out, bool nsIDNService::isInWhitelist(const nsACString &host) { - if (mIDNWhitelistPrefBranch) { + if (mIDNUseWhitelist && mIDNWhitelistPrefBranch) { nsAutoCString tld(host); // make sure the host is ACE for lookup and check that there are no // unassigned codepoints diff --git a/netwerk/dns/nsIDNService.h b/netwerk/dns/nsIDNService.h index 4a7ad805b1f..705e57ddbfd 100644 --- a/netwerk/dns/nsIDNService.h +++ b/netwerk/dns/nsIDNService.h @@ -69,6 +69,7 @@ private: }; restrictionProfile mRestrictionProfile; nsCOMPtr mIDNWhitelistPrefBranch; + bool mIDNUseWhitelist; }; #endif // nsIDNService_h__ From 4c50d5e15def4c07af0b00ea386f56d509f61881 Mon Sep 17 00:00:00 2001 From: Simon Montagu Date: Fri, 1 Mar 2013 03:56:45 -0800 Subject: [PATCH 101/140] Changes to existing tests caused by bug 722299, r=honza.b --- build/pgo/server-locations.txt | 2 + dom/tests/mochitest/dom-level0/idn_child.html | 8 ++-- .../test_setting_document.domain_idn.html | 18 ++++---- .../whatwg/test_postMessage_origin.xhtml | 42 +++++++++---------- netwerk/test/unit/test_idnservice.js | 8 ++++ 5 files changed, 46 insertions(+), 32 deletions(-) diff --git a/build/pgo/server-locations.txt b/build/pgo/server-locations.txt index b86337720ff..dbc0cf4af3f 100644 --- a/build/pgo/server-locations.txt +++ b/build/pgo/server-locations.txt @@ -117,6 +117,8 @@ http://sub1.xn--lt-uia.example.org:8000 privileged http://sub2.xn--lt-uia.example.org:80 privileged http://xn--exmple-cua.test:80 privileged http://sub1.xn--exmple-cua.test:80 privileged +http://xn--exaple-kqf.test:80 privileged +http://sub1.xn--exaple-kqf.test:80 privileged https://xn--hxajbheg2az3al.xn--jxalpdlp:443 privileged https://sub1.xn--hxajbheg2az3al.xn--jxalpdlp:443 privileged diff --git a/dom/tests/mochitest/dom-level0/idn_child.html b/dom/tests/mochitest/dom-level0/idn_child.html index e13fdd5f940..61243b322e0 100644 --- a/dom/tests/mochitest/dom-level0/idn_child.html +++ b/dom/tests/mochitest/dom-level0/idn_child.html @@ -21,10 +21,10 @@ function receiveMessage(evt) if (/test$/.test(domain)) { // XXX should really be IDN (bug 414090) - //if (domain !== "sub1.exämple.test") + //if (domain !== "sub1.exaмple.test") // message += " wrong-initial-domain(" + domain + ")"; // for now expect the punycode value - if (domain !== "sub1.xn--exmple-cua.test") + if (domain !== "sub1.xn--exaple-kqf.test") message += " wrong-initial-domain(" + domain + ")"; } else @@ -44,11 +44,11 @@ function receiveMessage(evt) break; case "?idn-nowhitelist": - message += idnTest("exämple.test"); + message += idnTest("exaмple.test"); break; case "?punycode-nowhitelist": - message += punycodeTest("xn--exmple-cua.test"); + message += punycodeTest("xn--exaple-kqf.test"); break; default: diff --git a/dom/tests/mochitest/dom-level0/test_setting_document.domain_idn.html b/dom/tests/mochitest/dom-level0/test_setting_document.domain_idn.html index 0c15ff48b96..b2e36dbbfa8 100644 --- a/dom/tests/mochitest/dom-level0/test_setting_document.domain_idn.html +++ b/dom/tests/mochitest/dom-level0/test_setting_document.domain_idn.html @@ -38,6 +38,10 @@ that in the future this test tests what it was intended to test (and, until bug 414090 is fixed, that it doesn't break when the Greek TLD is un-whitelisted). +After bug 722299 the IDN whitelist is expected to go away (bug +843689), but bug 414090 still applies, mutatis mutandis. The test has +been changed to use exaмple.test instead (with a Cyrillic м), which +will fail the mixed-script tests and use punycode. -->
@@ -48,8 +52,8 @@ un-whitelisted).

Not whitelisted

- - + +
@@ -67,7 +71,7 @@ var whitelistRegex =
              "mochitest/dom-level0/idn_child\\.html\\?(.+)$");
 
 var noWhitelistRegex =
-  new RegExp("^http://sub1\\.exämple\\.test/tests/dom/tests/" +
+  new RegExp("^http://sub1\\.exaмple\\.test/tests/dom/tests/" +
              "mochitest/dom-level0/idn_child\\.html\\?(.+)$");
 
 var state = 0;
@@ -97,8 +101,8 @@ function receiveMessage(evt)
     //
     // These two tests should illustrate what currently happens and what should
     // happen once bug 414090 is fixed.
-    todo_is(evt.origin, "http://sub1.exämple.test", "wrong sender");
-    todo_isnot(evt.origin, "http://sub1.xn--exmple-cua.test", "wrong sender");
+    todo_is(evt.origin, "http://sub1.exaмple.test", "wrong sender");
+    todo_isnot(evt.origin, "http://sub1.xn--exaple-kqf.test", "wrong sender");
   }
   else
   {
@@ -169,7 +173,7 @@ function run()
           ok(gotPunycodeWhitelist, "punycode whitelist message not received");
 
           var target = window.frames.idnKidNoWhitelist;
-          target.postMessage("idn-nowhitelist", "http://sub1.exämple.test");
+          target.postMessage("idn-nowhitelist", "http://sub1.exaмple.test");
 
           setTimeout(function()
           {
@@ -179,7 +183,7 @@ function run()
     
               var target = window.frames.punycodeKidNoWhitelist;
               target.postMessage("punycode-nowhitelist",
-                                 "http://sub1.exämple.test");
+                                 "http://sub1.exaмple.test");
 
               setTimeout(function()
               {
diff --git a/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml b/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml
index e39ff722c26..f6a9198968f 100644
--- a/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml
+++ b/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml
@@ -26,7 +26,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=417075
 
 
-
 
 
@@ -313,73 +313,73 @@ var tests =
    },
    // 45
    {
-     args: ["PASS", "http://sub1.exämple.test"],
+     args: ["PASS", "http://sub1.exaмple.test"],
      source: "idnKidNoWhitelist",
-     returnOrigin: "http://sub1.exämple.test",
+     returnOrigin: "http://sub1.exaмple.test",
 
      hasWrongReturnOriginBug: true
    },
    {
-     args: ["PASS", "http://sub1.exämple.test:80"],
+     args: ["PASS", "http://sub1.exaмple.test:80"],
      source: "idnKidNoWhitelist",
-     returnOrigin: "http://sub1.exämple.test",
+     returnOrigin: "http://sub1.exaмple.test",
 
      hasWrongReturnOriginBug: true
    },
    {
-     args: ["PASS", "http://sub1.exämple.test:80/"],
+     args: ["PASS", "http://sub1.exaмple.test:80/"],
      source: "idnKidNoWhitelist",
-     returnOrigin: "http://sub1.exämple.test",
+     returnOrigin: "http://sub1.exaмple.test",
 
      hasWrongReturnOriginBug: true
    },
    {
-     args: ["PASS", "http://sub1.exämple.test/"],
+     args: ["PASS", "http://sub1.exaмple.test/"],
      source: "idnKidNoWhitelist",
-     returnOrigin: "http://sub1.exämple.test",
+     returnOrigin: "http://sub1.exaмple.test",
 
      hasWrongReturnOriginBug: true
    },
    {
-     args: ["PASS", "http://sub1.exämple.test/foobar"],
+     args: ["PASS", "http://sub1.exaмple.test/foobar"],
      source: "idnKidNoWhitelist",
-     returnOrigin: "http://sub1.exämple.test",
+     returnOrigin: "http://sub1.exaмple.test",
 
      hasWrongReturnOriginBug: true
    },
    // 50
    {
-     args: ["PASS", "http://sub1.xn--exmple-cua.test"],
+     args: ["PASS", "http://sub1.xn--exaple-kqf.test"],
      source: "idnKidNoWhitelist",
-     returnOrigin: "http://sub1.exämple.test",
+     returnOrigin: "http://sub1.exaмple.test",
 
      hasWrongReturnOriginBug: true
    },
    {
-     args: ["PASS", "http://sub1.xn--exmple-cua.test:80"],
+     args: ["PASS", "http://sub1.xn--exaple-kqf.test:80"],
      source: "idnKidNoWhitelist",
-     returnOrigin: "http://sub1.exämple.test",
+     returnOrigin: "http://sub1.exaмple.test",
 
      hasWrongReturnOriginBug: true
    },
    {
-     args: ["PASS", "http://sub1.xn--exmple-cua.test:80/"],
+     args: ["PASS", "http://sub1.xn--exaple-kqf.test:80/"],
      source: "idnKidNoWhitelist",
-     returnOrigin: "http://sub1.exämple.test",
+     returnOrigin: "http://sub1.exaмple.test",
 
      hasWrongReturnOriginBug: true
    },
    {
-     args: ["PASS", "http://sub1.xn--exmple-cua.test/"],
+     args: ["PASS", "http://sub1.xn--exaple-kqf.test/"],
      source: "idnKidNoWhitelist",
-     returnOrigin: "http://sub1.exämple.test",
+     returnOrigin: "http://sub1.exaмple.test",
 
      hasWrongReturnOriginBug: true
    },
    {
-     args: ["PASS", "http://sub1.xn--exmple-cua.test/foobar"],
+     args: ["PASS", "http://sub1.xn--exaple-kqf.test/foobar"],
      source: "idnKidNoWhitelist",
-     returnOrigin: "http://sub1.exämple.test",
+     returnOrigin: "http://sub1.exaмple.test",
 
      hasWrongReturnOriginBug: true
    },
diff --git a/netwerk/test/unit/test_idnservice.js b/netwerk/test/unit/test_idnservice.js
index 40e48473237..a0f090bdc9f 100644
--- a/netwerk/test/unit/test_idnservice.js
+++ b/netwerk/test/unit/test_idnservice.js
@@ -28,6 +28,11 @@ function run_test() {
                       .getService(Components.interfaces.nsIPrefBranch);
   pbi.setBoolPref("network.IDN.whitelist.es", true);
 
+  // After bug 722299, set network.IDN.restriction_profile to "ASCII" in
+  // order not to change the behaviour of non-whitelisted TLDs
+  var oldProfile = pbi.getCharPref("network.IDN.restriction_profile", "moderate");
+  pbi.setCharPref("network.IDN.restriction_profile", "ASCII");
+
   // check convertToDisplayIDN against the whitelist
   var isASCII = {};
   do_check_eq(idnService.convertToDisplayIDN("b\u00FCcher.es", isASCII), "b\u00FCcher.es");
@@ -45,4 +50,7 @@ function run_test() {
   do_check_eq(isASCII.value, false);
   do_check_eq(idnService.convertToDisplayIDN("test.xn--k-dha", isASCII), "test.\u00FCk");
   do_check_eq(isASCII.value, false);
+
+  // reset pref to default
+  pbi.setCharPref("network.IDN.restriction_profile", oldProfile);
 }

From dbc02fca09320db39daebebbbe30f967e6c514ff Mon Sep 17 00:00:00 2001
From: Simon Montagu 
Date: Fri, 1 Mar 2013 03:56:45 -0800
Subject: [PATCH 102/140] Tests for bug 722299, r=honza.b

---
 netwerk/test/unit/test_bug722299.js | 329 ++++++++++++++++++++++++++++
 netwerk/test/unit/xpcshell.ini      |   1 +
 2 files changed, 330 insertions(+)
 create mode 100644 netwerk/test/unit/test_bug722299.js

diff --git a/netwerk/test/unit/test_bug722299.js b/netwerk/test/unit/test_bug722299.js
new file mode 100644
index 00000000000..b8672d21444
--- /dev/null
+++ b/netwerk/test/unit/test_bug722299.js
@@ -0,0 +1,329 @@
+// Test algorithm for unicode display of IDNA URL (bug 722299)
+const testcases = [
+    //  Original             Punycode or         Expected UTF-8 by profile
+    //    URL              normalized form      ASCII-Only, High, Moderate
+    //
+    // Latin script
+    ["cuillère", "xn--cuillre-6xa",                  false, true,  true],
+
+    // repeated non-spacing marks
+    ["gruz̀̀ere",  "xn--gruzere-ogea",                 false, false, false],
+
+    // non-XID character
+    ["I♥NY",     "xn--iny-zx5a",                     false, false, false],
+
+    // Cyrillic alone
+    ["толсто́й",  "xn--lsa83dealbred",                false, true,  true],
+
+    // Mixed script Cyrillic/Latin
+    ["толсто́й-in-Russian",
+                 "xn---in-russian-1jg071b0a8bb4cpd", false, false, false],
+
+    // Mixed script Latin/Cyrillic
+    ["war-and-миръ", "xn--war-and--b9g3b7b3h",       false, false, false],
+
+    // Cherokee (Restricted script)
+    ["ᏣᎳᎩ",     "xn--f9dt7l",                        false, false, false],
+
+    // Yi (Aspirational script)
+    ["ꆈꌠꁱꂷ", "xn--4o7a6e1x64c",                  false, true,  true],
+
+    // Greek alone
+    ["πλάτων",   "xn--hxa3ahjw4a",                   false, true,  true],
+
+    // Mixed script Greek/Latin
+    ["πλάτωνicrelationship",
+                 "xn--icrelationship-96j4t9a3cwe2e", false, false, false],
+
+    // Mixed script Latin/Greek
+    ["spaceὈδύσσεια", "xn--space-h9dui0b0ga2j1562b", false, false, false],
+
+    // Devanagari alone
+    ["मराठी",    "xn--d2b1ag0dl",                    false, true,  true],
+
+    // Devanagari with Armenian
+    ["मराठीՀայաստան",
+                 "xn--y9aaa1d0ai1cq964f8dwa2o1a",    false, false, false],
+
+    // Devanagari with common
+    ["मराठी123", "xn--123-mhh3em2hra",               false, true,  true],
+
+    // Common with Devanagari
+    ["123मराठी", "xn--123-phh3em2hra",               false, true,  true],
+
+    // Latin with Han
+    ["chairman毛",
+                 "xn--chairman-k65r",                false, true,  true],
+
+    // Han with Latin
+    ["山葵sauce", "xn--sauce-6j9ii40v",              false, true,  true],
+
+    // Latin with Han, Hiragana and Katakana
+    ["van語ではドイ", "xn--van-ub4bpb6w0in486d",     false, true,  true],
+
+    // Latin with Han, Katakana and Hiragana
+    ["van語ドイでは", "xn--van-ub4bpb4w0ip486d",     false, true,  true],
+
+    // Latin with Hiragana, Han and Katakana
+    ["vanでは語ドイ", "xn--van-ub4bpb6w0ip486d",     false, true,  true],
+
+    // Latin with Hiragana, Katakana and Han
+    ["vanではドイ語", "xn--van-ub4bpb6w0ir486d",     false, true,  true],
+
+    // Latin with Katakana, Han and Hiragana
+    ["vanドイ語では", "xn--van-ub4bpb4w0ir486d",     false, true,  true],
+
+    // Latin with Katakana, Hiragana and Han
+    ["vanドイでは語", "xn--van-ub4bpb4w0it486d",     false, true,  true],
+
+    // Han with Latin, Hiragana and Katakana
+    ["語vanではドイ", "xn--van-ub4bpb6w0ik486d",     false, true,  true],
+
+    // Han with Latin, Katakana and Hiragana
+    ["語vanドイでは", "xn--van-ub4bpb4w0im486d",     false, true,  true],
+
+    // Han with Hiragana, Latin and Katakana
+    ["語ではvanドイ", "xn--van-rb4bpb9w0ik486d",     false, true,  true],
+
+    // Han with Hiragana, Katakana and Latin
+    ["語ではドイvan", "xn--van-rb4bpb6w0in486d",     false, true,  true],
+
+    // Han with Katakana, Latin and Hiragana
+    ["語ドイvanでは", "xn--van-ub4bpb1w0ip486d",     false, true,  true],
+
+    // Han with Katakana, Hiragana and Latin
+    ["語ドイではvan", "xn--van-rb4bpb4w0ip486d",     false, true,  true],
+
+    // Hiragana with Latin, Han and Katakana
+    ["イツvan語ではド", "xn--van-ub4bpb1wvhsbx330n", false, true,  true],
+
+    // Hiragana with Latin, Katakana and Han
+    ["ではvanドイ語", "xn--van-rb4bpb9w0ir486d",     false, true,  true],
+
+    // Hiragana with Han, Latin and Katakana
+    ["では語vanドイ", "xn--van-rb4bpb9w0im486d",     false, true,  true],
+
+    // Hiragana with Han, Katakana and Latin
+    ["では語ドイvan", "xn--van-rb4bpb6w0ip486d",     false, true,  true],
+
+    // Hiragana with Katakana, Latin and Han
+    ["ではドイvan語", "xn--van-rb4bpb6w0iu486d",     false, true,  true],
+
+    // Hiragana with Katakana, Han and Latin
+    ["ではドイ語van", "xn--van-rb4bpb6w0ir486d",     false, true,  true],
+
+    // Katakana with Latin, Han and Hiragana
+    ["ドイvan語では", "xn--van-ub4bpb1w0iu486d",     false, true,  true],
+
+    // Katakana with Latin, Hiragana and Han
+    ["ドイvanでは語", "xn--van-ub4bpb1w0iw486d",     false, true,  true],
+
+    // Katakana with Han, Latin and Hiragana
+    ["ドイ語vanでは", "xn--van-ub4bpb1w0ir486d",     false, true,  true],
+
+    // Katakana with Han, Hiragana and Latin
+    ["ドイ語ではvan", "xn--van-rb4bpb4w0ir486d",     false, true,  true],
+
+    // Katakana with Hiragana, Latin and Han
+    ["ドイではvan語", "xn--van-rb4bpb4w0iw486d",     false, true,  true],
+
+    // Katakana with Hiragana, Han and Latin
+    ["ドイでは語van", "xn--van-rb4bpb4w0it486d",     false, true,  true],
+
+    // Han with common
+    ["中国123",   "xn--123-u68dy61b",                false, true,  true],
+
+    // common with Han
+    ["123中国",   "xn--123-x68dy61b",                false, true,  true],
+
+    // Characters that normalize to permitted characters
+    //  (also tests Plane 1 supplementary characters)
+    ["super𝟖",   "super8",                           true,  true,  true],
+
+    // Han from Plane 2
+    ["𠀀𠀁𠀂", "xn--j50icd",                         false, true,  true],
+
+    // Han from Plane 2 with js (UTF-16) escapes
+    ["\uD840\uDC00\uD840\uDC01\uD840\uDC02",
+            "xn--j50icd",                            false, true,  true],
+
+    // Same with a lone high surrogate at the end
+    ["\uD840\uDC00\uD840\uDC01\uD840", "",           false, false, false],
+
+    // Latin text and Bengali digits
+    ["super৪",   "xn--super-k2l",                    false, false, true],
+
+    // Bengali digits and Latin text
+    ["৫ab",   "xn--ab-x5f",                          false, false, true],
+
+    // Bengali text and Latin digits
+    ["অঙ্কুর8",    "xn--8-70d2cp0j6dtd",               false, true,  true],
+
+    // Latin digits and Bengali text
+    ["5াব",        "xn--5-h3d7c",                    false, true,  true],
+
+    // Mixed numbering systems
+    ["٢٠۰٠",     "xn--8hbae38c",                     false, false, false],
+
+    // Traditional Chinese
+    ["萬城",     "xn--uis754h",                      false, true,  true],
+
+    // Simplified Chinese
+    ["万城",     "xn--chq31v",                       false, true,  true],
+
+    // Simplified-only and Traditional-only Chinese in the same label
+    ["万萬城",   "xn--chq31vsl1b",                   false, false, false],
+
+    // Traditional-only and Simplified-only Chinese in the same label
+    ["萬万城",   "xn--chq31vrl1b",                   false, false, false],
+
+    // Han and Latin and Bopomofo
+    ["注音符号bopomofoㄅㄆㄇㄈ",
+                 "xn--bopomofo-hj5gkalm1637i876cuw0brk5f",
+                                                     false, true,  true],
+
+    // Han, bopomofo, Latin
+    ["注音符号ㄅㄆㄇㄈbopomofo",
+                 "xn--bopomofo-8i5gkalm9637i876cuw0brk5f",
+                                                     false, true,  true],
+
+    // Latin, Han, Bopomofo
+    ["bopomofo注音符号ㄅㄆㄇㄈ",
+                 "xn--bopomofo-hj5gkalm9637i876cuw0brk5f",
+                                                     false, true,  true],
+
+    // Latin, Bopomofo, Han
+    ["bopomofoㄅㄆㄇㄈ注音符号",
+                 "xn--bopomofo-hj5gkalm3737i876cuw0brk5f",
+                                                     false, true,  true],
+
+    // Bopomofo, Han, Latin
+    ["ㄅㄆㄇㄈ注音符号bopomofo",
+                 "xn--bopomofo-8i5gkalm3737i876cuw0brk5f",
+                                                     false, true,  true],
+
+    // Bopomofo, Latin, Han
+    ["ㄅㄆㄇㄈbopomofo注音符号",
+                 "xn--bopomofo-8i5gkalm1837i876cuw0brk5f",
+                                                     false, true,  true],
+
+    // Han, bopomofo and katakana
+    ["注音符号ㄅㄆㄇㄈボポモフォ",
+                 "xn--jckteuaez1shij0450gylvccz9asi4e",
+                                                     false, false, false],
+
+    // Han, katakana, bopomofo
+    ["注音符号ボポモフォㄅㄆㄇㄈ",
+                 "xn--jckteuaez6shij5350gylvccz9asi4e",
+                                                     false, false, false],
+
+    // bopomofo, han, katakana
+    ["ㄅㄆㄇㄈ注音符号ボポモフォ",
+                 "xn--jckteuaez1shij4450gylvccz9asi4e",
+                                                     false, false, false],
+
+    // bopomofo, katakana, han
+    ["ㄅㄆㄇㄈボポモフォ注音符号",
+                 "xn--jckteuaez1shij9450gylvccz9asi4e",
+                                                     false, false, false],
+
+    // katakana, Han, bopomofo
+    ["ボポモフォ注音符号ㄅㄆㄇㄈ",
+                 "xn--jckteuaez6shij0450gylvccz9asi4e",
+                                                     false, false, false],
+
+    // katakana, bopomofo, Han
+    ["ボポモフォㄅㄆㄇㄈ注音符号",
+                 "xn--jckteuaez6shij4450gylvccz9asi4e",
+                                                     false, false, false],
+
+    // Han, Hangul and Latin
+    ["韓한글hangul",
+                 "xn--hangul-2m5ti09k79ze",          false, true,  true],
+
+    // Han, Latin and Hangul
+    ["韓hangul한글",
+                 "xn--hangul-2m5to09k79ze",          false, true,  true],
+
+    // Hangul, Han and Latin
+    ["한글韓hangul",
+                 "xn--hangul-2m5th09k79ze",          false, true,  true],
+
+    // Hangul, Latin and Han
+    ["한글hangul韓",
+                 "xn--hangul-8m5t898k79ze",          false, true,  true],
+
+    // Latin, Han and Hangul
+    ["hangul韓한글",
+                 "xn--hangul-8m5ti09k79ze",          false, true,  true],
+
+    // Latin, Hangul and Han
+    ["hangul한글韓",
+                 "xn--hangul-8m5th09k79ze",          false, true,  true],
+
+    // Hangul and katakana
+    ["한글ハングル",
+                 "xn--qck1c2d4a9266lkmzb",           false, false, false],
+
+    // Katakana and Hangul
+    ["ハングル한글",
+                 "xn--qck1c2d4a2366lkmzb",           false, false, false]
+];
+
+
+const profiles = ["ASCII", "high", "moderate"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+function run_test() {
+    var pbi = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+    var oldProfile = pbi.getCharPref("network.IDN.restriction_profile", "moderate");
+    var oldWhiteListCom;
+    try {
+        oldWhitelistCom = pbi.getBoolPref("network.IDN.whitelist.com");
+    } catch(e) {
+        oldWhitelistCom = false;
+    }
+    var idnService = Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService);
+
+    for (var i = 0; i < profiles.length; ++i) {
+        pbi.setCharPref("network.IDN.restriction_profile", profiles[i]);
+        pbi.setBoolPref("network.IDN.whitelist.com", false);
+
+        dump("testing " + profiles[i] + " profile");
+
+        for (var j = 0; j < testcases.length; ++j) {
+            var test = testcases[j];
+            var URL = test[0] + ".com";
+            var punycodeURL = test[1] + ".com";
+            var expectedUnicode = test[2 + i];
+            var isASCII = {};
+
+	    var result;
+	    try {
+		result = idnService.convertToDisplayIDN(URL, isASCII);
+	    } catch(e) {
+		result = ".com";
+	    }
+            if (punycodeURL.substr(0, 4) == "xn--") {
+                // test convertToDisplayIDN with a Unicode URL and with a
+                //  Punycode URL if we have one
+                do_check_eq(escape(result),
+                            expectedUnicode ? escape(URL) : escape(punycodeURL));
+
+                result = idnService.convertToDisplayIDN(punycodeURL, isASCII);
+                do_check_eq(escape(result),
+                            expectedUnicode ? escape(URL) : escape(punycodeURL));
+            } else {
+                // The "punycode" URL isn't punycode. This happens in testcases
+                // where the Unicode URL has become normalized to an ASCII URL,
+                // so, even though expectedUnicode is true, the expected result
+                // is equal to punycodeURL
+                do_check_eq(escape(result), escape(punycodeURL));
+            }
+        }
+    }
+    pbi.setBoolPref("network.IDN.whitelist.com", oldWhitelistCom);
+    pbi.setCharPref("network.IDN.restriction_profile", oldProfile);
+}
diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini
index 72e15b2bdc1..97ba768d319 100644
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -91,6 +91,7 @@ fail-if = os == "android"
 [test_bug667818.js]
 [test_bug669001.js]
 [test_bug712914_secinfo_validation.js]
+[test_bug722299.js]
 [test_bug770243.js]
 [test_doomentry.js]
 [test_cacheflags.js]

From a57f8f8b90f13af2f5354e21c7df4c5f31864da2 Mon Sep 17 00:00:00 2001
From: Gene Lian 
Date: Fri, 1 Mar 2013 16:38:47 +0800
Subject: [PATCH 103/140] Bug 844429 - B2G SMS & MMS: move SMS codes into
 dom/mobilemessage to make it generic for MMS. r=vicamo

--HG--
rename : dom/sms/interfaces/nsIDOMMozSmsEvent.idl => dom/mobilemessage/interfaces/nsIDOMMozSmsEvent.idl
rename : dom/sms/interfaces/nsIDOMMozSmsMessage.idl => dom/mobilemessage/interfaces/nsIDOMMozSmsMessage.idl
rename : dom/sms/interfaces/nsIDOMNavigatorSms.idl => dom/mobilemessage/interfaces/nsIDOMNavigatorSms.idl
rename : dom/sms/interfaces/nsIDOMSmsCursor.idl => dom/mobilemessage/interfaces/nsIDOMSmsCursor.idl
rename : dom/sms/interfaces/nsIDOMSmsFilter.idl => dom/mobilemessage/interfaces/nsIDOMSmsFilter.idl
rename : dom/sms/interfaces/nsIDOMSmsManager.idl => dom/mobilemessage/interfaces/nsIDOMSmsManager.idl
rename : dom/sms/interfaces/nsIDOMSmsRequest.idl => dom/mobilemessage/interfaces/nsIDOMSmsRequest.idl
rename : dom/sms/interfaces/nsIDOMSmsSegmentInfo.idl => dom/mobilemessage/interfaces/nsIDOMSmsSegmentInfo.idl
rename : dom/sms/interfaces/nsISmsRequest.idl => dom/mobilemessage/interfaces/nsISmsRequest.idl
rename : dom/sms/interfaces/nsISmsService.idl => dom/mobilemessage/interfaces/nsISmsService.idl
rename : dom/sms/src/Constants.cpp => dom/mobilemessage/src/Constants.cpp
rename : dom/sms/src/Constants.h => dom/mobilemessage/src/Constants.h
rename : dom/sms/src/SmsCursor.cpp => dom/mobilemessage/src/SmsCursor.cpp
rename : dom/sms/src/SmsCursor.h => dom/mobilemessage/src/SmsCursor.h
rename : dom/sms/src/SmsFilter.cpp => dom/mobilemessage/src/SmsFilter.cpp
rename : dom/sms/src/SmsFilter.h => dom/mobilemessage/src/SmsFilter.h
rename : dom/sms/src/SmsManager.cpp => dom/mobilemessage/src/SmsManager.cpp
rename : dom/sms/src/SmsManager.h => dom/mobilemessage/src/SmsManager.h
rename : dom/sms/src/SmsMessage.cpp => dom/mobilemessage/src/SmsMessage.cpp
rename : dom/sms/src/SmsMessage.h => dom/mobilemessage/src/SmsMessage.h
rename : dom/sms/src/SmsRequest.cpp => dom/mobilemessage/src/SmsRequest.cpp
rename : dom/sms/src/SmsRequest.h => dom/mobilemessage/src/SmsRequest.h
rename : dom/sms/src/SmsSegmentInfo.cpp => dom/mobilemessage/src/SmsSegmentInfo.cpp
rename : dom/sms/src/SmsSegmentInfo.h => dom/mobilemessage/src/SmsSegmentInfo.h
rename : dom/sms/src/SmsServicesFactory.cpp => dom/mobilemessage/src/SmsServicesFactory.cpp
rename : dom/sms/src/SmsServicesFactory.h => dom/mobilemessage/src/SmsServicesFactory.h
rename : dom/sms/src/Types.h => dom/mobilemessage/src/Types.h
rename : dom/sms/src/android/SmsService.cpp => dom/mobilemessage/src/android/SmsService.cpp
rename : dom/sms/src/android/SmsService.h => dom/mobilemessage/src/android/SmsService.h
rename : dom/sms/src/fallback/SmsService.cpp => dom/mobilemessage/src/fallback/SmsService.cpp
rename : dom/sms/src/fallback/SmsService.h => dom/mobilemessage/src/fallback/SmsService.h
rename : dom/sms/src/ipc/PSms.ipdl => dom/mobilemessage/src/ipc/PSms.ipdl
rename : dom/sms/src/ipc/PSmsRequest.ipdl => dom/mobilemessage/src/ipc/PSmsRequest.ipdl
rename : dom/sms/src/ipc/SmsChild.cpp => dom/mobilemessage/src/ipc/SmsChild.cpp
rename : dom/sms/src/ipc/SmsChild.h => dom/mobilemessage/src/ipc/SmsChild.h
rename : dom/sms/src/ipc/SmsIPCService.cpp => dom/mobilemessage/src/ipc/SmsIPCService.cpp
rename : dom/sms/src/ipc/SmsIPCService.h => dom/mobilemessage/src/ipc/SmsIPCService.h
rename : dom/sms/src/ipc/SmsParent.cpp => dom/mobilemessage/src/ipc/SmsParent.cpp
rename : dom/sms/src/ipc/SmsParent.h => dom/mobilemessage/src/ipc/SmsParent.h
rename : dom/sms/src/ipc/SmsTypes.ipdlh => dom/mobilemessage/src/ipc/SmsTypes.ipdlh
rename : dom/sms/src/ipc/ipdl.mk => dom/mobilemessage/src/ipc/ipdl.mk
rename : dom/sms/src/ril/SmsService.cpp => dom/mobilemessage/src/ril/SmsService.cpp
rename : dom/sms/src/ril/SmsService.h => dom/mobilemessage/src/ril/SmsService.h
rename : dom/sms/tests/Makefile.in => dom/mobilemessage/tests/Makefile.in
rename : dom/sms/tests/marionette/manifest.ini => dom/mobilemessage/tests/marionette/manifest.ini
rename : dom/sms/tests/marionette/test_between_emulators.py => dom/mobilemessage/tests/marionette/test_between_emulators.py
rename : dom/sms/tests/marionette/test_bug814761.js => dom/mobilemessage/tests/marionette/test_bug814761.js
rename : dom/sms/tests/marionette/test_emulator_loopback.js => dom/mobilemessage/tests/marionette/test_emulator_loopback.js
rename : dom/sms/tests/marionette/test_filter_date.js => dom/mobilemessage/tests/marionette/test_filter_date.js
rename : dom/sms/tests/marionette/test_filter_date_notfound.js => dom/mobilemessage/tests/marionette/test_filter_date_notfound.js
rename : dom/sms/tests/marionette/test_filter_mixed.js => dom/mobilemessage/tests/marionette/test_filter_mixed.js
rename : dom/sms/tests/marionette/test_filter_number_multiple.js => dom/mobilemessage/tests/marionette/test_filter_number_multiple.js
rename : dom/sms/tests/marionette/test_filter_number_single.js => dom/mobilemessage/tests/marionette/test_filter_number_single.js
rename : dom/sms/tests/marionette/test_filter_read.js => dom/mobilemessage/tests/marionette/test_filter_read.js
rename : dom/sms/tests/marionette/test_filter_received.js => dom/mobilemessage/tests/marionette/test_filter_received.js
rename : dom/sms/tests/marionette/test_filter_sent.js => dom/mobilemessage/tests/marionette/test_filter_sent.js
rename : dom/sms/tests/marionette/test_filter_unread.js => dom/mobilemessage/tests/marionette/test_filter_unread.js
rename : dom/sms/tests/marionette/test_getmessage.js => dom/mobilemessage/tests/marionette/test_getmessage.js
rename : dom/sms/tests/marionette/test_getmessage_notfound.js => dom/mobilemessage/tests/marionette/test_getmessage_notfound.js
rename : dom/sms/tests/marionette/test_getmessages.js => dom/mobilemessage/tests/marionette/test_getmessages.js
rename : dom/sms/tests/marionette/test_incoming.js => dom/mobilemessage/tests/marionette/test_incoming.js
rename : dom/sms/tests/marionette/test_incoming_delete.js => dom/mobilemessage/tests/marionette/test_incoming_delete.js
rename : dom/sms/tests/marionette/test_incoming_max_segments.js => dom/mobilemessage/tests/marionette/test_incoming_max_segments.js
rename : dom/sms/tests/marionette/test_incoming_multipart.js => dom/mobilemessage/tests/marionette/test_incoming_multipart.js
rename : dom/sms/tests/marionette/test_mark_msg_read.js => dom/mobilemessage/tests/marionette/test_mark_msg_read.js
rename : dom/sms/tests/marionette/test_mark_msg_read_error.js => dom/mobilemessage/tests/marionette/test_mark_msg_read_error.js
rename : dom/sms/tests/marionette/test_message_classes.js => dom/mobilemessage/tests/marionette/test_message_classes.js
rename : dom/sms/tests/marionette/test_outgoing.js => dom/mobilemessage/tests/marionette/test_outgoing.js
rename : dom/sms/tests/marionette/test_outgoing_delete.js => dom/mobilemessage/tests/marionette/test_outgoing_delete.js
rename : dom/sms/tests/marionette/test_outgoing_max_segments.js => dom/mobilemessage/tests/marionette/test_outgoing_max_segments.js
rename : dom/sms/tests/marionette/test_segment_info.js => dom/mobilemessage/tests/marionette/test_segment_info.js
rename : dom/sms/tests/marionette/test_strict_7bit_encoding.js => dom/mobilemessage/tests/marionette/test_strict_7bit_encoding.js
rename : dom/sms/tests/moz.build => dom/mobilemessage/tests/moz.build
rename : dom/sms/tests/test_sms_basics.html => dom/mobilemessage/tests/test_sms_basics.html
rename : dom/sms/tests/test_smsdatabaseservice.xul => dom/mobilemessage/tests/test_smsdatabaseservice.xul
rename : dom/sms/tests/test_smsfilter.html => dom/mobilemessage/tests/test_smsfilter.html
rename : dom/sms/tests/test_smsservice_createsmsmessage.js => dom/mobilemessage/tests/test_smsservice_createsmsmessage.js
rename : dom/sms/tests/xpcshell.ini => dom/mobilemessage/tests/xpcshell.ini
---
 b2g/installer/package-manifest.in             |  1 -
 b2g/installer/removed-files.in                |  1 +
 browser/installer/package-manifest.in         |  1 -
 browser/installer/removed-files.in            |  1 +
 dom/base/Navigator.cpp                        |  3 +-
 dom/base/Navigator.h                          |  4 +-
 dom/base/nsDOMClassInfo.cpp                   |  4 +-
 dom/dom-config.mk                             |  1 -
 dom/ipc/ContentChild.cpp                      |  4 +-
 dom/ipc/ContentParent.cpp                     |  2 +-
 dom/ipc/Makefile.in                           |  2 +-
 dom/mobilemessage/Makefile.in                 |  4 +
 dom/mobilemessage/interfaces/Makefile.in      | 10 ++
 .../interfaces/nsIDOMMozSmsEvent.idl          |  0
 .../interfaces/nsIDOMMozSmsMessage.idl        |  0
 .../interfaces/nsIDOMNavigatorSms.idl         |  0
 .../interfaces/nsIDOMSmsCursor.idl            |  0
 .../interfaces/nsIDOMSmsFilter.idl            |  0
 .../interfaces/nsIDOMSmsManager.idl           |  0
 .../interfaces/nsIDOMSmsRequest.idl           |  0
 .../interfaces/nsIDOMSmsSegmentInfo.idl       |  0
 .../interfaces/nsISmsRequest.idl              |  0
 .../interfaces/nsISmsService.idl              |  0
 dom/mobilemessage/moz.build                   |  2 +-
 dom/{sms => mobilemessage}/src/Constants.cpp  |  4 +-
 dom/{sms => mobilemessage}/src/Constants.h    | 10 +-
 dom/mobilemessage/src/Makefile.in             | 31 +++++-
 dom/{sms => mobilemessage}/src/SmsCursor.cpp  |  4 +-
 dom/{sms => mobilemessage}/src/SmsCursor.h    |  8 +-
 dom/{sms => mobilemessage}/src/SmsFilter.cpp  |  6 +-
 dom/{sms => mobilemessage}/src/SmsFilter.h    | 18 ++--
 dom/{sms => mobilemessage}/src/SmsManager.cpp |  6 +-
 dom/{sms => mobilemessage}/src/SmsManager.h   |  8 +-
 dom/{sms => mobilemessage}/src/SmsMessage.cpp |  6 +-
 dom/{sms => mobilemessage}/src/SmsMessage.h   | 24 +++--
 dom/{sms => mobilemessage}/src/SmsRequest.cpp |  6 +-
 dom/{sms => mobilemessage}/src/SmsRequest.h   | 30 +++---
 .../src/SmsSegmentInfo.cpp                    |  6 +-
 .../src/SmsSegmentInfo.h                      | 14 ++-
 .../src/SmsServicesFactory.cpp                |  8 +-
 .../src/SmsServicesFactory.h                  | 10 +-
 dom/{sms => mobilemessage}/src/Types.h        | 42 ++++-----
 .../android/MobileMessageDatabaseService.cpp  |  2 -
 .../src/android/SmsService.cpp                |  6 +-
 .../src/android/SmsService.h                  | 10 +-
 .../src/fallback/SmsService.cpp               |  6 +-
 .../src/fallback/SmsService.h                 | 10 +-
 dom/{sms => mobilemessage}/src/ipc/PSms.ipdl  |  4 +-
 .../src/ipc/PSmsRequest.ipdl                  |  6 +-
 .../src/ipc/SmsChild.cpp                      |  7 +-
 dom/{sms => mobilemessage}/src/ipc/SmsChild.h | 16 ++--
 .../src/ipc/SmsIPCService.cpp                 |  8 +-
 .../src/ipc/SmsIPCService.h                   | 10 +-
 .../src/ipc/SmsParent.cpp                     |  4 +-
 .../src/ipc/SmsParent.h                       | 22 ++---
 .../src/ipc/SmsTypes.ipdlh                    |  4 +-
 dom/{sms => mobilemessage}/src/ipc/ipdl.mk    |  0
 .../src/ril/SmsService.cpp                    |  6 +-
 .../src/ril/SmsService.h                      | 10 +-
 dom/{sms => mobilemessage}/tests/Makefile.in  |  0
 .../tests/marionette/manifest.ini             |  0
 .../marionette/test_between_emulators.py      |  0
 .../tests/marionette/test_bug814761.js        |  0
 .../marionette/test_emulator_loopback.js      |  0
 .../tests/marionette/test_filter_date.js      |  0
 .../marionette/test_filter_date_notfound.js   |  0
 .../tests/marionette/test_filter_mixed.js     |  0
 .../marionette/test_filter_number_multiple.js |  0
 .../marionette/test_filter_number_single.js   |  0
 .../tests/marionette/test_filter_read.js      |  0
 .../tests/marionette/test_filter_received.js  |  0
 .../tests/marionette/test_filter_sent.js      |  0
 .../tests/marionette/test_filter_unread.js    |  0
 .../tests/marionette/test_getmessage.js       |  0
 .../marionette/test_getmessage_notfound.js    |  0
 .../tests/marionette/test_getmessages.js      |  0
 .../tests/marionette/test_incoming.js         |  0
 .../tests/marionette/test_incoming_delete.js  |  0
 .../marionette/test_incoming_max_segments.js  |  0
 .../marionette/test_incoming_multipart.js     |  0
 .../tests/marionette/test_mark_msg_read.js    |  0
 .../marionette/test_mark_msg_read_error.js    |  0
 .../tests/marionette/test_message_classes.js  |  0
 .../tests/marionette/test_outgoing.js         |  0
 .../tests/marionette/test_outgoing_delete.js  |  0
 .../marionette/test_outgoing_max_segments.js  |  0
 .../tests/marionette/test_segment_info.js     |  0
 .../marionette/test_strict_7bit_encoding.js   |  0
 .../tests}/moz.build                          |  0
 .../tests/test_sms_basics.html                |  0
 .../tests/test_smsdatabaseservice.xul         |  0
 .../tests/test_smsfilter.html                 |  0
 .../tests/test_smsservice_createsmsmessage.js |  0
 dom/{sms => mobilemessage}/tests/xpcshell.ini |  0
 dom/moz.build                                 |  1 -
 dom/sms/Makefile.in                           | 18 ----
 dom/sms/interfaces/Makefile.in                | 35 -------
 dom/sms/moz.build                             |  7 --
 dom/sms/src/Makefile.in                       | 94 -------------------
 dom/sms/src/moz.build                         |  5 -
 dom/sms/tests/moz.build                       |  5 -
 dom/system/gonk/RadioInterfaceLayer.js        | 18 ++--
 embedding/android/GeckoSmsManager.java        |  8 +-
 ipc/ipdl/Makefile.in                          |  2 +-
 layout/build/Makefile.in                      |  1 -
 layout/build/nsLayoutModule.cpp               |  4 +-
 mobile/android/base/GeckoSmsManager.java      |  8 +-
 mobile/android/installer/package-manifest.in  |  1 -
 mobile/android/installer/removed-files.in     |  1 +
 mobile/xul/installer/package-manifest.in      |  1 -
 mobile/xul/installer/removed-files.in         |  1 +
 .../client/marionette/tests/unit-tests.ini    |  2 +-
 testing/mochitest/android.json                |  2 +-
 testing/mochitest/b2g.json                    |  2 +-
 testing/xpcshell/xpcshell.ini                 |  2 +-
 testing/xpcshell/xpcshell_b2g.ini             |  2 +-
 widget/android/AndroidBridge.cpp              |  6 +-
 widget/android/AndroidBridge.h                |  8 +-
 widget/android/AndroidJNI.cpp                 | 13 +--
 119 files changed, 254 insertions(+), 405 deletions(-)
 rename dom/{sms => mobilemessage}/interfaces/nsIDOMMozSmsEvent.idl (100%)
 rename dom/{sms => mobilemessage}/interfaces/nsIDOMMozSmsMessage.idl (100%)
 rename dom/{sms => mobilemessage}/interfaces/nsIDOMNavigatorSms.idl (100%)
 rename dom/{sms => mobilemessage}/interfaces/nsIDOMSmsCursor.idl (100%)
 rename dom/{sms => mobilemessage}/interfaces/nsIDOMSmsFilter.idl (100%)
 rename dom/{sms => mobilemessage}/interfaces/nsIDOMSmsManager.idl (100%)
 rename dom/{sms => mobilemessage}/interfaces/nsIDOMSmsRequest.idl (100%)
 rename dom/{sms => mobilemessage}/interfaces/nsIDOMSmsSegmentInfo.idl (100%)
 rename dom/{sms => mobilemessage}/interfaces/nsISmsRequest.idl (100%)
 rename dom/{sms => mobilemessage}/interfaces/nsISmsService.idl (100%)
 rename dom/{sms => mobilemessage}/src/Constants.cpp (93%)
 rename dom/{sms => mobilemessage}/src/Constants.h (88%)
 rename dom/{sms => mobilemessage}/src/SmsCursor.cpp (96%)
 rename dom/{sms => mobilemessage}/src/SmsCursor.h (88%)
 rename dom/{sms => mobilemessage}/src/SmsFilter.cpp (98%)
 rename dom/{sms => mobilemessage}/src/SmsFilter.h (65%)
 rename dom/{sms => mobilemessage}/src/SmsManager.cpp (99%)
 rename dom/{sms => mobilemessage}/src/SmsManager.h (90%)
 rename dom/{sms => mobilemessage}/src/SmsMessage.cpp (98%)
 rename dom/{sms => mobilemessage}/src/SmsMessage.h (72%)
 rename dom/{sms => mobilemessage}/src/SmsRequest.cpp (99%)
 rename dom/{sms => mobilemessage}/src/SmsRequest.h (82%)
 rename dom/{sms => mobilemessage}/src/SmsSegmentInfo.cpp (93%)
 rename dom/{sms => mobilemessage}/src/SmsSegmentInfo.h (69%)
 rename dom/{sms => mobilemessage}/src/SmsServicesFactory.cpp (93%)
 rename dom/{sms => mobilemessage}/src/SmsServicesFactory.h (74%)
 rename dom/{sms => mobilemessage}/src/Types.h (55%)
 rename dom/{sms => mobilemessage}/src/android/SmsService.cpp (97%)
 rename dom/{sms => mobilemessage}/src/android/SmsService.h (71%)
 rename dom/{sms => mobilemessage}/src/fallback/SmsService.cpp (96%)
 rename dom/{sms => mobilemessage}/src/fallback/SmsService.h (72%)
 rename dom/{sms => mobilemessage}/src/ipc/PSms.ipdl (97%)
 rename dom/{sms => mobilemessage}/src/ipc/PSmsRequest.ipdl (94%)
 rename dom/{sms => mobilemessage}/src/ipc/SmsChild.cpp (97%)
 rename dom/{sms => mobilemessage}/src/ipc/SmsChild.h (83%)
 rename dom/{sms => mobilemessage}/src/ipc/SmsIPCService.cpp (97%)
 rename dom/{sms => mobilemessage}/src/ipc/SmsIPCService.h (78%)
 rename dom/{sms => mobilemessage}/src/ipc/SmsParent.cpp (99%)
 rename dom/{sms => mobilemessage}/src/ipc/SmsParent.h (86%)
 rename dom/{sms => mobilemessage}/src/ipc/SmsTypes.ipdlh (92%)
 rename dom/{sms => mobilemessage}/src/ipc/ipdl.mk (100%)
 rename dom/{sms => mobilemessage}/src/ril/SmsService.cpp (96%)
 rename dom/{sms => mobilemessage}/src/ril/SmsService.h (73%)
 rename dom/{sms => mobilemessage}/tests/Makefile.in (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/manifest.ini (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_between_emulators.py (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_bug814761.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_emulator_loopback.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_filter_date.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_filter_date_notfound.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_filter_mixed.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_filter_number_multiple.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_filter_number_single.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_filter_read.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_filter_received.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_filter_sent.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_filter_unread.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_getmessage.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_getmessage_notfound.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_getmessages.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_incoming.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_incoming_delete.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_incoming_max_segments.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_incoming_multipart.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_mark_msg_read.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_mark_msg_read_error.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_message_classes.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_outgoing.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_outgoing_delete.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_outgoing_max_segments.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_segment_info.js (100%)
 rename dom/{sms => mobilemessage}/tests/marionette/test_strict_7bit_encoding.js (100%)
 rename dom/{sms/interfaces => mobilemessage/tests}/moz.build (100%)
 rename dom/{sms => mobilemessage}/tests/test_sms_basics.html (100%)
 rename dom/{sms => mobilemessage}/tests/test_smsdatabaseservice.xul (100%)
 rename dom/{sms => mobilemessage}/tests/test_smsfilter.html (100%)
 rename dom/{sms => mobilemessage}/tests/test_smsservice_createsmsmessage.js (100%)
 rename dom/{sms => mobilemessage}/tests/xpcshell.ini (100%)
 delete mode 100644 dom/sms/Makefile.in
 delete mode 100644 dom/sms/interfaces/Makefile.in
 delete mode 100644 dom/sms/moz.build
 delete mode 100644 dom/sms/src/Makefile.in
 delete mode 100644 dom/sms/src/moz.build
 delete mode 100644 dom/sms/tests/moz.build

diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in
index 6fb91d85330..f46339e0ff1 100644
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -203,7 +203,6 @@
 @BINPATH@/components/dom_permissionsettings.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_mobilemessage.xpt
-@BINPATH@/components/dom_sms.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_threads.xpt
diff --git a/b2g/installer/removed-files.in b/b2g/installer/removed-files.in
index 985991832c9..0ea67f2fd5e 100644
--- a/b2g/installer/removed-files.in
+++ b/b2g/installer/removed-files.in
@@ -10,3 +10,4 @@ run-mozilla.sh
 #endif
 defaults/preferences/services-sync.js
 defaults/preferences/healthreport-prefs.js
+components/dom_sms.xpt
\ No newline at end of file
diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
index 9bf0f31966a..ac66aabcf17 100644
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -204,7 +204,6 @@
 @BINPATH@/components/dom_permissionsettings.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_mobilemessage.xpt
-@BINPATH@/components/dom_sms.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_traversal.xpt
diff --git a/browser/installer/removed-files.in b/browser/installer/removed-files.in
index 998e85e5c4d..80fd7403641 100644
--- a/browser/installer/removed-files.in
+++ b/browser/installer/removed-files.in
@@ -79,6 +79,7 @@ components/dom_telephony.xpt
 components/dom_wifi.xpt
 components/dom_system_b2g.xpt
 #endif
+components/dom_sms.xpt
 components/uconvd.dll
 components/WeaveCrypto.js
 components/WeaveCrypto.manifest
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
index ab5f9288112..b547a8de3cf 100644
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -29,7 +29,7 @@
 #include "PowerManager.h"
 #include "nsIDOMWakeLock.h"
 #include "nsIPowerManagerService.h"
-#include "SmsManager.h"
+#include "mozilla/dom/SmsManager.h"
 #include "nsISmsService.h"
 #include "mozilla/Hal.h"
 #include "nsIWebNavigation.h"
@@ -69,7 +69,6 @@
 #include "nsIDOMGlobalPropertyInitializer.h"
 
 using namespace mozilla::dom::power;
-using namespace mozilla::dom::sms;
 
 // This should not be in the namespace.
 DOMCI_DATA(Navigator, mozilla::dom::Navigator)
diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h
index 8714f45114a..582fb5effab 100644
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -65,9 +65,7 @@ namespace battery {
 class BatteryManager;
 } // namespace battery
 
-namespace sms {
 class SmsManager;
-} // namespace sms
 
 namespace network {
 class Connection;
@@ -197,7 +195,7 @@ private:
   nsRefPtr mNotification;
   nsRefPtr mBatteryManager;
   nsRefPtr mPowerManager;
-  nsRefPtr mSmsManager;
+  nsRefPtr mSmsManager;
 #ifdef MOZ_B2G_RIL
   nsCOMPtr mTelephony;
   nsCOMPtr mVoicemail;
diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp
index 018595cbc75..ffdc53a3370 100644
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -6,7 +6,7 @@
 
 #include "mozilla/Util.h"
 // On top because they include basictypes.h:
-#include "SmsFilter.h"
+#include "mozilla/dom/SmsFilter.h"
 
 #ifdef XP_WIN
 #undef GetClassName
@@ -1245,7 +1245,7 @@ static const nsConstructorFuncMapData kConstructorFuncMap[] =
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(_event_interface)
 #include "GeneratedEvents.h"
 #undef MOZ_GENERATED_EVENT_LIST
-  NS_DEFINE_CONSTRUCTOR_FUNC_DATA(MozSmsFilter, sms::SmsFilter::NewSmsFilter)
+  NS_DEFINE_CONSTRUCTOR_FUNC_DATA(MozSmsFilter, SmsFilter::NewSmsFilter)
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(FileReader, FileReaderCtor)
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(ArchiveReader, ArchiveReaderCtor)
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(XSLTProcessor, XSLTProcessorCtor)
diff --git a/dom/dom-config.mk b/dom/dom-config.mk
index 09cc36c0132..f9412477e3d 100644
--- a/dom/dom-config.mk
+++ b/dom/dom-config.mk
@@ -13,7 +13,6 @@ DOM_SRCDIRS = \
   dom/network/src \
   dom/settings \
   dom/phonenumberutils \
-  dom/sms/src \
   dom/contacts \
   dom/permission \
   dom/alarm \
diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp
index 5e46e87a76f..6e4d9fc0b4e 100644
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -88,7 +88,7 @@
 #endif
 
 #include "mozilla/dom/indexedDB/PIndexedDBChild.h"
-#include "mozilla/dom/sms/SmsChild.h"
+#include "mozilla/dom/mobilemessage/SmsChild.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
 #include "mozilla/dom/bluetooth/PBluetoothChild.h"
 #include "mozilla/ipc/InputStreamUtils.h"
@@ -111,7 +111,7 @@ using namespace mozilla::docshell;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::ipc;
-using namespace mozilla::dom::sms;
+using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::hal_sandbox;
 using namespace mozilla::ipc;
diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
index 2e6d311fe99..c7ee2bf03b5 100644
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -125,7 +125,7 @@ using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::dom::power;
-using namespace mozilla::dom::sms;
+using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::hal;
 using namespace mozilla::idl;
 using namespace mozilla::ipc;
diff --git a/dom/ipc/Makefile.in b/dom/ipc/Makefile.in
index 63654c9f5ce..f3501b9e150 100644
--- a/dom/ipc/Makefile.in
+++ b/dom/ipc/Makefile.in
@@ -91,7 +91,7 @@ LOCAL_INCLUDES += \
 	-I$(topsrcdir)/dom/base \
 	-I$(topsrcdir)/toolkit/xre \
 	-I$(topsrcdir)/hal/sandbox \
-	-I$(topsrcdir)/dom/sms/src/ipc \
+	-I$(topsrcdir)/dom/mobilemessage/src/ipc \
 	-I$(topsrcdir)/dom/devicestorage \
 	-I$(topsrcdir)/widget/xpwidgets \
 	-I$(topsrcdir)/dom/bluetooth \
diff --git a/dom/mobilemessage/Makefile.in b/dom/mobilemessage/Makefile.in
index 93542c8c854..cfa730adc58 100644
--- a/dom/mobilemessage/Makefile.in
+++ b/dom/mobilemessage/Makefile.in
@@ -11,4 +11,8 @@ relativesrcdir   = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+ifdef ENABLE_TESTS
+XPCSHELL_TESTS = tests
+endif
+
 include $(topsrcdir)/config/rules.mk
diff --git a/dom/mobilemessage/interfaces/Makefile.in b/dom/mobilemessage/interfaces/Makefile.in
index d925f93bd60..6d860f65620 100644
--- a/dom/mobilemessage/interfaces/Makefile.in
+++ b/dom/mobilemessage/interfaces/Makefile.in
@@ -15,6 +15,16 @@ include $(topsrcdir)/dom/dom-config.mk
 
 XPIDLSRCS = \
   nsIMobileMessageDatabaseService.idl \
+  nsIDOMNavigatorSms.idl \
+  nsIDOMSmsManager.idl \
+  nsIDOMMozSmsMessage.idl \
+  nsIDOMMozSmsEvent.idl \
+  nsIDOMSmsRequest.idl \
+  nsIDOMSmsFilter.idl \
+  nsIDOMSmsCursor.idl \
+  nsIDOMSmsSegmentInfo.idl \
+  nsISmsRequest.idl \
+  nsISmsService.idl \
   $(NULL)
 
 ifdef MOZ_B2G_RIL
diff --git a/dom/sms/interfaces/nsIDOMMozSmsEvent.idl b/dom/mobilemessage/interfaces/nsIDOMMozSmsEvent.idl
similarity index 100%
rename from dom/sms/interfaces/nsIDOMMozSmsEvent.idl
rename to dom/mobilemessage/interfaces/nsIDOMMozSmsEvent.idl
diff --git a/dom/sms/interfaces/nsIDOMMozSmsMessage.idl b/dom/mobilemessage/interfaces/nsIDOMMozSmsMessage.idl
similarity index 100%
rename from dom/sms/interfaces/nsIDOMMozSmsMessage.idl
rename to dom/mobilemessage/interfaces/nsIDOMMozSmsMessage.idl
diff --git a/dom/sms/interfaces/nsIDOMNavigatorSms.idl b/dom/mobilemessage/interfaces/nsIDOMNavigatorSms.idl
similarity index 100%
rename from dom/sms/interfaces/nsIDOMNavigatorSms.idl
rename to dom/mobilemessage/interfaces/nsIDOMNavigatorSms.idl
diff --git a/dom/sms/interfaces/nsIDOMSmsCursor.idl b/dom/mobilemessage/interfaces/nsIDOMSmsCursor.idl
similarity index 100%
rename from dom/sms/interfaces/nsIDOMSmsCursor.idl
rename to dom/mobilemessage/interfaces/nsIDOMSmsCursor.idl
diff --git a/dom/sms/interfaces/nsIDOMSmsFilter.idl b/dom/mobilemessage/interfaces/nsIDOMSmsFilter.idl
similarity index 100%
rename from dom/sms/interfaces/nsIDOMSmsFilter.idl
rename to dom/mobilemessage/interfaces/nsIDOMSmsFilter.idl
diff --git a/dom/sms/interfaces/nsIDOMSmsManager.idl b/dom/mobilemessage/interfaces/nsIDOMSmsManager.idl
similarity index 100%
rename from dom/sms/interfaces/nsIDOMSmsManager.idl
rename to dom/mobilemessage/interfaces/nsIDOMSmsManager.idl
diff --git a/dom/sms/interfaces/nsIDOMSmsRequest.idl b/dom/mobilemessage/interfaces/nsIDOMSmsRequest.idl
similarity index 100%
rename from dom/sms/interfaces/nsIDOMSmsRequest.idl
rename to dom/mobilemessage/interfaces/nsIDOMSmsRequest.idl
diff --git a/dom/sms/interfaces/nsIDOMSmsSegmentInfo.idl b/dom/mobilemessage/interfaces/nsIDOMSmsSegmentInfo.idl
similarity index 100%
rename from dom/sms/interfaces/nsIDOMSmsSegmentInfo.idl
rename to dom/mobilemessage/interfaces/nsIDOMSmsSegmentInfo.idl
diff --git a/dom/sms/interfaces/nsISmsRequest.idl b/dom/mobilemessage/interfaces/nsISmsRequest.idl
similarity index 100%
rename from dom/sms/interfaces/nsISmsRequest.idl
rename to dom/mobilemessage/interfaces/nsISmsRequest.idl
diff --git a/dom/sms/interfaces/nsISmsService.idl b/dom/mobilemessage/interfaces/nsISmsService.idl
similarity index 100%
rename from dom/sms/interfaces/nsISmsService.idl
rename to dom/mobilemessage/interfaces/nsISmsService.idl
diff --git a/dom/mobilemessage/moz.build b/dom/mobilemessage/moz.build
index 286af104855..e410ebf2dde 100644
--- a/dom/mobilemessage/moz.build
+++ b/dom/mobilemessage/moz.build
@@ -4,4 +4,4 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += ['interfaces', 'src']
-
+TEST_DIRS += ['tests']
diff --git a/dom/sms/src/Constants.cpp b/dom/mobilemessage/src/Constants.cpp
similarity index 93%
rename from dom/sms/src/Constants.cpp
rename to dom/mobilemessage/src/Constants.cpp
index 95d85b16db0..ee80b760b86 100644
--- a/dom/sms/src/Constants.cpp
+++ b/dom/mobilemessage/src/Constants.cpp
@@ -5,7 +5,7 @@
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 const char* kSmsReceivedObserverTopic        = "sms-received";
 const char* kSmsSendingObserverTopic         = "sms-sending";
@@ -14,6 +14,6 @@ const char* kSmsFailedObserverTopic          = "sms-failed";
 const char* kSmsDeliverySuccessObserverTopic = "sms-delivery-success";
 const char* kSmsDeliveryErrorObserverTopic   = "sms-delivery-error";
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/Constants.h b/dom/mobilemessage/src/Constants.h
similarity index 88%
rename from dom/sms/src/Constants.h
rename to dom/mobilemessage/src/Constants.h
index 8eea69019ec..72322ada3ae 100644
--- a/dom/sms/src/Constants.h
+++ b/dom/mobilemessage/src/Constants.h
@@ -3,12 +3,12 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_Constants_h
-#define mozilla_dom_sms_Constants_h
+#ifndef mozilla_dom_mobilemessage_Constants_h
+#define mozilla_dom_mobilemessage_Constants_h
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 // Defined in the .cpp.
 extern const char* kSmsReceivedObserverTopic;
@@ -34,8 +34,8 @@ extern const char* kSmsDeliveryErrorObserverTopic;
 #define MESSAGE_CLASS_CLASS_2 NS_LITERAL_STRING("class-2")
 #define MESSAGE_CLASS_CLASS_3 NS_LITERAL_STRING("class-3")
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_sms_Constants_h
+#endif // mozilla_dom_mobilemessage_Constants_h
diff --git a/dom/mobilemessage/src/Makefile.in b/dom/mobilemessage/src/Makefile.in
index 00f10ef546a..cc1f06deb29 100644
--- a/dom/mobilemessage/src/Makefile.in
+++ b/dom/mobilemessage/src/Makefile.in
@@ -29,18 +29,45 @@ endif # !_MSC_VER
 
 include $(topsrcdir)/dom/dom-config.mk
 
-EXPORTS_NAMESPACES = mozilla/dom/mobilemessage
+EXPORTS_NAMESPACES = \
+  mozilla/dom \
+  mozilla/dom/mobilemessage \
+  $(NULL)
+
+EXPORTS_mozilla/dom = \
+  SmsManager.h \
+  SmsMessage.h \
+  SmsRequest.h \
+  SmsSegmentInfo.h \
+  SmsFilter.h \
+  $(NULL)
 
 EXPORTS_mozilla/dom/mobilemessage = \
+  SmsChild.h \
+  SmsParent.h \
+  SmsServicesFactory.h \
+  Constants.h \
+  Types.h \
   $(NULL)
 
 CPPSRCS = \
+  SmsManager.cpp \
+  SmsService.cpp \
+  SmsIPCService.cpp \
+  SmsServicesFactory.cpp \
+  SmsParent.cpp \
+  SmsMessage.cpp \
+  Constants.cpp \
+  SmsChild.cpp \
+  SmsRequest.cpp \
+  SmsFilter.cpp \
+  SmsSegmentInfo.cpp \
+  SmsCursor.cpp \
   $(NULL)
 
 LOCAL_INCLUDES = \
   -I$(topsrcdir)/content/events/src \
   -I$(topsrcdir)/dom/base \
-  -I$(topsrcdir)/dom/sms/src \
   $(NULL)
 
 # Add VPATH to LOCAL_INCLUDES so we are going to include the correct backend
diff --git a/dom/sms/src/SmsCursor.cpp b/dom/mobilemessage/src/SmsCursor.cpp
similarity index 96%
rename from dom/sms/src/SmsCursor.cpp
rename to dom/mobilemessage/src/SmsCursor.cpp
index 3783a8ef37f..8c3d4d0cb2f 100644
--- a/dom/sms/src/SmsCursor.cpp
+++ b/dom/mobilemessage/src/SmsCursor.cpp
@@ -10,11 +10,10 @@
 #include "SmsRequest.h"
 #include "nsIMobileMessageDatabaseService.h"
 
-DOMCI_DATA(MozSmsCursor, mozilla::dom::sms::SmsCursor)
+DOMCI_DATA(MozSmsCursor, mozilla::dom::SmsCursor)
 
 namespace mozilla {
 namespace dom {
-namespace sms {
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SmsCursor)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozSmsCursor)
@@ -96,7 +95,6 @@ SmsCursor::Continue()
   return NS_OK;
 }
 
-} // namespace sms
 } // namespace dom
 } // namespace mozilla
 
diff --git a/dom/sms/src/SmsCursor.h b/dom/mobilemessage/src/SmsCursor.h
similarity index 88%
rename from dom/sms/src/SmsCursor.h
rename to dom/mobilemessage/src/SmsCursor.h
index 371e655581d..cea046c1158 100644
--- a/dom/sms/src/SmsCursor.h
+++ b/dom/mobilemessage/src/SmsCursor.h
@@ -3,8 +3,8 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_SmsCursor_h
-#define mozilla_dom_sms_SmsCursor_h
+#ifndef mozilla_dom_mobilemessage_SmsCursor_h
+#define mozilla_dom_mobilemessage_SmsCursor_h
 
 #include "nsIDOMSmsCursor.h"
 #include "nsCycleCollectionParticipant.h"
@@ -16,7 +16,6 @@ class nsISmsRequest;
 
 namespace mozilla {
 namespace dom {
-namespace sms {
 
 class SmsCursor MOZ_FINAL : public nsIDOMMozSmsCursor
 {
@@ -47,8 +46,7 @@ SmsCursor::SetMessage(nsIDOMMozSmsMessage* aMessage)
   mMessage = aMessage;
 }
 
-} // namespace sms
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_sms_SmsCursor_h
+#endif // mozilla_dom_mobilemessage_SmsCursor_h
diff --git a/dom/sms/src/SmsFilter.cpp b/dom/mobilemessage/src/SmsFilter.cpp
similarity index 98%
rename from dom/sms/src/SmsFilter.cpp
rename to dom/mobilemessage/src/SmsFilter.cpp
index f6f9fd361eb..9d7139bf6d9 100644
--- a/dom/sms/src/SmsFilter.cpp
+++ b/dom/mobilemessage/src/SmsFilter.cpp
@@ -14,11 +14,12 @@
 #include "nsJSUtils.h"
 #include "nsDOMString.h"
 
-DOMCI_DATA(MozSmsFilter, mozilla::dom::sms::SmsFilter)
+using namespace mozilla::dom::mobilemessage;
+
+DOMCI_DATA(MozSmsFilter, mozilla::dom::SmsFilter)
 
 namespace mozilla {
 namespace dom {
-namespace sms {
 
 NS_INTERFACE_MAP_BEGIN(SmsFilter)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozSmsFilter)
@@ -257,6 +258,5 @@ SmsFilter::SetRead(JSContext* aCx, const jsval& aRead)
   return NS_OK;
 }
 
-} // namespace sms
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/SmsFilter.h b/dom/mobilemessage/src/SmsFilter.h
similarity index 65%
rename from dom/sms/src/SmsFilter.h
rename to dom/mobilemessage/src/SmsFilter.h
index 7d93b9afb70..0b13f2855a6 100644
--- a/dom/sms/src/SmsFilter.h
+++ b/dom/mobilemessage/src/SmsFilter.h
@@ -3,17 +3,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_SmsFilter_h
-#define mozilla_dom_sms_SmsFilter_h
+#ifndef mozilla_dom_mobilemessage_SmsFilter_h
+#define mozilla_dom_mobilemessage_SmsFilter_h
 
-#include "mozilla/dom/sms/PSms.h"
+#include "mozilla/dom/mobilemessage/PSms.h"
 #include "nsIDOMSmsFilter.h"
 #include "Types.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 namespace dom {
-namespace sms {
 
 class SmsFilter MOZ_FINAL : public nsIDOMMozSmsFilter
 {
@@ -22,23 +21,22 @@ public:
   NS_DECL_NSIDOMMOZSMSFILTER
 
   SmsFilter();
-  SmsFilter(const SmsFilterData& aData);
+  SmsFilter(const mobilemessage::SmsFilterData& aData);
 
-  const SmsFilterData& GetData() const;
+  const mobilemessage::SmsFilterData& GetData() const;
 
   static nsresult NewSmsFilter(nsISupports** aSmsFilter);
 
 private:
-  SmsFilterData mData;
+  mobilemessage::SmsFilterData mData;
 };
 
-inline const SmsFilterData&
+inline const mobilemessage::SmsFilterData&
 SmsFilter::GetData() const {
   return mData;
 }
 
-} // namespace sms
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_sms_SmsFilter_h
+#endif // mozilla_dom_mobilemessage_SmsFilter_h
diff --git a/dom/sms/src/SmsManager.cpp b/dom/mobilemessage/src/SmsManager.cpp
similarity index 99%
rename from dom/sms/src/SmsManager.cpp
rename to dom/mobilemessage/src/SmsManager.cpp
index b498986a2c3..720054c7e4e 100644
--- a/dom/sms/src/SmsManager.cpp
+++ b/dom/mobilemessage/src/SmsManager.cpp
@@ -28,11 +28,12 @@
 #define DELIVERY_SUCCESS_EVENT_NAME NS_LITERAL_STRING("deliverysuccess")
 #define DELIVERY_ERROR_EVENT_NAME   NS_LITERAL_STRING("deliveryerror")
 
-DOMCI_DATA(MozSmsManager, mozilla::dom::sms::SmsManager)
+using namespace mozilla::dom::mobilemessage;
+
+DOMCI_DATA(MozSmsManager, mozilla::dom::SmsManager)
 
 namespace mozilla {
 namespace dom {
-namespace sms {
 
 NS_INTERFACE_MAP_BEGIN(SmsManager)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozSmsManager)
@@ -402,6 +403,5 @@ SmsManager::Observe(nsISupports* aSubject, const char* aTopic,
   return NS_OK;
 }
 
-} // namespace sms
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/SmsManager.h b/dom/mobilemessage/src/SmsManager.h
similarity index 90%
rename from dom/sms/src/SmsManager.h
rename to dom/mobilemessage/src/SmsManager.h
index 729c96a168d..449350686d3 100644
--- a/dom/sms/src/SmsManager.h
+++ b/dom/mobilemessage/src/SmsManager.h
@@ -3,8 +3,8 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_SmsManager_h
-#define mozilla_dom_sms_SmsManager_h
+#ifndef mozilla_dom_mobilemessage_SmsManager_h
+#define mozilla_dom_mobilemessage_SmsManager_h
 
 #include "nsIDOMSmsManager.h"
 #include "nsIObserver.h"
@@ -14,7 +14,6 @@ class nsIDOMMozSmsMessage;
 
 namespace mozilla {
 namespace dom {
-namespace sms {
 
 class SmsManager : public nsDOMEventTargetHelper
                  , public nsIDOMMozSmsManager
@@ -49,8 +48,7 @@ private:
                                          nsIDOMMozSmsMessage* aMessage);
 };
 
-} // namespace sms
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_sms_SmsManager_h
+#endif // mozilla_dom_mobilemessage_SmsManager_h
diff --git a/dom/sms/src/SmsMessage.cpp b/dom/mobilemessage/src/SmsMessage.cpp
similarity index 98%
rename from dom/sms/src/SmsMessage.cpp
rename to dom/mobilemessage/src/SmsMessage.cpp
index 1f96b78a939..6e87a9099e9 100644
--- a/dom/sms/src/SmsMessage.cpp
+++ b/dom/mobilemessage/src/SmsMessage.cpp
@@ -9,11 +9,12 @@
 #include "jsfriendapi.h" // For js_DateGetMsecSinceEpoch
 #include "Constants.h"
 
-DOMCI_DATA(MozSmsMessage, mozilla::dom::sms::SmsMessage)
+using namespace mozilla::dom::mobilemessage;
+
+DOMCI_DATA(MozSmsMessage, mozilla::dom::SmsMessage)
 
 namespace mozilla {
 namespace dom {
-namespace sms {
 
 NS_INTERFACE_MAP_BEGIN(SmsMessage)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozSmsMessage)
@@ -254,6 +255,5 @@ SmsMessage::GetRead(bool* aRead)
   return NS_OK;
 }
 
-} // namespace sms
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/SmsMessage.h b/dom/mobilemessage/src/SmsMessage.h
similarity index 72%
rename from dom/sms/src/SmsMessage.h
rename to dom/mobilemessage/src/SmsMessage.h
index 903b504485c..9f7d226d816 100644
--- a/dom/sms/src/SmsMessage.h
+++ b/dom/mobilemessage/src/SmsMessage.h
@@ -3,19 +3,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_SmsMessage_h
-#define mozilla_dom_sms_SmsMessage_h
+#ifndef mozilla_dom_mobilemessage_SmsMessage_h
+#define mozilla_dom_mobilemessage_SmsMessage_h
 
-#include "mozilla/dom/sms/PSms.h"
+#include "mozilla/dom/mobilemessage/PSms.h"
 #include "nsIDOMMozSmsMessage.h"
 #include "nsString.h"
 #include "jspubtd.h"
-#include "Types.h"
+#include "mozilla/dom/mobilemessage/Types.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 namespace dom {
-namespace sms {
 
 class SmsMessage MOZ_FINAL : public nsIDOMMozSmsMessage
 {
@@ -24,15 +23,15 @@ public:
   NS_DECL_NSIDOMMOZSMSMESSAGE
 
   SmsMessage(int32_t aId,
-             DeliveryState aDelivery,
-             DeliveryStatus aDeliveryStatus,
+             mobilemessage::DeliveryState aDelivery,
+             mobilemessage::DeliveryStatus aDeliveryStatus,
              const nsString& aSender,
              const nsString& aReceiver,
              const nsString& aBody,
-             MessageClass aMessageClass,
+             mobilemessage::MessageClass aMessageClass,
              uint64_t aTimestamp,
              bool aRead);
-  SmsMessage(const SmsMessageData& aData);
+  SmsMessage(const mobilemessage::SmsMessageData& aData);
 
   static nsresult Create(int32_t aId,
                          const nsAString& aDelivery,
@@ -45,17 +44,16 @@ public:
                          const bool aRead,
                          JSContext* aCx,
                          nsIDOMMozSmsMessage** aMessage);
-  const SmsMessageData& GetData() const;
+  const mobilemessage::SmsMessageData& GetData() const;
 
 private:
   // Don't try to use the default constructor.
   SmsMessage();
 
-  SmsMessageData mData;
+  mobilemessage::SmsMessageData mData;
 };
 
-} // namespace sms
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_sms_SmsMessage_h
+#endif // mozilla_dom_mobilemessage_SmsMessage_h
diff --git a/dom/sms/src/SmsRequest.cpp b/dom/mobilemessage/src/SmsRequest.cpp
similarity index 99%
rename from dom/sms/src/SmsRequest.cpp
rename to dom/mobilemessage/src/SmsRequest.cpp
index 8d8a71e672e..4b3be3901e9 100644
--- a/dom/sms/src/SmsRequest.cpp
+++ b/dom/mobilemessage/src/SmsRequest.cpp
@@ -23,11 +23,12 @@
 #define SUCCESS_EVENT_NAME NS_LITERAL_STRING("success")
 #define ERROR_EVENT_NAME   NS_LITERAL_STRING("error")
 
-DOMCI_DATA(MozSmsRequest, mozilla::dom::sms::SmsRequest)
+using namespace mozilla::dom::mobilemessage;
+
+DOMCI_DATA(MozSmsRequest, mozilla::dom::SmsRequest)
 
 namespace mozilla {
 namespace dom {
-namespace sms {
 
 NS_IMPL_ISUPPORTS1(SmsRequestForwarder, nsISmsRequest)
 
@@ -579,6 +580,5 @@ SmsRequest::NotifyThreadList(const InfallibleTArray& aItems)
   NotifyThreadList(OBJECT_TO_JSVAL(array), cx);
 }
 
-} // namespace sms
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/SmsRequest.h b/dom/mobilemessage/src/SmsRequest.h
similarity index 82%
rename from dom/sms/src/SmsRequest.h
rename to dom/mobilemessage/src/SmsRequest.h
index 7abe4b1b980..d6dc8fc0c31 100644
--- a/dom/sms/src/SmsRequest.h
+++ b/dom/mobilemessage/src/SmsRequest.h
@@ -3,8 +3,8 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_SmsRequest_h
-#define mozilla_dom_sms_SmsRequest_h
+#ifndef mozilla_dom_mobilemessage_SmsRequest_h
+#define mozilla_dom_mobilemessage_SmsRequest_h
 
 #include "nsIDOMSmsRequest.h"
 #include "nsISmsRequest.h"
@@ -15,18 +15,19 @@ class nsIDOMMozSmsCursor;
 
 namespace mozilla {
 namespace dom {
-namespace sms {
 
-class SmsRequestChild;
-class SmsRequestParent;
-class MessageReply;
-class ThreadListItem;
+namespace mobilemessage {
+  class SmsRequestChild;
+  class SmsRequestParent;
+  class MessageReply;
+  class ThreadListItem;
+}
 
 // We need this forwarder to avoid a QI to nsIClassInfo.
 // See: https://bugzilla.mozilla.org/show_bug.cgi?id=775997#c51 
 class SmsRequestForwarder : public nsISmsRequest
 {
-  friend class SmsRequestChild;
+  friend class mobilemessage::SmsRequestChild;
 
 public:
   NS_DECL_ISUPPORTS
@@ -68,7 +69,7 @@ public:
 
   static already_AddRefed Create(SmsManager* aManager);
 
-  static already_AddRefed Create(SmsRequestParent* requestParent);
+  static already_AddRefed Create(mobilemessage::SmsRequestParent* requestParent);
   void Reset();
 
   void SetActorDied() {
@@ -76,16 +77,16 @@ public:
   }
 
   void
-  NotifyThreadList(const InfallibleTArray& aItems);
+  NotifyThreadList(const InfallibleTArray& aItems);
 
 private:
   SmsRequest() MOZ_DELETE;
 
   SmsRequest(SmsManager* aManager);
-  SmsRequest(SmsRequestParent* aParent);
+  SmsRequest(mobilemessage::SmsRequestParent* aParent);
   ~SmsRequest();
 
-  nsresult SendMessageReply(const MessageReply& aReply);
+  nsresult SendMessageReply(const mobilemessage::MessageReply& aReply);
 
   /**
    * Root mResult (jsval) to prevent garbage collection.
@@ -139,13 +140,12 @@ private:
   bool      mResultRooted;
   bool      mDone;
   bool      mParentAlive;
-  SmsRequestParent* mParent;
+  mobilemessage::SmsRequestParent* mParent;
   nsCOMPtr mError;
   nsCOMPtr mCursor;
 };
 
-} // namespace sms
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_sms_SmsRequest_h
+#endif // mozilla_dom_mobilemessage_SmsRequest_h
diff --git a/dom/sms/src/SmsSegmentInfo.cpp b/dom/mobilemessage/src/SmsSegmentInfo.cpp
similarity index 93%
rename from dom/sms/src/SmsSegmentInfo.cpp
rename to dom/mobilemessage/src/SmsSegmentInfo.cpp
index 356c81530a6..426ee87e778 100644
--- a/dom/sms/src/SmsSegmentInfo.cpp
+++ b/dom/mobilemessage/src/SmsSegmentInfo.cpp
@@ -6,11 +6,12 @@
 #include "SmsSegmentInfo.h"
 #include "nsIDOMClassInfo.h"
 
-DOMCI_DATA(MozSmsSegmentInfo, mozilla::dom::sms::SmsSegmentInfo)
+using namespace mozilla::dom::mobilemessage;
+
+DOMCI_DATA(MozSmsSegmentInfo, mozilla::dom::SmsSegmentInfo)
 
 namespace mozilla {
 namespace dom {
-namespace sms {
 
 NS_INTERFACE_MAP_BEGIN(SmsSegmentInfo)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozSmsSegmentInfo)
@@ -54,6 +55,5 @@ SmsSegmentInfo::GetCharsAvailableInLastSegment(int32_t* aCharsAvailableInLastSeg
   return NS_OK;
 }
 
-} // namespace sms
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/SmsSegmentInfo.h b/dom/mobilemessage/src/SmsSegmentInfo.h
similarity index 69%
rename from dom/sms/src/SmsSegmentInfo.h
rename to dom/mobilemessage/src/SmsSegmentInfo.h
index 4b43152e623..eb3e52472db 100644
--- a/dom/sms/src/SmsSegmentInfo.h
+++ b/dom/mobilemessage/src/SmsSegmentInfo.h
@@ -3,16 +3,15 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_SmsSegmentInfo_h
-#define mozilla_dom_sms_SmsSegmentInfo_h
+#ifndef mozilla_dom_mobilemessage_SmsSegmentInfo_h
+#define mozilla_dom_mobilemessage_SmsSegmentInfo_h
 
 #include "nsIDOMSmsSegmentInfo.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/dom/sms/SmsTypes.h"
+#include "mozilla/dom/mobilemessage/SmsTypes.h"
 
 namespace mozilla {
 namespace dom {
-namespace sms {
 
 class SmsSegmentInfo MOZ_FINAL : public nsIDOMMozSmsSegmentInfo
 {
@@ -23,14 +22,13 @@ public:
   SmsSegmentInfo(int32_t aSegments,
                  int32_t aCharsPerSegment,
                  int32_t aCharsAvailableInLastSegment);
-  SmsSegmentInfo(const SmsSegmentInfoData& aData);
+  SmsSegmentInfo(const mobilemessage::SmsSegmentInfoData& aData);
 
 private:
-  SmsSegmentInfoData mData;
+  mobilemessage::SmsSegmentInfoData mData;
 };
 
-} // namespace sms
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_sms_SmsSegmentInfo_h
+#endif // mozilla_dom_mobilemessage_SmsSegmentInfo_h
diff --git a/dom/sms/src/SmsServicesFactory.cpp b/dom/mobilemessage/src/SmsServicesFactory.cpp
similarity index 93%
rename from dom/sms/src/SmsServicesFactory.cpp
rename to dom/mobilemessage/src/SmsServicesFactory.cpp
index 8b1f7dcdbd5..bafa84df721 100644
--- a/dom/sms/src/SmsServicesFactory.cpp
+++ b/dom/mobilemessage/src/SmsServicesFactory.cpp
@@ -14,13 +14,9 @@
 
 #define RIL_MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1"
 
-#ifndef MOZ_B2G_RIL
-using namespace mozilla::dom::mobilemessage;
-#endif
-
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 /* static */ already_AddRefed
 SmsServicesFactory::CreateSmsService()
@@ -53,6 +49,6 @@ SmsServicesFactory::CreateMobileMessageDatabaseService()
   return mobileMessageDBService.forget();
 }
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/SmsServicesFactory.h b/dom/mobilemessage/src/SmsServicesFactory.h
similarity index 74%
rename from dom/sms/src/SmsServicesFactory.h
rename to dom/mobilemessage/src/SmsServicesFactory.h
index ffe6f50c94c..962a6c12196 100644
--- a/dom/sms/src/SmsServicesFactory.h
+++ b/dom/mobilemessage/src/SmsServicesFactory.h
@@ -3,8 +3,8 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_SmsServicesFactory_h
-#define mozilla_dom_sms_SmsServicesFactory_h
+#ifndef mozilla_dom_mobilemessage_SmsServicesFactory_h
+#define mozilla_dom_mobilemessage_SmsServicesFactory_h
 
 #include "nsCOMPtr.h"
 
@@ -13,7 +13,7 @@ class nsIMobileMessageDatabaseService;
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 class SmsServicesFactory
 {
@@ -22,8 +22,8 @@ public:
   static already_AddRefed CreateMobileMessageDatabaseService();
 };
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_sms_SmsServicesFactory_h
+#endif // mozilla_dom_mobilemessage_SmsServicesFactory_h
diff --git a/dom/sms/src/Types.h b/dom/mobilemessage/src/Types.h
similarity index 55%
rename from dom/sms/src/Types.h
rename to dom/mobilemessage/src/Types.h
index 2f5d5098d67..c85f883f0fc 100644
--- a/dom/sms/src/Types.h
+++ b/dom/mobilemessage/src/Types.h
@@ -3,14 +3,14 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_Types_h
-#define mozilla_dom_sms_Types_h
+#ifndef mozilla_dom_mobilemessage_Types_h
+#define mozilla_dom_mobilemessage_Types_h
 
 #include "IPCMessageUtils.h"
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 // For SmsMessageData.delivery.
 // Please keep the following files in sync with enum below:
@@ -55,7 +55,7 @@ enum MessageClass {
   eMessageClass_EndGuard
 };
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
@@ -65,42 +65,42 @@ namespace IPC {
  * Delivery state serializer.
  */
 template <>
-struct ParamTraits
-  : public EnumSerializer
+struct ParamTraits
+  : public EnumSerializer
 {};
 
 /**
  * Delivery status serializer.
  */
 template <>
-struct ParamTraits
-  : public EnumSerializer
+struct ParamTraits
+  : public EnumSerializer
 {};
 
 /**
  * Read state serializer.
  */
 template <>
-struct ParamTraits
-  : public EnumSerializer
+struct ParamTraits
+  : public EnumSerializer
 {};
 
 /**
  * Message class serializer.
  */
 template <>
-struct ParamTraits
-  : public EnumSerializer
+struct ParamTraits
+  : public EnumSerializer
 {};
 
 } // namespace IPC
 
-#endif // mozilla_dom_sms_Types_h
+#endif // mozilla_dom_mobilemessage_Types_h
diff --git a/dom/mobilemessage/src/android/MobileMessageDatabaseService.cpp b/dom/mobilemessage/src/android/MobileMessageDatabaseService.cpp
index d73aca5c4e7..f7968c0c08e 100644
--- a/dom/mobilemessage/src/android/MobileMessageDatabaseService.cpp
+++ b/dom/mobilemessage/src/android/MobileMessageDatabaseService.cpp
@@ -7,8 +7,6 @@
 #include "MobileMessageDatabaseService.h"
 #include "AndroidBridge.h"
 
-using namespace mozilla::dom::sms;
-
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
diff --git a/dom/sms/src/android/SmsService.cpp b/dom/mobilemessage/src/android/SmsService.cpp
similarity index 97%
rename from dom/sms/src/android/SmsService.cpp
rename to dom/mobilemessage/src/android/SmsService.cpp
index ecd28689302..394293bb1a6 100644
--- a/dom/sms/src/android/SmsService.cpp
+++ b/dom/mobilemessage/src/android/SmsService.cpp
@@ -3,7 +3,7 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/dom/sms/SmsMessage.h"
+#include "SmsMessage.h"
 #include "SmsService.h"
 #include "SmsSegmentInfo.h"
 #include "AndroidBridge.h"
@@ -11,7 +11,7 @@
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 NS_IMPL_ISUPPORTS1(SmsService, nsISmsService)
 
@@ -82,6 +82,6 @@ SmsService::CreateSmsSegmentInfo(int32_t aSegments,
   return NS_OK;
 }
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/android/SmsService.h b/dom/mobilemessage/src/android/SmsService.h
similarity index 71%
rename from dom/sms/src/android/SmsService.h
rename to dom/mobilemessage/src/android/SmsService.h
index 0105e52f6ca..7a1a4d438b8 100644
--- a/dom/sms/src/android/SmsService.h
+++ b/dom/mobilemessage/src/android/SmsService.h
@@ -3,14 +3,14 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_SmsService_h
-#define mozilla_dom_sms_SmsService_h
+#ifndef mozilla_dom_mobilemessage_SmsService_h
+#define mozilla_dom_mobilemessage_SmsService_h
 
 #include "nsISmsService.h"
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 class SmsService MOZ_FINAL : public nsISmsService
 {
@@ -19,8 +19,8 @@ public:
   NS_DECL_NSISMSSERVICE
 };
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_sms_SmsService_h
+#endif // mozilla_dom_mobilemessage_SmsService_h
diff --git a/dom/sms/src/fallback/SmsService.cpp b/dom/mobilemessage/src/fallback/SmsService.cpp
similarity index 96%
rename from dom/sms/src/fallback/SmsService.cpp
rename to dom/mobilemessage/src/fallback/SmsService.cpp
index 1ef70ac4dfd..3cf66dff606 100644
--- a/dom/sms/src/fallback/SmsService.cpp
+++ b/dom/mobilemessage/src/fallback/SmsService.cpp
@@ -3,14 +3,14 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/dom/sms/SmsMessage.h"
+#include "SmsMessage.h"
 #include "SmsSegmentInfo.h"
 #include "SmsService.h"
 #include "jsapi.h"
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 NS_IMPL_ISUPPORTS1(SmsService, nsISmsService)
 
@@ -69,6 +69,6 @@ SmsService::CreateSmsSegmentInfo(int32_t aSegments,
   return NS_OK;
 }
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/fallback/SmsService.h b/dom/mobilemessage/src/fallback/SmsService.h
similarity index 72%
rename from dom/sms/src/fallback/SmsService.h
rename to dom/mobilemessage/src/fallback/SmsService.h
index 3bc97c16e08..7402ae6cb31 100644
--- a/dom/sms/src/fallback/SmsService.h
+++ b/dom/mobilemessage/src/fallback/SmsService.h
@@ -3,15 +3,15 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_SmsService_h
-#define mozilla_dom_sms_SmsService_h
+#ifndef mozilla_dom_mobilemessage_SmsService_h
+#define mozilla_dom_mobilemessage_SmsService_h
 
 #include "nsISmsService.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 class SmsService MOZ_FINAL : public nsISmsService
 {
@@ -20,8 +20,8 @@ public:
   NS_DECL_NSISMSSERVICE
 };
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_sms_SmsService_h
+#endif // mozilla_dom_mobilemessage_SmsService_h
diff --git a/dom/sms/src/ipc/PSms.ipdl b/dom/mobilemessage/src/ipc/PSms.ipdl
similarity index 97%
rename from dom/sms/src/ipc/PSms.ipdl
rename to dom/mobilemessage/src/ipc/PSms.ipdl
index c83c0eaa8d3..ebc7a83bfa7 100644
--- a/dom/sms/src/ipc/PSms.ipdl
+++ b/dom/mobilemessage/src/ipc/PSms.ipdl
@@ -10,7 +10,7 @@ include SmsTypes;
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 struct SmsFilterData {
   uint64_t      startDate;
@@ -105,6 +105,6 @@ parent:
   ClearMessageList(int32_t aListId);
 };
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/ipc/PSmsRequest.ipdl b/dom/mobilemessage/src/ipc/PSmsRequest.ipdl
similarity index 94%
rename from dom/sms/src/ipc/PSmsRequest.ipdl
rename to dom/mobilemessage/src/ipc/PSmsRequest.ipdl
index 6949eea9b47..c13233b4513 100644
--- a/dom/sms/src/ipc/PSmsRequest.ipdl
+++ b/dom/mobilemessage/src/ipc/PSmsRequest.ipdl
@@ -5,14 +5,14 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
-include "mozilla/dom/sms/Types.h";
+include "mozilla/dom/mobilemessage/Types.h";
 
 include protocol PSms;
 include SmsTypes;
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 protocol PSmsRequest
 {
@@ -121,6 +121,6 @@ union MessageReply
   ReplyThreadListFail;
 };
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/ipc/SmsChild.cpp b/dom/mobilemessage/src/ipc/SmsChild.cpp
similarity index 97%
rename from dom/sms/src/ipc/SmsChild.cpp
rename to dom/mobilemessage/src/ipc/SmsChild.cpp
index 23b1ae90d2f..5665c344068 100644
--- a/dom/sms/src/ipc/SmsChild.cpp
+++ b/dom/mobilemessage/src/ipc/SmsChild.cpp
@@ -11,7 +11,8 @@
 #include "SmsRequest.h"
 
 using namespace mozilla;
-using namespace mozilla::dom::sms;
+using namespace mozilla::dom;
+using namespace mozilla::dom::mobilemessage;
 
 namespace {
 
@@ -32,7 +33,7 @@ NotifyObserversWithSmsMessage(const char* aEventName,
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 SmsChild::SmsChild()
 {
@@ -191,6 +192,6 @@ SmsRequestChild::Recv__delete__(const MessageReply& aReply)
   return true;
 }
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/ipc/SmsChild.h b/dom/mobilemessage/src/ipc/SmsChild.h
similarity index 83%
rename from dom/sms/src/ipc/SmsChild.h
rename to dom/mobilemessage/src/ipc/SmsChild.h
index e42ad9b8194..85b27de60fc 100644
--- a/dom/sms/src/ipc/SmsChild.h
+++ b/dom/mobilemessage/src/ipc/SmsChild.h
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_SmsChild_h
-#define mozilla_dom_sms_SmsChild_h
+#ifndef mozilla_dom_mobilemessage_SmsChild_h
+#define mozilla_dom_mobilemessage_SmsChild_h
 
-#include "mozilla/dom/sms/PSmsChild.h"
-#include "mozilla/dom/sms/PSmsRequestChild.h"
+#include "mozilla/dom/mobilemessage/PSmsChild.h"
+#include "mozilla/dom/mobilemessage/PSmsRequestChild.h"
 
 class nsISmsRequest;
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 class SmsChild : public PSmsChild
 {
@@ -53,7 +53,7 @@ protected:
 
 class SmsRequestChild : public PSmsRequestChild
 {
-  friend class mozilla::dom::sms::SmsChild;
+  friend class SmsChild;
 
   nsCOMPtr mReplyRequest;
 
@@ -70,8 +70,8 @@ protected:
   Recv__delete__(const MessageReply& aReply) MOZ_OVERRIDE;
 };
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_sms_SmsChild_h
+#endif // mozilla_dom_mobilemessage_SmsChild_h
diff --git a/dom/sms/src/ipc/SmsIPCService.cpp b/dom/mobilemessage/src/ipc/SmsIPCService.cpp
similarity index 97%
rename from dom/sms/src/ipc/SmsIPCService.cpp
rename to dom/mobilemessage/src/ipc/SmsIPCService.cpp
index 46b86c27159..097ed63fb42 100644
--- a/dom/sms/src/ipc/SmsIPCService.cpp
+++ b/dom/mobilemessage/src/ipc/SmsIPCService.cpp
@@ -7,15 +7,15 @@
 #include "SmsIPCService.h"
 #include "nsXULAppAPI.h"
 #include "jsapi.h"
-#include "mozilla/dom/sms/SmsChild.h"
-#include "mozilla/dom/sms/SmsMessage.h"
+#include "mozilla/dom/mobilemessage/SmsChild.h"
+#include "SmsMessage.h"
 #include "SmsFilter.h"
 #include "SmsRequest.h"
 #include "SmsSegmentInfo.h"
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 PSmsChild* gSmsChild;
 
@@ -170,6 +170,6 @@ SmsIPCService::GetThreadList(nsISmsRequest* aRequest)
   return NS_OK;
 }
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/ipc/SmsIPCService.h b/dom/mobilemessage/src/ipc/SmsIPCService.h
similarity index 78%
rename from dom/sms/src/ipc/SmsIPCService.h
rename to dom/mobilemessage/src/ipc/SmsIPCService.h
index 6e745a26709..71fdfa7f6d9 100644
--- a/dom/sms/src/ipc/SmsIPCService.h
+++ b/dom/mobilemessage/src/ipc/SmsIPCService.h
@@ -3,8 +3,8 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_SmsIPCService_h
-#define mozilla_dom_sms_SmsIPCService_h
+#ifndef mozilla_dom_mobilemessage_SmsIPCService_h
+#define mozilla_dom_mobilemessage_SmsIPCService_h
 
 #include "nsISmsService.h"
 #include "nsIMobileMessageDatabaseService.h"
@@ -12,7 +12,7 @@
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 class PSmsChild;
 
@@ -28,8 +28,8 @@ private:
   static PSmsChild* GetSmsChild();
 };
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_sms_SmsIPCService_h
+#endif // mozilla_dom_mobilemessage_SmsIPCService_h
diff --git a/dom/sms/src/ipc/SmsParent.cpp b/dom/mobilemessage/src/ipc/SmsParent.cpp
similarity index 99%
rename from dom/sms/src/ipc/SmsParent.cpp
rename to dom/mobilemessage/src/ipc/SmsParent.cpp
index 467bf0a8c53..bddb3a6cc8b 100644
--- a/dom/sms/src/ipc/SmsParent.cpp
+++ b/dom/mobilemessage/src/ipc/SmsParent.cpp
@@ -18,7 +18,7 @@
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 NS_IMPL_ISUPPORTS1(SmsParent, nsIObserver)
 
@@ -364,6 +364,6 @@ SmsRequestParent::DoRequest(const GetThreadListRequest& aRequest)
   return true;
 }
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/ipc/SmsParent.h b/dom/mobilemessage/src/ipc/SmsParent.h
similarity index 86%
rename from dom/sms/src/ipc/SmsParent.h
rename to dom/mobilemessage/src/ipc/SmsParent.h
index feb90a4ad1e..4f00348f05a 100644
--- a/dom/sms/src/ipc/SmsParent.h
+++ b/dom/mobilemessage/src/ipc/SmsParent.h
@@ -3,27 +3,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_SmsParent_h
-#define mozilla_dom_sms_SmsParent_h
+#ifndef mozilla_dom_mobilemessage_SmsParent_h
+#define mozilla_dom_mobilemessage_SmsParent_h
 
-#include "mozilla/dom/sms/PSmsParent.h"
-#include "mozilla/dom/sms/PSmsRequestParent.h"
+#include "mozilla/dom/mobilemessage/PSmsParent.h"
+#include "mozilla/dom/mobilemessage/PSmsRequestParent.h"
 #include "nsIObserver.h"
 
 namespace mozilla {
 namespace dom {
 
 class ContentParent;
-
-} // namespace dom
-} // namespace mozilla
-
-namespace mozilla {
-namespace dom {
-namespace sms {
-
 class SmsRequest;
 
+namespace mobilemessage {
+
 class SmsParent : public PSmsParent
                 , public nsIObserver
 {
@@ -99,8 +93,8 @@ protected:
   DoRequest(const GetThreadListRequest& aRequest);
 };
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_sms_SmsParent_h
+#endif // mozilla_dom_mobilemessage_SmsParent_h
diff --git a/dom/sms/src/ipc/SmsTypes.ipdlh b/dom/mobilemessage/src/ipc/SmsTypes.ipdlh
similarity index 92%
rename from dom/sms/src/ipc/SmsTypes.ipdlh
rename to dom/mobilemessage/src/ipc/SmsTypes.ipdlh
index 97407ab58fc..051e90d62c3 100644
--- a/dom/sms/src/ipc/SmsTypes.ipdlh
+++ b/dom/mobilemessage/src/ipc/SmsTypes.ipdlh
@@ -4,7 +4,7 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-include "mozilla/dom/sms/Types.h";
+include "mozilla/dom/mobilemessage/Types.h";
 
 using DeliveryState;
 using DeliveryStatus;
@@ -13,7 +13,7 @@ using ReadState;
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 struct SmsSegmentInfoData
 {
diff --git a/dom/sms/src/ipc/ipdl.mk b/dom/mobilemessage/src/ipc/ipdl.mk
similarity index 100%
rename from dom/sms/src/ipc/ipdl.mk
rename to dom/mobilemessage/src/ipc/ipdl.mk
diff --git a/dom/sms/src/ril/SmsService.cpp b/dom/mobilemessage/src/ril/SmsService.cpp
similarity index 96%
rename from dom/sms/src/ril/SmsService.cpp
rename to dom/mobilemessage/src/ril/SmsService.cpp
index 87e3afcf1e9..9ccaa38839e 100644
--- a/dom/sms/src/ril/SmsService.cpp
+++ b/dom/mobilemessage/src/ril/SmsService.cpp
@@ -3,14 +3,14 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/dom/sms/SmsMessage.h"
+#include "SmsMessage.h"
 #include "SmsService.h"
 #include "jsapi.h"
 #include "SmsSegmentInfo.h"
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 NS_IMPL_ISUPPORTS1(SmsService, nsISmsService)
 
@@ -79,6 +79,6 @@ SmsService::CreateSmsSegmentInfo(int32_t aSegments,
   return NS_OK;
 }
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
diff --git a/dom/sms/src/ril/SmsService.h b/dom/mobilemessage/src/ril/SmsService.h
similarity index 73%
rename from dom/sms/src/ril/SmsService.h
rename to dom/mobilemessage/src/ril/SmsService.h
index 35dff1d8b1e..ebfe1f55078 100644
--- a/dom/sms/src/ril/SmsService.h
+++ b/dom/mobilemessage/src/ril/SmsService.h
@@ -2,8 +2,8 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_sms_SmsService_h
-#define mozilla_dom_sms_SmsService_h
+#ifndef mozilla_dom_mobilemessage_SmsService_h
+#define mozilla_dom_mobilemessage_SmsService_h
 
 #include "nsISmsService.h"
 #include "nsCOMPtr.h"
@@ -11,7 +11,7 @@
 
 namespace mozilla {
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 
 class SmsService : public nsISmsService
 {
@@ -24,8 +24,8 @@ protected:
   nsCOMPtr mRIL;
 };
 
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_sms_SmsService_h
+#endif // mozilla_dom_mobilemessage_SmsService_h
diff --git a/dom/sms/tests/Makefile.in b/dom/mobilemessage/tests/Makefile.in
similarity index 100%
rename from dom/sms/tests/Makefile.in
rename to dom/mobilemessage/tests/Makefile.in
diff --git a/dom/sms/tests/marionette/manifest.ini b/dom/mobilemessage/tests/marionette/manifest.ini
similarity index 100%
rename from dom/sms/tests/marionette/manifest.ini
rename to dom/mobilemessage/tests/marionette/manifest.ini
diff --git a/dom/sms/tests/marionette/test_between_emulators.py b/dom/mobilemessage/tests/marionette/test_between_emulators.py
similarity index 100%
rename from dom/sms/tests/marionette/test_between_emulators.py
rename to dom/mobilemessage/tests/marionette/test_between_emulators.py
diff --git a/dom/sms/tests/marionette/test_bug814761.js b/dom/mobilemessage/tests/marionette/test_bug814761.js
similarity index 100%
rename from dom/sms/tests/marionette/test_bug814761.js
rename to dom/mobilemessage/tests/marionette/test_bug814761.js
diff --git a/dom/sms/tests/marionette/test_emulator_loopback.js b/dom/mobilemessage/tests/marionette/test_emulator_loopback.js
similarity index 100%
rename from dom/sms/tests/marionette/test_emulator_loopback.js
rename to dom/mobilemessage/tests/marionette/test_emulator_loopback.js
diff --git a/dom/sms/tests/marionette/test_filter_date.js b/dom/mobilemessage/tests/marionette/test_filter_date.js
similarity index 100%
rename from dom/sms/tests/marionette/test_filter_date.js
rename to dom/mobilemessage/tests/marionette/test_filter_date.js
diff --git a/dom/sms/tests/marionette/test_filter_date_notfound.js b/dom/mobilemessage/tests/marionette/test_filter_date_notfound.js
similarity index 100%
rename from dom/sms/tests/marionette/test_filter_date_notfound.js
rename to dom/mobilemessage/tests/marionette/test_filter_date_notfound.js
diff --git a/dom/sms/tests/marionette/test_filter_mixed.js b/dom/mobilemessage/tests/marionette/test_filter_mixed.js
similarity index 100%
rename from dom/sms/tests/marionette/test_filter_mixed.js
rename to dom/mobilemessage/tests/marionette/test_filter_mixed.js
diff --git a/dom/sms/tests/marionette/test_filter_number_multiple.js b/dom/mobilemessage/tests/marionette/test_filter_number_multiple.js
similarity index 100%
rename from dom/sms/tests/marionette/test_filter_number_multiple.js
rename to dom/mobilemessage/tests/marionette/test_filter_number_multiple.js
diff --git a/dom/sms/tests/marionette/test_filter_number_single.js b/dom/mobilemessage/tests/marionette/test_filter_number_single.js
similarity index 100%
rename from dom/sms/tests/marionette/test_filter_number_single.js
rename to dom/mobilemessage/tests/marionette/test_filter_number_single.js
diff --git a/dom/sms/tests/marionette/test_filter_read.js b/dom/mobilemessage/tests/marionette/test_filter_read.js
similarity index 100%
rename from dom/sms/tests/marionette/test_filter_read.js
rename to dom/mobilemessage/tests/marionette/test_filter_read.js
diff --git a/dom/sms/tests/marionette/test_filter_received.js b/dom/mobilemessage/tests/marionette/test_filter_received.js
similarity index 100%
rename from dom/sms/tests/marionette/test_filter_received.js
rename to dom/mobilemessage/tests/marionette/test_filter_received.js
diff --git a/dom/sms/tests/marionette/test_filter_sent.js b/dom/mobilemessage/tests/marionette/test_filter_sent.js
similarity index 100%
rename from dom/sms/tests/marionette/test_filter_sent.js
rename to dom/mobilemessage/tests/marionette/test_filter_sent.js
diff --git a/dom/sms/tests/marionette/test_filter_unread.js b/dom/mobilemessage/tests/marionette/test_filter_unread.js
similarity index 100%
rename from dom/sms/tests/marionette/test_filter_unread.js
rename to dom/mobilemessage/tests/marionette/test_filter_unread.js
diff --git a/dom/sms/tests/marionette/test_getmessage.js b/dom/mobilemessage/tests/marionette/test_getmessage.js
similarity index 100%
rename from dom/sms/tests/marionette/test_getmessage.js
rename to dom/mobilemessage/tests/marionette/test_getmessage.js
diff --git a/dom/sms/tests/marionette/test_getmessage_notfound.js b/dom/mobilemessage/tests/marionette/test_getmessage_notfound.js
similarity index 100%
rename from dom/sms/tests/marionette/test_getmessage_notfound.js
rename to dom/mobilemessage/tests/marionette/test_getmessage_notfound.js
diff --git a/dom/sms/tests/marionette/test_getmessages.js b/dom/mobilemessage/tests/marionette/test_getmessages.js
similarity index 100%
rename from dom/sms/tests/marionette/test_getmessages.js
rename to dom/mobilemessage/tests/marionette/test_getmessages.js
diff --git a/dom/sms/tests/marionette/test_incoming.js b/dom/mobilemessage/tests/marionette/test_incoming.js
similarity index 100%
rename from dom/sms/tests/marionette/test_incoming.js
rename to dom/mobilemessage/tests/marionette/test_incoming.js
diff --git a/dom/sms/tests/marionette/test_incoming_delete.js b/dom/mobilemessage/tests/marionette/test_incoming_delete.js
similarity index 100%
rename from dom/sms/tests/marionette/test_incoming_delete.js
rename to dom/mobilemessage/tests/marionette/test_incoming_delete.js
diff --git a/dom/sms/tests/marionette/test_incoming_max_segments.js b/dom/mobilemessage/tests/marionette/test_incoming_max_segments.js
similarity index 100%
rename from dom/sms/tests/marionette/test_incoming_max_segments.js
rename to dom/mobilemessage/tests/marionette/test_incoming_max_segments.js
diff --git a/dom/sms/tests/marionette/test_incoming_multipart.js b/dom/mobilemessage/tests/marionette/test_incoming_multipart.js
similarity index 100%
rename from dom/sms/tests/marionette/test_incoming_multipart.js
rename to dom/mobilemessage/tests/marionette/test_incoming_multipart.js
diff --git a/dom/sms/tests/marionette/test_mark_msg_read.js b/dom/mobilemessage/tests/marionette/test_mark_msg_read.js
similarity index 100%
rename from dom/sms/tests/marionette/test_mark_msg_read.js
rename to dom/mobilemessage/tests/marionette/test_mark_msg_read.js
diff --git a/dom/sms/tests/marionette/test_mark_msg_read_error.js b/dom/mobilemessage/tests/marionette/test_mark_msg_read_error.js
similarity index 100%
rename from dom/sms/tests/marionette/test_mark_msg_read_error.js
rename to dom/mobilemessage/tests/marionette/test_mark_msg_read_error.js
diff --git a/dom/sms/tests/marionette/test_message_classes.js b/dom/mobilemessage/tests/marionette/test_message_classes.js
similarity index 100%
rename from dom/sms/tests/marionette/test_message_classes.js
rename to dom/mobilemessage/tests/marionette/test_message_classes.js
diff --git a/dom/sms/tests/marionette/test_outgoing.js b/dom/mobilemessage/tests/marionette/test_outgoing.js
similarity index 100%
rename from dom/sms/tests/marionette/test_outgoing.js
rename to dom/mobilemessage/tests/marionette/test_outgoing.js
diff --git a/dom/sms/tests/marionette/test_outgoing_delete.js b/dom/mobilemessage/tests/marionette/test_outgoing_delete.js
similarity index 100%
rename from dom/sms/tests/marionette/test_outgoing_delete.js
rename to dom/mobilemessage/tests/marionette/test_outgoing_delete.js
diff --git a/dom/sms/tests/marionette/test_outgoing_max_segments.js b/dom/mobilemessage/tests/marionette/test_outgoing_max_segments.js
similarity index 100%
rename from dom/sms/tests/marionette/test_outgoing_max_segments.js
rename to dom/mobilemessage/tests/marionette/test_outgoing_max_segments.js
diff --git a/dom/sms/tests/marionette/test_segment_info.js b/dom/mobilemessage/tests/marionette/test_segment_info.js
similarity index 100%
rename from dom/sms/tests/marionette/test_segment_info.js
rename to dom/mobilemessage/tests/marionette/test_segment_info.js
diff --git a/dom/sms/tests/marionette/test_strict_7bit_encoding.js b/dom/mobilemessage/tests/marionette/test_strict_7bit_encoding.js
similarity index 100%
rename from dom/sms/tests/marionette/test_strict_7bit_encoding.js
rename to dom/mobilemessage/tests/marionette/test_strict_7bit_encoding.js
diff --git a/dom/sms/interfaces/moz.build b/dom/mobilemessage/tests/moz.build
similarity index 100%
rename from dom/sms/interfaces/moz.build
rename to dom/mobilemessage/tests/moz.build
diff --git a/dom/sms/tests/test_sms_basics.html b/dom/mobilemessage/tests/test_sms_basics.html
similarity index 100%
rename from dom/sms/tests/test_sms_basics.html
rename to dom/mobilemessage/tests/test_sms_basics.html
diff --git a/dom/sms/tests/test_smsdatabaseservice.xul b/dom/mobilemessage/tests/test_smsdatabaseservice.xul
similarity index 100%
rename from dom/sms/tests/test_smsdatabaseservice.xul
rename to dom/mobilemessage/tests/test_smsdatabaseservice.xul
diff --git a/dom/sms/tests/test_smsfilter.html b/dom/mobilemessage/tests/test_smsfilter.html
similarity index 100%
rename from dom/sms/tests/test_smsfilter.html
rename to dom/mobilemessage/tests/test_smsfilter.html
diff --git a/dom/sms/tests/test_smsservice_createsmsmessage.js b/dom/mobilemessage/tests/test_smsservice_createsmsmessage.js
similarity index 100%
rename from dom/sms/tests/test_smsservice_createsmsmessage.js
rename to dom/mobilemessage/tests/test_smsservice_createsmsmessage.js
diff --git a/dom/sms/tests/xpcshell.ini b/dom/mobilemessage/tests/xpcshell.ini
similarity index 100%
rename from dom/sms/tests/xpcshell.ini
rename to dom/mobilemessage/tests/xpcshell.ini
diff --git a/dom/moz.build b/dom/moz.build
index fdc85ed4b46..5e2ac6edb15 100644
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -53,7 +53,6 @@ PARALLEL_DIRS += [
     'quota',
     'settings',
     'mobilemessage',
-    'sms',
     'mms',
     'src',
     'time',
diff --git a/dom/sms/Makefile.in b/dom/sms/Makefile.in
deleted file mode 100644
index cfa730adc58..00000000000
--- a/dom/sms/Makefile.in
+++ /dev/null
@@ -1,18 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-DEPTH            = @DEPTH@
-topsrcdir        = @top_srcdir@
-srcdir           = @srcdir@
-VPATH            = @srcdir@
-
-relativesrcdir   = @relativesrcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-ifdef ENABLE_TESTS
-XPCSHELL_TESTS = tests
-endif
-
-include $(topsrcdir)/config/rules.mk
diff --git a/dom/sms/interfaces/Makefile.in b/dom/sms/interfaces/Makefile.in
deleted file mode 100644
index ed566fa5942..00000000000
--- a/dom/sms/interfaces/Makefile.in
+++ /dev/null
@@ -1,35 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-DEPTH            = @DEPTH@
-topsrcdir        = @top_srcdir@
-srcdir           = @srcdir@
-VPATH            = @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-XPIDL_MODULE = dom_sms
-
-include $(topsrcdir)/dom/dom-config.mk
-
-XPIDLSRCS = \
-  nsIDOMNavigatorSms.idl \
-  nsIDOMSmsManager.idl \
-  nsIDOMMozSmsMessage.idl \
-  nsIDOMMozSmsEvent.idl \
-  nsIDOMSmsRequest.idl \
-  nsIDOMSmsFilter.idl \
-  nsIDOMSmsCursor.idl \
-  nsIDOMSmsSegmentInfo.idl \
-  nsISmsRequest.idl \
-  nsISmsService.idl \
-  $(NULL)
-
-include $(topsrcdir)/config/rules.mk
-
-XPIDL_FLAGS += \
-  -I$(topsrcdir)/dom/base \
-  -I$(topsrcdir)/dom/interfaces/base \
-  -I$(topsrcdir)/dom/interfaces/events \
-  $(NULL)
diff --git a/dom/sms/moz.build b/dom/sms/moz.build
deleted file mode 100644
index e410ebf2dde..00000000000
--- a/dom/sms/moz.build
+++ /dev/null
@@ -1,7 +0,0 @@
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-DIRS += ['interfaces', 'src']
-TEST_DIRS += ['tests']
diff --git a/dom/sms/src/Makefile.in b/dom/sms/src/Makefile.in
deleted file mode 100644
index 0cd13f5df72..00000000000
--- a/dom/sms/src/Makefile.in
+++ /dev/null
@@ -1,94 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-DEPTH            = @DEPTH@
-topsrcdir        = @top_srcdir@
-srcdir           = @srcdir@
-VPATH            = \
-  $(srcdir) \
-  $(srcdir)/ipc \
-  $(NULL)
-
-include $(DEPTH)/config/autoconf.mk
-
-ifeq (android,$(MOZ_WIDGET_TOOLKIT))
-VPATH += \
-  $(srcdir)/android \
-  $(topsrcdir)/dom/mobilemessage/src/android \
-  $(NULL)
-else ifdef MOZ_B2G_RIL
-VPATH += \
-  $(srcdir)/ril \
-  $(topsrcdir)/dom/mobilemessage/src/ril \
-  $(NULL)
-else
-VPATH += \
-  $(srcdir)/fallback \
-  $(topsrcdir)/dom/mobilemessage/src/fallback \
-  $(NULL)
-endif
-
-LIBRARY_NAME     = dom_sms_s
-LIBXUL_LIBRARY   = 1
-FORCE_STATIC_LIB = 1
-ifndef _MSC_VER
-FAIL_ON_WARNINGS := 1
-endif # !_MSC_VER
-
-include $(topsrcdir)/dom/dom-config.mk
-
-EXPORTS_NAMESPACES = mozilla/dom/sms
-
-EXPORTS_mozilla/dom/sms = \
-  SmsChild.h \
-  SmsParent.h \
-  SmsServicesFactory.h \
-  Constants.h \
-  Types.h \
-  SmsMessage.h \
-  SmsRequest.h \
-  SmsSegmentInfo.h \
-  SmsFilter.h \
-  $(NULL)
-
-CPPSRCS = \
-  SmsManager.cpp \
-  SmsService.cpp \
-  SmsIPCService.cpp \
-  SmsServicesFactory.cpp \
-  SmsParent.cpp \
-  SmsMessage.cpp \
-  Constants.cpp \
-  SmsChild.cpp \
-  SmsRequest.cpp \
-  SmsFilter.cpp \
-  SmsSegmentInfo.cpp \
-  SmsCursor.cpp \
-  $(NULL)
-
-LOCAL_INCLUDES = \
-  -I$(topsrcdir)/content/events/src \
-  -I$(topsrcdir)/dom/base \
-  $(NULL)
-
-# Add VPATH to LOCAL_INCLUDES so we are going to include the correct backend
-# subdirectory (and the ipc one).
-LOCAL_INCLUDES += $(VPATH:%=-I%)
-
-ifdef MOZ_B2G_RIL
-LOCAL_INCLUDES += \
-  -I$(topsrcdir)/dom/telephony \
-  -I$(topsrcdir)/dom/system/gonk \
-  $(NULL)
-
-EXTRA_COMPONENTS = \
-  $(NULL)
-else
-CPPSRCS += \
-  $(NULL)  
-endif
-
-include $(topsrcdir)/config/config.mk
-include $(topsrcdir)/ipc/chromium/chromium-config.mk
-include $(topsrcdir)/config/rules.mk
diff --git a/dom/sms/src/moz.build b/dom/sms/src/moz.build
deleted file mode 100644
index 58ce5e27339..00000000000
--- a/dom/sms/src/moz.build
+++ /dev/null
@@ -1,5 +0,0 @@
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
diff --git a/dom/sms/tests/moz.build b/dom/sms/tests/moz.build
deleted file mode 100644
index 58ce5e27339..00000000000
--- a/dom/sms/tests/moz.build
+++ /dev/null
@@ -1,5 +0,0 @@
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js
index e2214d58340..e35891175b6 100644
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -57,10 +57,10 @@ const kTimeNitzAutomaticUpdateEnabled    = "time.nitz.automatic-update.enabled";
 const kTimeNitzAvailable                 = "time.nitz.available";
 const kCellBroadcastSearchList           = "ril.cellbroadcast.searchlist";
 
-const DOM_SMS_DELIVERY_RECEIVED          = "received";
-const DOM_SMS_DELIVERY_SENDING           = "sending";
-const DOM_SMS_DELIVERY_SENT              = "sent";
-const DOM_SMS_DELIVERY_ERROR             = "error";
+const DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED = "received";
+const DOM_MOBILE_MESSAGE_DELIVERY_SENDING  = "sending";
+const DOM_MOBILE_MESSAGE_DELIVERY_SENT     = "sent";
+const DOM_MOBILE_MESSAGE_DELIVERY_ERROR    = "error";
 
 const CALL_WAKELOCK_TIMEOUT              = 5000;
 
@@ -1551,7 +1551,7 @@ RadioInterfaceLayer.prototype = {
 
       gSystemMessenger.broadcastMessage("sms-received", {
         id: message.id,
-        delivery: DOM_SMS_DELIVERY_RECEIVED,
+        delivery: DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED,
         deliveryStatus: RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS,
         sender: message.sender,
         receiver: message.receiver,
@@ -1569,7 +1569,7 @@ RadioInterfaceLayer.prototype = {
                                                                      notifyReceived);
     } else {
       message.id = -1;
-      message.delivery = DOM_SMS_DELIVERY_RECEIVED;
+      message.delivery = DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED;
       message.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS;
       message.read = false;
 
@@ -1604,14 +1604,14 @@ RadioInterfaceLayer.prototype = {
     }
 
     gMobileMessageDatabaseService.setMessageDelivery(options.sms.id,
-                                                     DOM_SMS_DELIVERY_SENT,
+                                                     DOM_MOBILE_MESSAGE_DELIVERY_SENT,
                                                      options.sms.deliveryStatus,
                                                      function notifyResult(rv, record) {
       let sms = this.createSmsMessageFromRecord(record);
       //TODO bug 832140 handle !Components.isSuccessCode(rv)
       gSystemMessenger.broadcastMessage("sms-sent",
                                         {id: options.sms.id,
-                                         delivery: DOM_SMS_DELIVERY_SENT,
+                                         delivery: DOM_MOBILE_MESSAGE_DELIVERY_SENT,
                                          deliveryStatus: options.sms.deliveryStatus,
                                          sender: message.sender || null,
                                          receiver: options.sms.receiver,
@@ -1672,7 +1672,7 @@ RadioInterfaceLayer.prototype = {
     }
 
     gMobileMessageDatabaseService.setMessageDelivery(options.sms.id,
-                                                     DOM_SMS_DELIVERY_ERROR,
+                                                     DOM_MOBILE_MESSAGE_DELIVERY_ERROR,
                                                      RIL.GECKO_SMS_DELIVERY_STATUS_ERROR,
                                                      function notifyResult(rv, record) {
       let sms = this.createSmsMessageFromRecord(record);
diff --git a/embedding/android/GeckoSmsManager.java b/embedding/android/GeckoSmsManager.java
index 5ca77519725..2f1cd224e74 100644
--- a/embedding/android/GeckoSmsManager.java
+++ b/embedding/android/GeckoSmsManager.java
@@ -298,7 +298,7 @@ public class GeckoSmsManager
 
   /*
    * Make sure that the following error codes are in sync with the ones
-   * defined in dom/sms/interfaces/nsISmsRequestManager.idl. They are owned
+   * defined in dom/mobilemessage/interfaces/nsISmsRequestManager.idl. They are owned
    * owned by the interface.
    */
   public final static int kNoError       = 0;
@@ -317,7 +317,7 @@ public class GeckoSmsManager
 
   /*
    * Keep the following state codes in syng with |DeliveryState| in:
-   * dom/sms/src/Types.h
+   * dom/mobilemessage/src/Types.h
    */
   private final static int kDeliveryStateSent     = 0;
   private final static int kDeliveryStateReceived = 1;
@@ -326,7 +326,7 @@ public class GeckoSmsManager
 
   /*
    * Keep the following status codes in sync with |DeliveryStatus| in:
-   * dom/sms/src/Types.h
+   * dom/mobilemessage/src/Types.h
    */
   private final static int kDeliveryStatusNotApplicable = 0;
   private final static int kDeliveryStatusSuccess       = 1;
@@ -344,7 +344,7 @@ public class GeckoSmsManager
 
   /*
    * Keep the following values in sync with |MessageClass| in:
-   * dom/sms/src/Types.h
+   * dom/mobilemessage/src/Types.h
    */
   private final static int kMessageClassNormal  = 0;
   private final static int kMessageClassClass0  = 1;
diff --git a/ipc/ipdl/Makefile.in b/ipc/ipdl/Makefile.in
index 4ec81cd34d5..d98f6c4c440 100644
--- a/ipc/ipdl/Makefile.in
+++ b/ipc/ipdl/Makefile.in
@@ -28,7 +28,7 @@ IPDLDIRS =  \
   dom/bluetooth/ipc \
   dom/plugins/ipc  \
   dom/ipc \
-  dom/sms/src/ipc \
+  dom/mobilemessage/src/ipc \
   dom/src/storage \
   dom/network/src \
   gfx/layers/ipc \
diff --git a/layout/build/Makefile.in b/layout/build/Makefile.in
index 7cd4dc95533..f483857f8c4 100644
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -77,7 +77,6 @@ SHARED_LIBRARY_LIBS = \
 	$(DEPTH)/dom/network/src/$(LIB_PREFIX)dom_network_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/media/$(LIB_PREFIX)dom_media_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/mobilemessage/src/$(LIB_PREFIX)dom_mobilemessage_s.$(LIB_SUFFIX) \
-	$(DEPTH)/dom/sms/src/$(LIB_PREFIX)dom_sms_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/src/events/$(LIB_PREFIX)jsdomevents_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/src/json/$(LIB_PREFIX)json_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/src/jsurl/$(LIB_PREFIX)jsurl_s.$(LIB_SUFFIX) \
diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp
index 7efd9c95039..e399dd98e65 100644
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -221,7 +221,7 @@ static void Shutdown();
 #include "nsCSPService.h"
 #include "nsISmsService.h"
 #include "nsIMobileMessageDatabaseService.h"
-#include "mozilla/dom/sms/SmsServicesFactory.h"
+#include "mozilla/dom/mobilemessage/SmsServicesFactory.h"
 #include "nsIPowerManagerService.h"
 #include "nsIAlarmHalService.h"
 #include "nsIMediaManager.h"
@@ -241,7 +241,7 @@ static void Shutdown();
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::file;
-using namespace mozilla::dom::sms;
+using namespace mozilla::dom::mobilemessage;
 using mozilla::dom::alarm::AlarmHalService;
 using mozilla::dom::indexedDB::IndexedDatabaseManager;
 using mozilla::dom::power::PowerManagerService;
diff --git a/mobile/android/base/GeckoSmsManager.java b/mobile/android/base/GeckoSmsManager.java
index e5885a815ec..4b2ec603cfb 100644
--- a/mobile/android/base/GeckoSmsManager.java
+++ b/mobile/android/base/GeckoSmsManager.java
@@ -292,7 +292,7 @@ public class GeckoSmsManager
 
   /*
    * Make sure that the following error codes are in sync with |ErrorType| in:
-   * dom/sms/src/Types.h
+   * dom/mobilemessage/src/Types.h
    * The error code are owned by the DOM.
    */
   public final static int kNoError       = 0;
@@ -311,7 +311,7 @@ public class GeckoSmsManager
 
   /*
    * Keep the following state codes in syng with |DeliveryState| in:
-   * dom/sms/src/Types.h
+   * dom/mobilemessage/src/Types.h
    */
   private final static int kDeliveryStateSent     = 0;
   private final static int kDeliveryStateReceived = 1;
@@ -320,7 +320,7 @@ public class GeckoSmsManager
 
   /*
    * Keep the following status codes in sync with |DeliveryStatus| in:
-   * dom/sms/src/Types.h
+   * dom/mobilemessage/src/Types.h
    */
   private final static int kDeliveryStatusNotApplicable = 0;
   private final static int kDeliveryStatusSuccess       = 1;
@@ -338,7 +338,7 @@ public class GeckoSmsManager
 
   /*
    * Keep the following values in sync with |MessageClass| in:
-   * dom/sms/src/Types.h
+   * dom/mobilemessage/src/Types.h
    */
   private final static int kMessageClassNormal  = 0;
   private final static int kMessageClassClass0  = 1;
diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in
index 6e0849edabf..a377cbabe0f 100644
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -132,7 +132,6 @@
 @BINPATH@/components/dom_settings.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_mobilemessage.xpt
-@BINPATH@/components/dom_sms.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_system.xpt
diff --git a/mobile/android/installer/removed-files.in b/mobile/android/installer/removed-files.in
index 55a3d3697fa..a0f651e7862 100644
--- a/mobile/android/installer/removed-files.in
+++ b/mobile/android/installer/removed-files.in
@@ -8,3 +8,4 @@ extensions/feedback@mobile.mozilla.org.xpi
 #endif
 @DLL_PREFIX@mozutils@DLL_SUFFIX@
 jssubloader/
+components/dom_sms.xpt
\ No newline at end of file
diff --git a/mobile/xul/installer/package-manifest.in b/mobile/xul/installer/package-manifest.in
index 205c2213055..eb82226aadf 100644
--- a/mobile/xul/installer/package-manifest.in
+++ b/mobile/xul/installer/package-manifest.in
@@ -183,7 +183,6 @@
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_mobilemessage.xpt
-@BINPATH@/components/dom_sms.xpt
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_threads.xpt
 @BINPATH@/components/dom_traversal.xpt
diff --git a/mobile/xul/installer/removed-files.in b/mobile/xul/installer/removed-files.in
index bf483cd555f..b6eced4ac38 100644
--- a/mobile/xul/installer/removed-files.in
+++ b/mobile/xul/installer/removed-files.in
@@ -32,3 +32,4 @@ extensions/feedback@mobile.mozilla.org.xpi
 #endif
 @DLL_PREFIX@mozutils@DLL_SUFFIX@
 jssubloader/
+components/dom_sms.xpt
\ No newline at end of file
diff --git a/testing/marionette/client/marionette/tests/unit-tests.ini b/testing/marionette/client/marionette/tests/unit-tests.ini
index 989b3207190..379b3bd832e 100644
--- a/testing/marionette/client/marionette/tests/unit-tests.ini
+++ b/testing/marionette/client/marionette/tests/unit-tests.ini
@@ -18,7 +18,7 @@ skip = false
 [include:../../../../../dom/telephony/test/marionette/manifest.ini]
 [include:../../../../../dom/voicemail/test/marionette/manifest.ini]
 [include:../../../../../dom/battery/test/marionette/manifest.ini]
-[include:../../../../../dom/sms/tests/marionette/manifest.ini]
+[include:../../../../../dom/mobilemessage/tests/marionette/manifest.ini]
 [include:../../../../../dom/network/tests/marionette/manifest.ini]
 [include:../../../../../dom/system/gonk/tests/marionette/manifest.ini]
 [include:../../../../../dom/icc/tests/marionette/manifest.ini]
diff --git a/testing/mochitest/android.json b/testing/mochitest/android.json
index c47ba7570ad..d2bd48356a2 100644
--- a/testing/mochitest/android.json
+++ b/testing/mochitest/android.json
@@ -162,7 +162,7 @@
  "dom/indexedDB/test/test_webapp_clearBrowserData_oop_inproc.html": "No test app installed",
  "dom/network/tests/test_network_basics.html": "",
  "dom/permission/tests/test_permission_basics.html": "",
- "dom/sms/tests/test_sms_basics.html": "",
+ "dom/mobilemessage/tests/test_sms_basics.html": "",
  "dom/tests/mochitest/ajax/jquery/test_jQuery.html": "bug 775227",
  "dom/tests/mochitest/ajax/offline/test_simpleManifest.html": "TIMED_OUT",
  "dom/tests/mochitest/ajax/offline/test_updatingManifest.html": "TIMED_OUT",
diff --git a/testing/mochitest/b2g.json b/testing/mochitest/b2g.json
index 962f6b71967..781ec2e83ed 100644
--- a/testing/mochitest/b2g.json
+++ b/testing/mochitest/b2g.json
@@ -58,7 +58,7 @@
     "dom/imptests/webapps/DOMCore/tests/approved/test_Range-surroundContents.html":" Test timed out.",
     "dom/indexedDB/": "",
 
-    "dom/sms": "",
+    "dom/mobilemessage": "",
 
     "dom/media/tests/mochitest/test_getUserMedia_exceptions.html":" Exception for wrong object type as third parameter",
     "dom/network/tests/test_networkstats_basics.html":" uncaught exception - uncaught exception: 2147500033 at :0",
diff --git a/testing/xpcshell/xpcshell.ini b/testing/xpcshell/xpcshell.ini
index 271f966a769..03d783806ff 100644
--- a/testing/xpcshell/xpcshell.ini
+++ b/testing/xpcshell/xpcshell.ini
@@ -12,7 +12,7 @@
 [include:dom/activities/tests/unit/xpcshell.ini]
 [include:dom/encoding/test/unit/xpcshell.ini]
 [include:dom/plugins/test/unit/xpcshell.ini]
-[include:dom/sms/tests/xpcshell.ini]
+[include:dom/mobilemessage/tests/xpcshell.ini]
 [include:dom/mms/tests/xpcshell.ini]
 [include:dom/network/tests/unit/xpcshell.ini]
 [include:dom/network/tests/unit_ipc/xpcshell.ini]
diff --git a/testing/xpcshell/xpcshell_b2g.ini b/testing/xpcshell/xpcshell_b2g.ini
index be6eae453c2..b87183780eb 100644
--- a/testing/xpcshell/xpcshell_b2g.ini
+++ b/testing/xpcshell/xpcshell_b2g.ini
@@ -2,7 +2,7 @@
 ; License, v. 2.0. If a copy of the MPL was not distributed with this
 ; file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-[include:dom/sms/tests/xpcshell.ini]
+[include:dom/mobilemessage/tests/xpcshell.ini]
 [include:dom/mms/tests/xpcshell.ini]
 [include:dom/system/gonk/tests/xpcshell.ini]
 [include:toolkit/devtools/debugger/tests/unit/xpcshell.ini]
diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp
index 006917c0b05..b5a8a64bb3e 100644
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -22,7 +22,7 @@
 #include "mozilla/Preferences.h"
 #include "nsThreadUtils.h"
 #include "nsIThreadManager.h"
-#include "mozilla/dom/sms/PSms.h"
+#include "mozilla/dom/mobilemessage/PSms.h"
 #include "gfxImageSurface.h"
 #include "gfxContext.h"
 #include "nsPresContext.h"
@@ -1635,7 +1635,7 @@ AndroidBridge::SetURITitle(const nsAString& aURI, const nsAString& aTitle)
 
 nsresult
 AndroidBridge::GetSegmentInfoForText(const nsAString& aText,
-                                     dom::sms::SmsSegmentInfoData* aData)
+                                     dom::mobilemessage::SmsSegmentInfoData* aData)
 {
     ALOG_BRIDGE("AndroidBridge::GetSegmentInfoForText");
 
@@ -1724,7 +1724,7 @@ AndroidBridge::DeleteMessage(int32_t aMessageId, nsISmsRequest* aRequest)
 }
 
 void
-AndroidBridge::CreateMessageList(const dom::sms::SmsFilterData& aFilter, bool aReverse,
+AndroidBridge::CreateMessageList(const dom::mobilemessage::SmsFilterData& aFilter, bool aReverse,
                                  nsISmsRequest* aRequest)
 {
     ALOG_BRIDGE("AndroidBridge::CreateMessageList");
diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h
index e1597474119..5043111b4d4 100644
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -62,10 +62,10 @@ class NetworkInformation;
 } // namespace hal
 
 namespace dom {
-namespace sms {
+namespace mobilemessage {
 struct SmsFilterData;
 struct SmsSegmentInfoData;
-} // namespace sms
+} // namespace mobilemessage
 } // namespace dom
 
 namespace layers {
@@ -314,11 +314,11 @@ public:
     void DisableBatteryNotifications();
     void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo);
 
-    nsresult GetSegmentInfoForText(const nsAString& aText, dom::sms::SmsSegmentInfoData* aData);
+    nsresult GetSegmentInfoForText(const nsAString& aText, dom::mobilemessage::SmsSegmentInfoData* aData);
     void SendMessage(const nsAString& aNumber, const nsAString& aText, nsISmsRequest* aRequest);
     void GetMessage(int32_t aMessageId, nsISmsRequest* aRequest);
     void DeleteMessage(int32_t aMessageId, nsISmsRequest* aRequest);
-    void CreateMessageList(const dom::sms::SmsFilterData& aFilter, bool aReverse, nsISmsRequest* aRequest);
+    void CreateMessageList(const dom::mobilemessage::SmsFilterData& aFilter, bool aReverse, nsISmsRequest* aRequest);
     void GetNextMessageInList(int32_t aListId, nsISmsRequest* aRequest);
     void ClearMessageList(int32_t aListId);
     already_AddRefed DequeueSmsRequest(uint32_t aRequestId);
diff --git a/widget/android/AndroidJNI.cpp b/widget/android/AndroidJNI.cpp
index fcaca57cb5d..b25c753fca6 100644
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -30,17 +30,18 @@
 
 #include "mozilla/unused.h"
 
-#include "mozilla/dom/sms/SmsMessage.h"
-#include "mozilla/dom/sms/Constants.h"
-#include "mozilla/dom/sms/Types.h"
-#include "mozilla/dom/sms/PSms.h"
-#include "mozilla/dom/sms/SmsParent.h"
+#include "mozilla/dom/SmsMessage.h"
+#include "mozilla/dom/mobilemessage/Constants.h"
+#include "mozilla/dom/mobilemessage/Types.h"
+#include "mozilla/dom/mobilemessage/PSms.h"
+#include "mozilla/dom/mobilemessage/SmsParent.h"
 #include "nsIMobileMessageDatabaseService.h"
 #include "nsPluginInstanceOwner.h"
 #include "nsSurfaceTexture.h"
 
 using namespace mozilla;
-using namespace mozilla::dom::sms;
+using namespace mozilla::dom;
+using namespace mozilla::dom::mobilemessage;
 
 /* Forward declare all the JNI methods as extern "C" */
 

From e53c24efbd2a88f29fb9eeff54c73fe0410352d7 Mon Sep 17 00:00:00 2001
From: Jonathan Kew 
Date: Fri, 1 Mar 2013 13:40:10 +0000
Subject: [PATCH 104/140] bug 846690 - add a memory-pressure notification
 observer to the hyphenation manager, to discard dictionaries when we're low
 on memory. r=smontagu

---
 .../hyphenation/public/nsHyphenationManager.h |  8 +++++
 intl/hyphenation/src/nsHyphenationManager.cpp | 36 +++++++++++++++++--
 2 files changed, 41 insertions(+), 3 deletions(-)

diff --git a/intl/hyphenation/public/nsHyphenationManager.h b/intl/hyphenation/public/nsHyphenationManager.h
index f54e8da7b97..55f26e5404a 100644
--- a/intl/hyphenation/public/nsHyphenationManager.h
+++ b/intl/hyphenation/public/nsHyphenationManager.h
@@ -41,6 +41,7 @@
 #include "nsInterfaceHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsHashKeys.h"
+#include "nsIObserver.h"
 #include "mozilla/Omnijar.h"
 
 class nsHyphenator;
@@ -62,6 +63,13 @@ private:
   ~nsHyphenationManager();
 
 protected:
+  class MemoryPressureObserver MOZ_FINAL : public nsIObserver
+  {
+  public:
+      NS_DECL_ISUPPORTS
+      NS_DECL_NSIOBSERVER
+  };
+
   void LoadPatternList();
   void LoadPatternListFromOmnijar(mozilla::Omnijar::Type aType);
   void LoadPatternListFromDir(nsIFile *aDir);
diff --git a/intl/hyphenation/src/nsHyphenationManager.cpp b/intl/hyphenation/src/nsHyphenationManager.cpp
index c8fbd6f26cf..bd05fee4102 100644
--- a/intl/hyphenation/src/nsHyphenationManager.cpp
+++ b/intl/hyphenation/src/nsHyphenationManager.cpp
@@ -48,18 +48,48 @@
 #include "nsUnicharUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsZipArchive.h"
+#include "mozilla/Services.h"
+#include "nsIObserverService.h"
+#include "nsCRT.h"
 
 using namespace mozilla;
 
-#define INTL_HYPHENATIONALIAS_PREFIX "intl.hyphenation-alias."
+static const char kIntlHyphenationAliasPrefix[] = "intl.hyphenation-alias.";
+static const char kMemoryPressureNotification[] = "memory-pressure";
 
 nsHyphenationManager *nsHyphenationManager::sInstance = nullptr;
 
+NS_IMPL_ISUPPORTS1(nsHyphenationManager::MemoryPressureObserver,
+                   nsIObserver)
+
+NS_IMETHODIMP
+nsHyphenationManager::MemoryPressureObserver::Observe(nsISupports *aSubject,
+                                                      const char *aTopic,
+                                                      const PRUnichar *aData)
+{
+  if (!nsCRT::strcmp(aTopic, kMemoryPressureNotification)) {
+    // We don't call Instance() here, as we don't want to create a hyphenation
+    // manager if there isn't already one in existence.
+    // (This observer class is local to the hyphenation manager, so it can use
+    // the protected members directly.)
+    if (nsHyphenationManager::sInstance) {
+      nsHyphenationManager::sInstance->mHyphenators.Clear();
+    }
+  }
+  return NS_OK;
+}
+
 nsHyphenationManager*
 nsHyphenationManager::Instance()
 {
   if (sInstance == nullptr) {
     sInstance = new nsHyphenationManager();
+
+    nsCOMPtr obs = mozilla::services::GetObserverService();
+    if (obs) {
+        obs->AddObserver(new MemoryPressureObserver,
+                         kMemoryPressureNotification, false);
+    }
   }
   return sInstance;
 }
@@ -296,14 +326,14 @@ nsHyphenationManager::LoadAliases()
   }
   uint32_t prefCount;
   char **prefNames;
-  nsresult rv = prefRootBranch->GetChildList(INTL_HYPHENATIONALIAS_PREFIX,
+  nsresult rv = prefRootBranch->GetChildList(kIntlHyphenationAliasPrefix,
                                              &prefCount, &prefNames);
   if (NS_SUCCEEDED(rv) && prefCount > 0) {
     for (uint32_t i = 0; i < prefCount; ++i) {
       nsAdoptingCString value = Preferences::GetCString(prefNames[i]);
       if (value) {
         nsAutoCString alias(prefNames[i]);
-        alias.Cut(0, strlen(INTL_HYPHENATIONALIAS_PREFIX));
+        alias.Cut(0, sizeof(kIntlHyphenationAliasPrefix) - 1);
         ToLowerCase(alias);
         ToLowerCase(value);
         nsCOMPtr aliasAtom = do_GetAtom(alias);

From 1aabec3bb3a1f9ae89925ff3d18ce42db47b5248 Mon Sep 17 00:00:00 2001
From: Jonathan Kew 
Date: Fri, 1 Mar 2013 13:41:30 +0000
Subject: [PATCH 105/140] bug 846732 - replace tri-license boilerplate with
 MPL2 in our hyphenation code. r=gerv

---
 .../hyphenation/public/nsHyphenationManager.h | 40 ++-----------------
 intl/hyphenation/public/nsHyphenator.h        | 40 ++-----------------
 intl/hyphenation/src/hnjalloc.h               | 40 ++-----------------
 intl/hyphenation/src/hnjstdio.cpp             | 40 ++-----------------
 intl/hyphenation/src/nsHyphenationManager.cpp | 40 ++-----------------
 intl/hyphenation/src/nsHyphenator.cpp         | 40 ++-----------------
 6 files changed, 24 insertions(+), 216 deletions(-)

diff --git a/intl/hyphenation/public/nsHyphenationManager.h b/intl/hyphenation/public/nsHyphenationManager.h
index 55f26e5404a..20ceceff38b 100644
--- a/intl/hyphenation/public/nsHyphenationManager.h
+++ b/intl/hyphenation/public/nsHyphenationManager.h
@@ -1,39 +1,7 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Hyphenation Service.
- *
- * The Initial Developer of the Original Code is
- * Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Jonathan Kew 
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsHyphenationManager_h__
 #define nsHyphenationManager_h__
diff --git a/intl/hyphenation/public/nsHyphenator.h b/intl/hyphenation/public/nsHyphenator.h
index d2e79f99a6a..e3919f98ab9 100644
--- a/intl/hyphenation/public/nsHyphenator.h
+++ b/intl/hyphenation/public/nsHyphenator.h
@@ -1,39 +1,7 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Hyphenation Service.
- *
- * The Initial Developer of the Original Code is
- * Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Jonathan Kew 
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsHyphenator_h__
 #define nsHyphenator_h__
diff --git a/intl/hyphenation/src/hnjalloc.h b/intl/hyphenation/src/hnjalloc.h
index d678a7dd638..a5e7693227e 100644
--- a/intl/hyphenation/src/hnjalloc.h
+++ b/intl/hyphenation/src/hnjalloc.h
@@ -1,39 +1,7 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Foundation code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2005-2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Jonathan Kew 
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Simple replacement for hnjalloc.h from libhyphen-2.x, to use moz_x* memory
diff --git a/intl/hyphenation/src/hnjstdio.cpp b/intl/hyphenation/src/hnjstdio.cpp
index 2bc0fb43bdb..d07bd14de6a 100644
--- a/intl/hyphenation/src/hnjstdio.cpp
+++ b/intl/hyphenation/src/hnjstdio.cpp
@@ -1,39 +1,7 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Hyphenation Service.
- *
- * The Initial Developer of the Original Code is
- * Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Jonathan Kew 
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This file provides substitutes for the basic stdio routines used by hyphen.c
 // to read its dictionary files. We #define the stdio names to these versions
diff --git a/intl/hyphenation/src/nsHyphenationManager.cpp b/intl/hyphenation/src/nsHyphenationManager.cpp
index bd05fee4102..e4d37433cb0 100644
--- a/intl/hyphenation/src/nsHyphenationManager.cpp
+++ b/intl/hyphenation/src/nsHyphenationManager.cpp
@@ -1,39 +1,7 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Hyphenation Service.
- *
- * The Initial Developer of the Original Code is
- * Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Jonathan Kew 
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsHyphenationManager.h"
 #include "nsHyphenator.h"
diff --git a/intl/hyphenation/src/nsHyphenator.cpp b/intl/hyphenation/src/nsHyphenator.cpp
index a4bf977edc9..932dfa1cc66 100644
--- a/intl/hyphenation/src/nsHyphenator.cpp
+++ b/intl/hyphenation/src/nsHyphenator.cpp
@@ -1,39 +1,7 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Hyphenation Service.
- *
- * The Initial Developer of the Original Code is
- * Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Jonathan Kew 
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsHyphenator.h"
 #include "nsIFile.h"

From d8f02f9dc32f024bb811c7c895584f850c044173 Mon Sep 17 00:00:00 2001
From: Josh Matthews 
Date: Fri, 1 Mar 2013 09:08:53 -0500
Subject: [PATCH 106/140] Bug 844561 - Avoid prompting about closing private
 windows unnecessarily. r=ehsan

---
 browser/base/content/browser.js                      |  9 +++++----
 browser/base/content/test/Makefile.in                |  1 +
 .../base/content/test/browser_private_no_prompt.js   | 12 ++++++++++++
 3 files changed, 18 insertions(+), 4 deletions(-)
 create mode 100644 browser/base/content/test/browser_private_no_prompt.js

diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 257ea2ce3ea..0f386835bdd 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6409,20 +6409,20 @@ function warnAboutClosingWindow() {
   // Figure out if there's at least one other browser window around.
   let e = Services.wm.getEnumerator("navigator:browser");
   let otherPBWindowExists = false;
-  let warnAboutClosingTabs = false;
+  let nonPopupPresent = false;
   while (e.hasMoreElements()) {
     let win = e.getNext();
     if (win != window) {
       if (isPBWindow && PrivateBrowsingUtils.isWindowPrivate(win))
         otherPBWindowExists = true;
       if (win.toolbar.visible)
-        warnAboutClosingTabs = true;
+        nonPopupPresent = true;
       // If the current window is not in private browsing mode we don't need to 
       // look for other pb windows, we can leave the loop when finding the 
       // first non-popup window. If however the current window is in private 
       // browsing mode then we need at least one other pb and one non-popup 
       // window to break out early.
-      if ((!isPBWindow || otherPBWindowExists) && warnAboutClosingTabs)
+      if ((!isPBWindow || otherPBWindowExists) && nonPopupPresent)
         break;
     }
   }
@@ -6437,7 +6437,8 @@ function warnAboutClosingWindow() {
     if (exitingCanceled.data)
       return false;
   }
-  if (warnAboutClosingTabs)
+
+  if (!isPBWindow && nonPopupPresent)
     return gBrowser.warnAboutClosingTabs(true);
 
   let os = Services.obs;
diff --git a/browser/base/content/test/Makefile.in b/browser/base/content/test/Makefile.in
index 9158dc26090..d45aa5b8548 100644
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -315,6 +315,7 @@ _BROWSER_FILES = \
                  feed_tab.html \
                  browser_pluginCrashCommentAndURL.js \
                  pluginCrashCommentAndURL.html \
+                 browser_private_no_prompt.js \
                  $(NULL)
 
 ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
diff --git a/browser/base/content/test/browser_private_no_prompt.js b/browser/base/content/test/browser_private_no_prompt.js
new file mode 100644
index 00000000000..1635eddf54e
--- /dev/null
+++ b/browser/base/content/test/browser_private_no_prompt.js
@@ -0,0 +1,12 @@
+function test() {
+  waitForExplicitFinish();
+  var privateWin = OpenBrowserWindow({private: true});
+  privateWin.addEventListener("load", function onload() {
+    privateWin.removeEventListener("load", onload, false);
+
+    privateWin.BrowserOpenTab();
+    privateWin.BrowserTryToCloseWindow();
+    ok(true, "didn't prompt");
+    finish();
+  }, false);
+}
\ No newline at end of file

From eb2c24812787843d95ad9d1c064ee2e289ff6573 Mon Sep 17 00:00:00 2001
From: "Carl X. Su" 
Date: Fri, 1 Mar 2013 05:22:56 +0800
Subject: [PATCH 107/140] Bug 365367 - NS_CONSOLEMESSAGE_CONTRACTID removed
 because it is never used. r=bsmedberg

---
 xpcom/base/nsIConsoleMessage.idl | 2 --
 1 file changed, 2 deletions(-)

diff --git a/xpcom/base/nsIConsoleMessage.idl b/xpcom/base/nsIConsoleMessage.idl
index 87039fdaabe..360e5360ac0 100644
--- a/xpcom/base/nsIConsoleMessage.idl
+++ b/xpcom/base/nsIConsoleMessage.idl
@@ -19,6 +19,4 @@ interface nsIConsoleMessage : nsISupports
 %{ C++
 #define NS_CONSOLEMESSAGE_CID \
 { 0x56c9d666, 0x1dd2, 0x11b2, { 0xb4, 0x3c, 0xa8, 0x4b, 0xf3, 0xb3, 0xec, 0xbb }}
-
-#define NS_CONSOLEMESSAGE_CONTRACTID "@mozilla.org/consolemessage;1"
 %}

From 15276e6154371dd914e873f1495df53403971614 Mon Sep 17 00:00:00 2001
From: Paul Adenot 
Date: Fri, 1 Mar 2013 15:18:58 +0100
Subject: [PATCH 108/140] Bug 634747 - Expect some assertions on windows on
 test_seek.html while we investigate. r=edmorley

---
 content/media/test/test_seek.html | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/content/media/test/test_seek.html b/content/media/test/test_seek.html
index 37fbc520c27..f0cc54a2183 100644
--- a/content/media/test/test_seek.html
+++ b/content/media/test/test_seek.html
@@ -25,6 +25,11 @@
 
 var manager = new MediaTestManager;
 
+// https://bugzilla.mozilla.org/show_bug.cgi?id=634747
+if (navigator.platform.startsWith("Win")) {
+  SimpleTest.expectAssertions(0, 5);
+}
+
 const NUM_SEEK_TESTS = 13;
 
 function createTestArray() {

From 484e0884902c5842cabfead288eb0c281a294bcb Mon Sep 17 00:00:00 2001
From: Henri Sivonen 
Date: Fri, 1 Mar 2013 16:42:39 +0200
Subject: [PATCH 109/140] Bug 844792 addendum - Adjust advice about multipart
 support removal in XHR and make the message non-localizable. r=smaug.

---
 content/base/src/nsXMLHttpRequest.cpp       | 9 ++++++++-
 dom/locales/en-US/chrome/dom/dom.properties | 1 -
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp
index b596cbe248b..47ca511a3ab 100644
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -3360,7 +3360,14 @@ nsXMLHttpRequest::SetMultipart(bool aMultipart, nsresult& aRv)
     return;
   }
 
-  LogMessage("MultipartXHRWarning", GetOwner());
+  nsCOMPtr window = GetOwner();
+  nsCOMPtr doc;
+  if (window) {
+    doc = do_QueryInterface(window->GetExtantDocument());
+  }
+  nsContentUtils::ReportToConsoleNonLocalized(
+    NS_LITERAL_STRING("Support for multipart responses in XMLHttpRequest is going to be removed in an upcoming version. Please migrate to checking the responseText from progress events, to Server-Sent Events or to Web Sockets."),
+    nsIScriptError::warningFlag, "DOM", doc);
 
   if (aMultipart) {
     mState |= XML_HTTP_REQUEST_MULTIPART;
diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties
index 550eb4d1847..78b811d50c9 100644
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -83,7 +83,6 @@ RemovedFullScreenElement=Exited full-screen because full-screen element was remo
 FocusedWindowedPluginWhileFullScreen=Exited full-screen because windowed plugin was focused.
 HTMLMultipartXHRWarning=HTML parsing in XMLHttpRequest is not supported for multipart responses.
 HTMLSyncXHRWarning=HTML parsing in XMLHttpRequest is not supported in the synchronous mode.
-MultipartXHRWarning=Support for multipart responses in XMLHttpRequest is going to be removed in an upcoming version. Please migrate to chunked responses or to Web Sockets.
 InvalidRedirectChannelWarning=Unable to redirect to %S because the channel doesn't implement nsIWritablePropertyBag2.
 ReportOnlyCSPIgnored=Report-only CSP policy will be ignored because there are other non-report-only CSP policies applied.
 ResponseTypeSyncXHRWarning=Use of XMLHttpRequest's responseType attribute is no longer supported in the synchronous mode in window context.

From 0e0562fc36d81464adf602522a5f49cbb8027ed4 Mon Sep 17 00:00:00 2001
From: Tom Schuster 
Date: Thu, 28 Feb 2013 13:46:42 +0100
Subject: [PATCH 110/140] Bug 661961 - change about:plugins to add the path for
 every extension. r=bsmedberg

---
 dom/locales/en-US/chrome/plugins.properties   |  1 +
 toolkit/content/plugins.html                  | 13 +++++++++++--
 toolkit/mozapps/extensions/PluginProvider.jsm |  7 +++++++
 3 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/dom/locales/en-US/chrome/plugins.properties b/dom/locales/en-US/chrome/plugins.properties
index da1a6886676..123896c3df4 100644
--- a/dom/locales/en-US/chrome/plugins.properties
+++ b/dom/locales/en-US/chrome/plugins.properties
@@ -11,6 +11,7 @@ enabledplugins_label=Enabled plugins
 nopluginsareenabled_label=No enabled plugins found
 findpluginupdates_label=Find updates for installed plugins at
 file_label=File:
+path_label=Path:
 version_label=Version:
 state_label=State:
 mimetype_label=MIME Type
diff --git a/toolkit/content/plugins.html b/toolkit/content/plugins.html
index da68fa4c259..86620975046 100644
--- a/toolkit/content/plugins.html
+++ b/toolkit/content/plugins.html
@@ -95,9 +95,9 @@
      "STATE_OUTDATED",
      "STATE_VULNERABLE_UPDATE_AVAILABLE",
      "STATE_VULNERABLE_NO_UPDATE"].forEach(function(label) {
-      stateNames[Ci.nsIBlocklistService[label]] = label;  
+      stateNames[Ci.nsIBlocklistService[label]] = label;
     });
-    
+
     for (var i = 0; i < aPlugins.length; i++) {
       var plugin = aPlugins[i];
       if (plugin) {
@@ -119,6 +119,15 @@
         fileDd.appendChild(document.createTextNode(plugin.pluginLibraries));
         dl.appendChild(fileDd);
 
+        // "Path: /usr/lib/mozilla/plugins/libtotem-cone-plugin.so"
+        var pathDd = document.createElement("dd");
+        var path = document.createElement("span");
+        path.setAttribute("class", "label");
+        path.appendChild(document.createTextNode(pluginsbundle.GetStringFromName("path_label") + " "));
+        pathDd.appendChild(path);
+        pathDd.appendChild(document.createTextNode(plugin.pluginFullpath));
+        dl.appendChild(pathDd);
+
         // "Version: "
         var versionDd = document.createElement("dd");
         var version = document.createElement("span");
diff --git a/toolkit/mozapps/extensions/PluginProvider.jsm b/toolkit/mozapps/extensions/PluginProvider.jsm
index ec4457547b3..fb85e12d868 100644
--- a/toolkit/mozapps/extensions/PluginProvider.jsm
+++ b/toolkit/mozapps/extensions/PluginProvider.jsm
@@ -353,6 +353,13 @@ function PluginWrapper(aId, aName, aDescription, aTags) {
     return libs;
   });
 
+  this.__defineGetter__("pluginFullpath", function() {
+    let paths = [];
+    for (let tag of aTags)
+      paths.push(tag.fullpath);
+    return paths;
+  })
+
   this.__defineGetter__("pluginMimeTypes", function() {
     let types = [];
     for (let tag of aTags)

From 6ddfa9e20ac3f60da26398ac6148c2a7d8ac23e1 Mon Sep 17 00:00:00 2001
From: Tom Schuster 
Date: Thu, 28 Feb 2013 13:46:50 +0100
Subject: [PATCH 111/140] Bug 844505 - Remove enablePrivilege from
 test_form_autocomplete.html. r=mounir

---
 .../satchel/test/test_form_autocomplete.html         | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/toolkit/components/satchel/test/test_form_autocomplete.html b/toolkit/components/satchel/test/test_form_autocomplete.html
index 15c9d8b760d..96b3dfb030b 100644
--- a/toolkit/components/satchel/test/test_form_autocomplete.html
+++ b/toolkit/components/satchel/test/test_form_autocomplete.html
@@ -117,13 +117,11 @@ Form History test: form field autocomplete
 
 /** Test for Form History autocomplete **/
 
-netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-
 var input = $_(1, "field1");
-const shiftModifier = Components.interfaces.nsIDOMEvent.SHIFT_MASK;
+const shiftModifier = Event.SHIFT_MASK;
 
 // Get the form history service
-var fh = Components.classes["@mozilla.org/satchel/form-history;1"].
+var fh = SpecialPowers.Cc["@mozilla.org/satchel/form-history;1"].
          getService(Components.interfaces.nsIFormHistory2);
 ok(fh != null, "got form history service");
 
@@ -194,8 +192,6 @@ function checkForm(expectedValue) {
  * timeout.
  */
 function runTest(testNum) {
-  // Seems we need to enable this again, or sendKeyEvent() complaints.
-  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
   ok(true, "Starting test #" + testNum);
 
   switch(testNum) {
@@ -375,9 +371,7 @@ function runTest(testNum) {
 
         // On OS X, shift-backspace and shift-delete work, just delete does not.
         // On Win/Linux, shift-backspace does not work, delete and shift-delete do.
-        var osString = Components.classes["@mozilla.org/xre/app-info;1"].
-                       getService(Components.interfaces.nsIXULRuntime).OS;
-        if (osString == "Darwin")
+        if (SpecialPowers.OS == "Darwin")
           doKey("back_space", shiftModifier);
         else
           doKey("delete", shiftModifier);

From 7b58c017fe0e158f88fd22808670f3416df44743 Mon Sep 17 00:00:00 2001
From: Tom Schuster 
Date: Thu, 28 Feb 2013 13:50:24 +0100
Subject: [PATCH 112/140] Bug 836949 - Remove E4X only unicode flags. r=jwalden

---
 js/src/vm/Unicode.cpp     | 1037 ++++++++++++++++---------------------
 js/src/vm/Unicode.h       |    5 +-
 js/src/vm/make_unicode.py |   20 +-
 3 files changed, 470 insertions(+), 592 deletions(-)

diff --git a/js/src/vm/Unicode.cpp b/js/src/vm/Unicode.cpp
index e61916eb1a5..42e6ba3fb08 100644
--- a/js/src/vm/Unicode.cpp
+++ b/js/src/vm/Unicode.cpp
@@ -14,17 +14,17 @@ using namespace js::unicode;
  * First let's have a look at a jschar, 16-bits:
  *              [................]
  * Step 1:
- *  Extracting the upper 10 bits from the jschar.
- *   upper = char >> 6 ([**********......])
+ *  Extracting the upper 11 bits from the jschar.
+ *   upper = char >>  5 ([***********.....])
  * Step 2:
  *  Using these bits to get an reduced index from index1.
  *   index = index1[upper]
  * Step 3:
- *  Combining the index and the bottom 6 bits of the orginial jschar.
- *   real_index = index2[(index << 6) + (jschar & 0x3f)] ([..********++++++])
+ *  Combining the index and the bottom 5 bits of the original jschar.
+ *   real_index = index2[(index << 5) + (char & ((1 << 5) - 1))] ([...********+++++])
  *
- * The advantage here is that the bigest number in index1 doesn't need 10 bits,
- * but 8 and we save some memory.
+ * The advantage here is that the biggest number in index1 doesn't need 10 bits,
+ * but 7 and we save some memory.
  *
  * Step 4:
  *  Get the character informations by looking up real_index in js_charinfo.
@@ -147,12 +147,10 @@ const CharacterInfo unicode::js_charinfo[] = {
     {65440, 0, 2},
     {0, 65529, 2},
     {0, 80, 2},
-    {0, 0, 16},
     {0, 15, 2},
     {65521, 0, 2},
     {0, 48, 2},
     {65488, 0, 2},
-    {0, 0, 36},
     {0, 7264, 2},
     {42877, 7545, 10},
     {3814, 0, 2},
@@ -198,63 +196,120 @@ const CharacterInfo unicode::js_charinfo[] = {
 };
 
 const uint8_t unicode::index1[] = {
-      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,
-     18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,
-     36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,
-     54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,  65,  66,  67,  26,  26,  26,  26,
-     26,  68,  69,  70,  71,  72,  73,  74,  75,  26,  26,  26,  26,  26,  26,  26,  26,  76,
-     77,  78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,
-     95,  96,  97,  98,  99, 100,  94, 101,  26, 102,  26, 103, 104, 104, 105, 104, 106, 107,
-    108, 109, 110, 111, 112, 113, 114, 115, 116,  94,  94,  94,  94,  94,  94,  94,  94,  94,
-     94,  94, 117, 118,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
-     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94, 119, 120, 104, 121,
-    122, 123, 124, 125, 126,  94,  94,  94,  94,  94,  94,  94, 127,  75, 128, 129, 130,  26,
-    131, 132,  94,  94,  94,  94,  94,  94,  94,  94,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  86,  94,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26, 133,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26, 133, 134,  26,  26,  26,  26, 135, 136,
-    137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
-    155,  94,  94, 156,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,
-     26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26,  26, 157, 158,
-     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
-     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
-     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
-     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
-     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
-     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
-     94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,  94,
-     94,  94,  94,  94,  94,  94,  26,  26,  26,  26,  26, 159,  26, 160, 161, 162, 163, 164,
-     26,  26,  26,  26, 165, 166, 167, 168, 169, 170,  26, 171, 172, 173, 174, 175,
+      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15,   8,  16,
+     17,  18,  19,  20,  21,  22,  23,  23,  24,  25,  26,  27,  28,  29,  30,  31,  32,   8,
+     33,   8,  34,   8,   8,  35,  36,  37,  38,  39,  40,  41,  42,  20,  43,  44,  20,  20,
+     45,  46,  47,  48,  49,  20,  20,  50,  51,  52,  53,  54,  55,   4,   4,  56,   4,  57,
+     58,  59,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  62,  71,
+     74,  75,  76,  77,  78,  79,  80,  81,  82,  73,  83,  84,  85,  86,  83,  87,  88,  89,
+     90,  91,  92,  93,  94,  95,  96,   4,  97,  98,  99,   4, 100, 101, 102, 103, 104, 105,
+    106,   4,  20, 107, 108, 109, 110, 111, 112, 113,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20, 114,  20, 115, 116, 117,  20, 118,  20, 119,   4, 120,  20,  20, 121,  94,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20, 122,
+    123,  20,  20, 124, 125, 126, 127, 128,  20, 129, 130, 131, 132,  20,  20, 133,  20, 134,
+     20, 135, 136, 137, 138, 139,  20, 140, 141,   4, 142,  20, 143, 144, 145, 146,   4,   4,
+    147, 129, 148, 149, 150, 151,  20, 152,  20, 153, 154, 155,   4,   4, 156, 157,  20,  20,
+     20, 158,  20,  20,  23, 159,   8,   8,   8,   8, 160,   8,   8,   8, 161, 162, 163, 164,
+    162, 165, 166, 167, 168, 169, 170, 171, 172,   4, 173, 174, 175, 176, 177, 178, 179,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4, 180, 181, 182,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4, 183, 184, 185, 186,   8,   8,   8, 187,
+    188, 189,  20, 190, 191, 192, 192,  23,   4, 193,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4, 194, 195,  94,  20, 196,  94,  20, 113, 197, 198,  20,  20,
+    199, 200,   4, 201,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20, 135,   4,   4,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+    202,   4,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20, 202,   4, 201, 155,  20,  20,  20,  20,  20,  20,  20,  20, 203, 204,   8, 205,
+    206,  20,  20, 207, 208, 209,   8, 210, 211, 212,   4, 213, 214, 215,  20, 216, 217, 129,
+    218, 219,  51, 220, 221, 136,  58, 222, 223,   4,  20, 224, 225, 226,  20, 227, 228, 229,
+    230, 231,   4,   4,   4,   4,  20, 232,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20,  20, 233, 234, 235,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,  20,  20,  20,  20,  20,  20,
+     20,  20,  20,  20,  20, 236,  20,  20, 237,   4, 238, 239, 240,  20,  20, 241, 242,  20,
+     20,  20,  20,  20,  20,  20,  20,  20,  20, 155, 201,  20, 243,  20, 244, 245, 246, 247,
+    248, 249,  20,  20,  20, 250, 251,   2,   3, 252,  20, 253, 254,   4,
 };
 
 const uint8_t unicode::index2[] = {
@@ -286,74 +341,58 @@ const uint8_t unicode::index2[] = {
       5,   5,  33,  34,  35,  33,  34,  35,  33,  34,  35,   8,   9,   8,   9,   8,   9,   8,
       9,   8,   9,   8,   9,   8,   9,   8,   9,  36,   8,   9,   8,   9,   8,   9,   8,   9,
       8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   5,  33,  34,  35,   8,   9,  37,  38,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,  39,   5,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   5,   5,   5,   5,   5,   5,  40,   8,   9,  41,  42,  43,
-     43,   8,   9,  44,  45,  46,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,  47,  48,
-     49,  50,  51,   5,  52,  52,   5,  53,   5,  54,   5,   5,   5,   5,  52,   5,   5,  55,
-      5,  56,  57,   5,  58,  59,   5,  60,   5,   5,   5,  59,   5,  61,  62,   5,   5,  63,
-      5,   5,   5,   5,   5,   5,   5,  64,   5,   5,  65,   5,   5,  65,   5,   5,   5,   5,
-     65,  66,  67,  67,  68,   5,   5,   5,   5,   5,  69,   5,   5,   5,   5,   5,   5,   5,
+      8,   9,   8,   9,   8,   9,   8,   9,  39,   5,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   5,   5,   5,   5,   5,   5,  40,   8,
+      9,  41,  42,  43,  43,   8,   9,  44,  45,  46,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,  47,  48,  49,  50,  51,   5,  52,  52,   5,  53,   5,  54,   5,   5,   5,   5,
+     52,   5,   5,  55,   5,  56,  57,   5,  58,  59,   5,  60,   5,   5,   5,  59,   5,  61,
+     62,   5,   5,  63,   5,   5,   5,   5,   5,   5,   5,  64,   5,   5,  65,   5,   5,  65,
+      5,   5,   5,   5,  65,  66,  67,  67,  68,   5,   5,   5,   5,   5,  69,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
-      5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   5,   0,   5,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,
+      5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   5,   0,   5,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,  70,
       2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
       2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,  70,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   8,   9,
-      8,   9,   5,   0,   8,   9,   0,   0,   5,  27,  27,  27,   0,   0,   0,   0,   0,   0,
-      0,   0,  71,   0,  72,  72,  72,   0,  73,   0,  74,  74,   5,   3,   3,   3,   3,   3,
-      3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   0,   3,   3,   3,   3,   3,
-      3,   3,   3,   3,  75,  76,  76,  76,   5,   4,   4,   4,   4,   4,   4,   4,   4,   4,
-      4,   4,   4,   4,   4,   4,   4,   4,  77,   4,   4,   4,   4,   4,   4,   4,   4,   4,
-     78,  79,  79,  80,  81,  82,   5,   5,   5,  83,  84,  85,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-     86,  87,  88,   5,  89,  90,   0,   8,   9,  91,   8,   9,   5,  39,  39,  39,  92,  92,
-     92,  92,  92,  92,  92,  92,  92,  92,  92,  92,  92,  92,  92,  92,   3,   3,   3,   3,
+      2,   2,   2,   2,   2,   2,   8,   9,   8,   9,   5,   0,   8,   9,   0,   0,   5,  27,
+     27,  27,   0,   0,   0,   0,   0,   0,   0,   0,  71,   0,  72,  72,  72,   0,  73,   0,
+     74,  74,   5,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
+      3,   3,   0,   3,   3,   3,   3,   3,   3,   3,   3,   3,  75,  76,  76,  76,   5,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,  77,   4,
+      4,   4,   4,   4,   4,   4,   4,   4,  78,  79,  79,  80,  81,  82,   5,   5,   5,  83,
+     84,  85,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,  86,  87,  88,   5,  89,  90,   0,   8,   9,  91,
+      8,   9,   5,  39,  39,  39,  92,  92,  92,  92,  92,  92,  92,  92,  92,  92,  92,  92,
+     92,  92,  92,  92,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
       3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
-      3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   4,   4,   4,   4,   4,   4,   4,   4,
       4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
-      4,   4,   4,   4,   4,   4,  87,  87,  87,  87,  87,  87,  87,  87,  87,  87,  87,  87,
-     87,  87,  87,  87,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   0,   2,   2,   2,   2,   2,  93,  93,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,  94,   8,   9,   8,   9,   8,   9,   8,
-      9,   8,   9,   8,   9,   8,   9,  95,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   0,   0,   0,   0,   0,   0,   0,   0,   0,  96,  96,  96,
+      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,  87,  87,  87,  87,
+     87,  87,  87,  87,  87,  87,  87,  87,  87,  87,  87,  87,   8,   9,   0,   2,   2,   2,
+      2,   2,   0,   0,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,  93,   8,   9,   8,   9,   8,   9,   8,   9,   8,
+      9,   8,   9,   8,   9,  94,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,
+     95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,
+     95,  95,  95,  95,  95,   0,   0,   5,   0,   0,   0,   0,   0,   0,   0,  96,  96,  96,
      96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,
-     96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,   0,
-      0,   5,   0,   0,   0,   0,   0,   0,   0,  97,  97,  97,  97,  97,  97,  97,  97,  97,
-     97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,
-     97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,   5,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+     96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,   5,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,
       2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   2,   0,   2,   2,   0,
-      2,   2,   0,   2,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      0,   2,   0,   2,   2,   0,   2,   2,   0,   2,   0,   0,   0,   0,   0,   0,   0,   0,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   0,   0,   0,   0,   0,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   5,   5,   5,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,
-      0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   0,   0,   0,   0,   5,   5,   2,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,
+      5,   5,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   0,   5,   2,   2,   2,   2,   2,   2,   2,   0,   0,   2,   2,   2,   2,   2,
       2,   5,   5,   2,   2,   0,   2,   2,   2,   2,   5,   5,   2,   2,   2,   2,   2,   2,
@@ -363,364 +402,268 @@ const uint8_t unicode::index2[] = {
       5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
       2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,   0,   0,   0,   0,   5,   0,
+      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   5,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   5,   2,   2,   2,   5,   2,   2,   2,   2,   2,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   2,   2,   2,   0,   0,   0,   0,   5,   0,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   0,   2,   2,   2,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   5,   5,   0,   0,   0,   0,   5,   0,   0,   0,   0,   0,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      2,   2,   2,   2,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   2,   2,   2,
-      5,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   0,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,
-      2,   2,   2,  98,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   2,  98,   2,   5,  98,  98,  98,   2,   2,   2,   2,   2,   2,   2,
-      2,  98,  98,  98,  98,   2,  98,  98,   5,   2,   2,   2,   2,   2,   2,   2,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,
-      5,   5,   0,   2,  98,  98,   0,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   5,
-      5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   0,
-      0,   0,   5,   5,   5,   5,   0,   0,   2,   5,  98,  98,  98,   2,   2,   2,   2,   0,
-      0,  98,  98,   0,   0,  98,  98,   2,   5,   0,   0,   0,   0,   0,   0,   0,   0,  98,
-      0,   0,   0,   0,   5,   5,   0,   5,   5,   5,   2,   2,   0,   0,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   2,   2,  98,   0,   5,   5,   5,   5,   5,   5,   0,   0,   0,
+      5,   5,   5,   5,   5,   5,   2,   2,   2,   5,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   2,   2,   2,   2,   2,   2,   2,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   0,   0,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,
+      5,   5,   5,   5,   0,   2,   2,   2,   0,   5,   5,   5,   5,   5,   5,   5,   5,   0,
       0,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,
-      5,   5,   0,   5,   5,   0,   5,   5,   0,   0,   2,   0,  98,  98,  98,   2,   2,   0,
-      0,   0,   0,   2,   2,   0,   0,   2,   2,   2,   0,   0,   0,   2,   0,   0,   0,   0,
-      0,   0,   0,   5,   5,   5,   5,   0,   5,   0,   0,   0,   0,   0,   0,   0,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,   5,   2,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   2,   2,  98,   0,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   0,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   0,   0,   0,   5,   5,   5,   5,   0,   0,   2,   5,   2,   2,   2,   2,   2,   2,
+      2,   0,   0,   2,   2,   0,   0,   2,   2,   2,   5,   0,   0,   0,   0,   0,   0,   0,
+      0,   2,   0,   0,   0,   0,   5,   5,   0,   5,   5,   5,   2,   2,   0,   0,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   0,   5,   5,   5,   5,   5,   5,   0,
+      0,   0,   0,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,
-      5,   0,   5,   5,   0,   5,   5,   5,   5,   5,   0,   0,   2,   5,  98,  98,  98,   2,
-      2,   2,   2,   2,   0,   2,   2,  98,   0,  98,  98,   2,   0,   0,   5,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   2,   2,   0,   0,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   2,  98,  98,   0,   5,   5,   5,   5,   5,
-      5,   5,   5,   0,   0,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   0,   5,   5,   0,   5,   5,   0,   5,   5,   0,   0,   2,   0,   2,   2,   2,   2,
+      2,   0,   0,   0,   0,   2,   2,   0,   0,   2,   2,   2,   0,   0,   0,   2,   0,   0,
+      0,   0,   0,   0,   0,   5,   5,   5,   5,   0,   5,   0,   0,   0,   0,   0,   0,   0,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,   5,   2,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   0,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   0,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,
-      5,   5,   5,   0,   5,   5,   0,   5,   5,   5,   5,   5,   0,   0,   2,   5,  98,   2,
-     98,   2,   2,   2,   2,   0,   0,  98,  98,   0,   0,  98,  98,   2,   0,   0,   0,   0,
-      0,   0,   0,   0,   2,  98,   0,   0,   0,   0,   5,   5,   0,   5,   5,   5,   2,   2,
-      0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   5,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   5,   0,   5,   5,   5,
-      5,   5,   5,   0,   0,   0,   5,   5,   5,   0,   5,   5,   5,   5,   0,   0,   0,   5,
-      5,   0,   5,   0,   5,   5,   0,   0,   0,   5,   5,   0,   0,   0,   5,   5,   5,   0,
-      0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,
-     98,  98,   2,  98,  98,   0,   0,   0,  98,  98,  98,   0,  98,  98,  98,   2,   0,   0,
-      5,   0,   0,   0,   0,   0,   0,  98,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,  98,  98,   0,   5,
-      5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   0,   0,
-      0,   5,   2,   2,   2,  98,  98,  98,  98,   0,   2,   2,   2,   0,   2,   2,   2,   2,
-      0,   0,   0,   0,   0,   0,   0,   2,   2,   0,   5,   5,   0,   0,   0,   0,   0,   0,
-      5,   5,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,  98,
-      0,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   0,   5,   5,   5,   5,
+      5,   5,   5,   0,   5,   5,   0,   5,   5,   5,   5,   5,   0,   0,   2,   5,   2,   2,
+      2,   2,   2,   2,   2,   2,   0,   2,   2,   2,   0,   2,   2,   2,   0,   0,   5,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   2,   2,
+      0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   0,   0,   2,
+      2,   0,   0,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   0,   0,
+      0,   0,   5,   5,   0,   5,   5,   5,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   0,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   2,   5,   0,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,   5,
+      5,   0,   5,   5,   5,   5,   0,   0,   0,   5,   5,   0,   5,   0,   5,   5,   0,   0,
+      0,   5,   5,   0,   0,   0,   5,   5,   5,   0,   0,   0,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   2,   2,   2,   2,   2,   0,   0,   0,
+      2,   2,   2,   0,   2,   2,   2,   2,   0,   0,   5,   0,   0,   0,   0,   0,   0,   2,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   2,   2,   2,   0,   5,   5,   5,   5,   5,   5,   5,   5,   0,
+      5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   0,   5,   5,   5,   5,   5,   0,   0,   0,   5,   2,   2,   2,   2,   2,   2,
+      2,   0,   2,   2,   2,   0,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   2,
+      2,   0,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   0,   5,   5,   5,
+      5,   5,   5,   5,   5,   0,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   0,   0,   2,   5,
+      2,   2,   2,   2,   2,   2,   2,   0,   2,   2,   2,   0,   2,   2,   2,   2,   0,   0,
+      0,   0,   0,   0,   0,   2,   2,   0,   0,   0,   0,   0,   0,   0,   5,   0,   5,   5,
+      2,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   5,   5,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,
-      0,   0,   2,   5,  98,   2,  98,  98,  98,  98,  98,   0,   2,  98,  98,   0,  98,  98,
-      2,   2,   0,   0,   0,   0,   0,   0,   0,  98,  98,   0,   0,   0,   0,   0,   0,   0,
-      5,   0,   5,   5,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      0,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-     98,  98,   0,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   0,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   0,   0,   5,  98,  98,  98,   2,   2,   2,   2,   0,  98,  98,  98,   0,
-     98,  98,  98,   2,   5,   0,   0,   0,   0,   0,   0,   0,   0,  98,   0,   0,   0,   0,
+      5,   5,   5,   0,   0,   5,   2,   2,   2,   2,   2,   2,   2,   0,   2,   2,   2,   0,
+      2,   2,   2,   2,   5,   0,   0,   0,   0,   0,   0,   0,   0,   2,   0,   0,   0,   0,
       0,   0,   0,   0,   5,   5,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
       2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
-      0,   0,  98,  98,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      0,   0,   2,   2,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   0,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   0,
-      0,   0,   2,   0,   0,   0,   0,  98,  98,  98,   2,   2,   2,   0,   2,   0,  98,  98,
-     98,  98,  98,  98,  98,  98,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,  98,  98,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   2,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   0,   2,   0,   2,   2,
+      2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   5,   5,
       2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
       5,   2,   2,   2,   2,   2,   2,   2,   2,   0,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      2,   2,   0,   0,   0,   0,   0,   0,   0,   5,   5,   0,   5,   0,   0,   5,   5,   0,
+      5,   0,   0,   5,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   0,   5,   5,   5,
+      5,   5,   5,   5,   0,   5,   5,   5,   0,   5,   0,   5,   0,   0,   5,   5,   0,   5,
+      5,   5,   5,   2,   5,   5,   2,   2,   2,   2,   2,   2,   0,   2,   2,   5,   0,   0,
+      5,   5,   5,   5,   5,   0,   5,   0,   2,   2,   2,   2,   2,   2,   0,   0,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   5,   5,   5,   5,   5,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   5,   5,   0,   5,   0,   0,   5,   5,   0,   5,   0,   0,   5,
-      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,
-      0,   5,   5,   5,   0,   5,   0,   5,   0,   0,   5,   5,   0,   5,   5,   5,   5,   2,
-      5,   5,   2,   2,   2,   2,   2,   2,   0,   2,   2,   5,   0,   0,   5,   5,   5,   5,
-      5,   0,   5,   0,   2,   2,   2,   2,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   0,   0,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   0,   0,   0,   0,
-      0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   2,   0,   2,   0,   2,   0,   0,   0,   0,  98,  98,   5,   5,
-      5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      0,   0,   2,   2,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   0,   2,   0,   2,
+      0,   0,   0,   0,   2,   2,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,  98,   2,   2,   2,   2,   2,   0,   2,   2,   5,   5,
-      5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   2,   2,   2,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,
+      0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   0,   2,   2,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
       2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
+      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   5,   5,
+      5,   5,   5,   5,   2,   2,   2,   2,   5,   5,   5,   5,   2,   2,   2,   5,   2,   2,
+      2,   5,   5,   2,   2,   2,   2,   2,   2,   2,   5,   5,   5,   2,   2,   2,   2,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   0,   0,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,
+     97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,
+     97,  97,  97,  97,  97,  97,  97,  97,   0,  97,   0,   0,   0,   0,   0,  97,   0,   0,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,  98,
-     98,   2,   2,   2,   2,  98,   2,   2,   2,   2,   2,   2,  98,   2,   2,  98,  98,   2,
-      2,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,
-      5,   5,   5,   5,   5,   5,  98,  98,   2,   2,   5,   5,   5,   5,   2,   2,   2,   5,
-     98,  98,  98,   5,   5,  98,  98,  98,  98,  98,  98,  98,   5,   5,   5,   2,   2,   2,
-      2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,  98,  98,   2,
-      2,  98,  98,  98,  98,  98,  98,   2,   5,  98,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,  98,  98,  98,   2,   0,   0,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,
-     99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,  99,
-     99,  99,  99,  99,  99,  99,  99,  99,  99,  99,   0,  99,   0,   0,   0,   0,   0,  99,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   0,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   0,
+      5,   0,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,
+      5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   0,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   0,
+      5,   0,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,
       0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,
-      5,   0,   5,   0,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   0,   0,   2,   2,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   1,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,
-      5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   0,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   0,
-      5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   2,   2,   2,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   1,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,   5,
-      5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   2,   2,
-      2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   0,   0,   0,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,
-      5,   5,   5,   0,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,
-     98,   2,   2,   2,   2,   2,   2,   2,  98,  98,  98,  98,  98,  98,  98,  98,   2,  98,
-     98,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   5,   0,   0,
-      0,   0,   5,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   1,   0,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,
-      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   5,   0,   0,   0,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,
+      2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   0,   5,   5,   5,   0,   2,   2,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,
-      0,   0,   2,   2,   2,  98,  98,  98,  98,   2,   2,  98,  98,  98,   0,   0,   0,   0,
-     98,  98,   2,  98,  98,  98,  98,  98,  98,   2,   2,   2,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,
-      0,   0,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,
-     98,   5,   5,   5,   5,   5,   5,   5,  98,  98,   0,   0,   0,   0,   0,   0,   2,   2,
+      5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   0,   0,   0,   5,   0,   0,   0,   0,   5,   2,   0,   0,   2,   2,
       2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,  98,
-     98,  98,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,  98,   2,  98,   2,   2,   2,   2,   2,   2,   2,   0,   2,  98,
-      2,  98,  98,   2,   2,   2,   2,   2,   2,   2,   2,  98,  98,  98,  98,  98,  98,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,
-     98,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,  98,   2,   2,   2,   2,
-      2,  98,   2,  98,  98,  98,  98,  98,   2,  98,  98,   5,   5,   5,   5,   5,   5,   5,
-      0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,
-     98,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,  98,   2,   2,   2,   2,
-     98,  98,   2,   2,  98,   2,  98,  98,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,  98,   2,   2,  98,  98,  98,   2,
-     98,   2,   2,   2,  98,  98,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-     98,  98,  98,  98,  98,  98,  98,  98,   2,   2,   2,   2,   2,   2,   2,   2,  98,  98,
-      2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   0,   0,   0,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      2,   2,   2,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,  98,
-      2,   2,   2,   2,   2,   2,   2,   5,   5,   5,   5,   2,   5,   5,   5,   5,  98,  98,
-      2,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5, 100,   5,   5,
-      5, 101,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   5,   5,   5,   5,   5, 102,   5,   5,
-    103,   5,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9, 104, 104,
-    104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104,
-    104, 104,   0,   0, 105, 105, 105, 105, 105, 105,   0,   0, 104, 104, 104, 104, 104, 104,
-    104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104,
-    105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104,   0,   0, 105, 105,
-    105, 105, 105, 105,   0,   0,   5, 104,   5, 104,   5, 104,   5, 104,   0, 105,   0, 105,
-      0, 105,   0, 105, 104, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105,
-    105, 105, 106, 106, 107, 107, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111,   0,   0,
-    104, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104,
-    104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104,
-    104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104,   5, 112,   5,   0,
-      5,   5, 105, 105, 113, 113, 114,   0, 115,   0,   0,   0,   5, 112,   5,   0,   5,   5,
-    116, 116, 116, 116, 114,   0,   0,   0, 104, 104,   5,   5,   0,   0,   5,   5, 105, 105,
-    117, 117,   0,   0,   0,   0, 104, 104,   5,   5,   5,  88,   5,   5, 105, 105, 118, 118,
-     91,   0,   0,   0,   0,   0,   5, 112,   5,   0,   5,   5, 119, 119, 120, 120, 114,   0,
-      0,   0,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   0,   2,   2,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   1,   1,   0,   0,   0,   0,   0,   1,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,  93,  93,  93,  93,   2,  93,  93,  93,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   5,   0,   0,   0,   0,   5,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   0,   0,   0,   5,   5,   5,   5,   5,
-      0,   0,   0,   0,   0,   0,   5,   0, 121,   0,   5,   0, 122, 123,   5,   5,   0,   5,
-      5,   5, 124,   5,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   0,   0,
-      0,   0,   0,   5,   5,   5,   5,   5,   0,   0,   0,   0, 125,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 126, 126, 126, 126, 126, 126,
-    126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 127, 127, 127, 127, 127, 127, 127, 127,
-    127, 127, 127, 127, 127, 127, 127, 127,   5,   5,   5,   8,   9,   5,   5,   5,   5,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-    128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
-    128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
-    129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,
-     96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,
-     96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,   0,  97,  97,  97,  97,
-     97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,
-     97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,  97,
-     97,  97,  97,  97,  97,  97,  97,   0,   8,   9, 130, 131, 132, 133, 134,   8,   9,   8,
-      9,   8,   9, 135, 136, 137, 138,   5,   8,   9,   5,   8,   9,   5,   5,   5,   5,   5,
-      5,   5, 139, 139,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   5,   0,   0,   0,   0,   0,   0,   8,   9,   8,   9,   2,   2,   2,
-      8,   9,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 140, 140, 140, 140,
-    140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140,
-    140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140,   0, 140,
-      0,   0,   0,   0,   0, 140,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   5,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
-      5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,
-      5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,
-      5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,
-      5,   5,   5,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   1,   0,   0,   0,   0,   5,   5,   5,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,
-      2,   2,  98,  98,   0,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   0,
+      0,   0,   0,   0,   0,   2,   2,   2,   1,   0,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,
+      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   5,   0,   0,   0,
       0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   0,   0,   2,   2,   0,   0,   5,   5,   5,   0,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,
+      5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,   5,   5,   5,
+      5,   5,   2,   2,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,
+      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   2,   2,   2,   2,   2,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,
-      5,   5,   5,   5,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   0,   0,   0,   5,   5,   5,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   2,   2,   2,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,   5,   5,   2,   5,   5,
+      5,   5,   2,   2,   2,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,  98,   5,   5,   5,  99,   5,   5,   2,   2,   2,   2,   2,   2,
+      2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   2,   2,   2,   2,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   5,   5,   5,   5,   5, 100,
+      5,   5, 101,   5, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103,
+    103, 103, 102, 102, 102, 102, 102, 102,   0,   0, 103, 103, 103, 103, 103, 103,   0,   0,
+    102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 103, 103, 102, 102,
+    102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102,
+    102, 102,   0,   0, 103, 103, 103, 103, 103, 103,   0,   0,   5, 102,   5, 102,   5, 102,
+      5, 102,   0, 103,   0, 103,   0, 103,   0, 103, 102, 102, 102, 102, 102, 102, 102, 102,
+    103, 103, 103, 103, 103, 103, 103, 103, 104, 104, 105, 105, 105, 105, 106, 106, 107, 107,
+    108, 108, 109, 109,   0,   0, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103,
+    103, 103, 103, 103, 102, 102,   5, 110,   5,   0,   5,   5, 103, 103, 111, 111, 112,   0,
+    113,   0,   0,   0,   5, 110,   5,   0,   5,   5, 114, 114, 114, 114, 112,   0,   0,   0,
+    102, 102,   5,   5,   0,   0,   5,   5, 103, 103, 115, 115,   0,   0,   0,   0, 102, 102,
+      5,   5,   5,  88,   5,   5, 103, 103, 116, 116,  91,   0,   0,   0,   0,   0,   5, 110,
+      5,   0,   5,   5, 117, 117, 118, 118, 112,   0,   0,   0,   1,   1,   1,   1,   1,   1,
+      1,   1,   1,   1,   1,   0,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   1,   1,
+      0,   0,   0,   0,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   1,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   0,   0,   0,   0,   2,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   5,   0,   0,   0,   0,   5,   0,   0,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   0,   5,   0,   0,   0,   5,   5,   5,   5,   5,   0,   0,   0,   0,
+      0,   0,   5,   0, 119,   0,   5,   0, 120, 121,   5,   5,   0,   5,   5,   5, 122,   5,
+      5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   0,   0,   0,   0,   0,   5,
+      5,   5,   5,   5,   0,   0,   0,   0, 123,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
+    124, 124, 124, 124, 124, 124, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
+    125, 125, 125, 125,   5,   5,   5,   8,   9,   5,   5,   5,   5,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
+    126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 127, 127, 127, 127, 127, 127,
+    127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+    127, 127,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,
+     95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,
+     95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,  95,   0,
+     96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,
+     96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,
+     96,  96,  96,  96,  96,  96,  96,  96,  96,  96,  96,   0,   8,   9, 128, 129, 130, 131,
+    132,   8,   9,   8,   9,   8,   9, 133, 134, 135, 136,   5,   8,   9,   5,   8,   9,   5,
+      5,   5,   5,   5,   5,   5, 137, 137,   8,   9,   8,   9,   5,   0,   0,   0,   0,   0,
+      0,   8,   9,   8,   9,   2,   2,   2,   8,   9,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138,
+    138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138,
+    138, 138, 138, 138, 138, 138,   0, 138,   0,   0,   0,   0,   0, 138,   0,   0,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   5,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,
+      5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,
+      5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   1,   0,
+      0,   0,   0,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   0,   5,   5,   5,   5,   5,   0,   0,
+      5,   5,   5,   5,   5,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   2,   2,   0,
+      0,   5,   5,   5,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
@@ -728,162 +671,102 @@ const uint8_t unicode::index2[] = {
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   5,   2,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   0,   5,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   0,   0,   0,   0,   0,   0,   0,   2,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,
-      2,   2,   2,   2,   2,   2,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   8,   9,   8,   9,   8,   9,   8,   9,
+      0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   8,   9,   8,   9,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,   5,   5,   8,   9,   8,   9,   8,   9,   8,   9,
       8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   5,   2,  93,  93,  93,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      0,   5,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   0,   0,   0,   0,   0,   0,   0,   2,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      0,   0,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   5,   5,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,
-      8,   9,   8,   9,   8,   9,   8,   9,   5,   5,   5,   5,   5,   5,   5,   5,   5,   8,
-      9,   8,   9, 100,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   5,   0,   0,   8,
-      9,  56,   5,   0,   8,   9,   8,   9,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   8,   9,   8,   9,   8,   9,   8,   9,   8,   9,  57,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      8,   9,   8,   9,   5,   5,   5,   5,   5,   5,   5,   5,   5,   8,   9,   8,   9,  98,
+      8,   9,   8,   9,   8,   9,   8,   9,   8,   9,   5,   0,   0,   8,   9,  56,   5,   0,
+      8,   9,   8,   9,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   8,   9,
+      8,   9,   8,   9,   8,   9,   8,   9,  57,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   5,   5,   5,   2,   5,   5,   5,
       5,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,  98,  98,   2,   2,  98,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  98,  98,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,   0,   0,
+      0,   0,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,  98,  98,  98,  98,  98,  98,  98,  98,  98,  98,
-     98,  98,  98,  98,  98,  98,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   5,   5,
-      5,   5,   5,   5,   0,   0,   0,   5,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,
-      2,   2,   2,   2,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,
-      2,   2,   2,   2,  98,  98,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   2,   2,   2,  98,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,  98,  98,   2,   2,   2,   2,
-     98,  98,   2,  98,  98,  98,  98,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   2,   2,   2,   2,   2,   2,  98,  98,   2,   2,  98,  98,   2,   2,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   2,   5,   5,   5,   5,   5,   5,
-      5,   5,   2,  98,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,
-      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,  98,   0,   0,   0,   0,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   5,   2,   2,   2,   5,
-      5,   2,   2,   5,   5,   5,   5,   5,   2,   2,   5,   2,   5,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,  98,
-      2,   2,  98,  98,   0,   0,   5,   5,   5,  98,   2,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   0,
-      0,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   5,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,
+      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   2,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   0,   0,   2,   2,
+      2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,  98,  98,   2,  98,  98,   2,  98,
-     98,   0,  98,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,
-      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   0,   0,   0,   0,
-      0,   5,   2,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   0,   5,   0,
-      5,   5,   0,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   0,   0,   0,   5,   2,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   2,   5,   2,   2,   2,   5,   5,   2,   2,   5,
+      5,   5,   5,   5,   2,   2,   5,   2,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,
+      0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   2,   2,   2,   2,   2,
+      0,   0,   5,   5,   5,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,
+      5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,
+      5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,
+      5,   0,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   2,   2,   2,   2,   2,   2,   2,
+      2,   0,   2,   2,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,
+      0,   0,   0,   0,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,
+      0,   0,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   5,   2,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   0,   5,   5,   5,   5,   5,   0,   5,   0,   5,   5,   0,   5,   5,   0,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   5,   5,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   0,   2,   2,
-      2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,
-      2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   5,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
+      2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   2,   2,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   5,   5,   5,   5,   5,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   0,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   0,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
-      0,   0,   0,   0,   0,   0,   0,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
-      3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   0,   0,   0,
-      0,   2,   0,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,
-      4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   4,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
+      5,   0,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
       5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,   5,
-      5,   5,   5,   0,   0,   0,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,
-      5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      5,   5,   5,   5,   5,   5,   5,   5,   5,   0,   0,   0,   5,   5,   5,   5,   5,   5,
+      0,   0,   5,   5,   5,   5,   5,   5,   0,   0,   5,   5,   5,   5,   5,   5,   0,   0,
+      5,   5,   5,   0,   0,   0,
 };
 
 
diff --git a/js/src/vm/Unicode.h b/js/src/vm/Unicode.h
index 941a2496cc3..a83a0925de3 100644
--- a/js/src/vm/Unicode.h
+++ b/js/src/vm/Unicode.h
@@ -120,8 +120,9 @@ extern const CharacterInfo js_charinfo[];
 inline const CharacterInfo&
 CharInfo(jschar code)
 {
-    size_t index = index1[code >> 6];
-    index = index2[(index << 6) + (code & 0x3f)];
+    const size_t shift = 5;
+    size_t index = index1[code >> shift];
+    index = index2[(index << shift) + (code & ((1 << shift) - 1))];
 
     return js_charinfo[index];
 }
diff --git a/js/src/vm/make_unicode.py b/js/src/vm/make_unicode.py
index 3537b09058d..5a878ea564e 100644
--- a/js/src/vm/make_unicode.py
+++ b/js/src/vm/make_unicode.py
@@ -49,8 +49,6 @@ FLAG_SPACE = 1 << 0
 FLAG_LETTER = 1 << 1
 FLAG_IDENTIFIER_PART = 1 << 2
 FLAG_NO_DELTA = 1 << 3
-FLAG_ENCLOSING_MARK = 1 << 4
-FLAG_COMBINING_SPACING_MARK = 1 << 5
 
 MAX = 0xffff
 
@@ -115,10 +113,6 @@ def generate_unicode_stuff(unicode_data, data_file, test_mapping, test_space):
             flags |= FLAG_LETTER
         if category in ['Mn', 'Mc', 'Nd', 'Pc'] or code == ZWNJ or code == ZWJ: # $ 7.6 (IdentifierPart)
             flags |= FLAG_IDENTIFIER_PART
-        if category == 'Me':
-            flags |= FLAG_ENCLOSING_MARK
-        if category == 'Mc':
-            flags |= FLAG_COMBINING_SPACING_MARK
 
         if uppercase:
             upper = int(uppercase, 16)
@@ -194,7 +188,7 @@ if (typeof reportCompare === "function")
     index1, index2, shift = splitbins(index)
 
     # Don't forget to update CharInfo in Unicode.cpp if you need to change this
-    assert shift == 6
+    assert shift == 5
 
     # verify correctness
     for char in index:
@@ -212,17 +206,17 @@ if (typeof reportCompare === "function")
  * First let's have a look at a jschar, 16-bits:
  *              [................]
  * Step 1:
- *  Extracting the upper 10 bits from the jschar.
- *   upper = char >> 6 ([**********......])
+ *  Extracting the upper 11 bits from the jschar.
+ *   upper = char >>  5 ([***********.....])
  * Step 2:
  *  Using these bits to get an reduced index from index1.
  *   index = index1[upper]
  * Step 3:
- *  Combining the index and the bottom 6 bits of the orginial jschar.
- *   real_index = index2[(index << 6) + (jschar & 0x3f)] ([..********++++++])
+ *  Combining the index and the bottom 5 bits of the original jschar.
+ *   real_index = index2[(index << 5) + (char & ((1 << 5) - 1))] ([...********+++++])
  *
- * The advantage here is that the bigest number in index1 doesn't need 10 bits,
- * but 8 and we save some memory.
+ * The advantage here is that the biggest number in index1 doesn't need 10 bits,
+ * but 7 and we save some memory.
  *
  * Step 4:
  *  Get the character informations by looking up real_index in js_charinfo.

From 1d2ee7b792dac6749bf743906b72e0529f1aea81 Mon Sep 17 00:00:00 2001
From: Matt Brubeck 
Date: Fri, 1 Mar 2013 06:50:19 -0800
Subject: [PATCH 113/140] Bug 846621 - "Open link in new tab" in Metro should
 open a background tab [r=jimm]

---
 browser/metro/base/content/ContextCommands.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/browser/metro/base/content/ContextCommands.js b/browser/metro/base/content/ContextCommands.js
index ba9bae70c81..a18fcd81c36 100644
--- a/browser/metro/base/content/ContextCommands.js
+++ b/browser/metro/base/content/ContextCommands.js
@@ -128,7 +128,8 @@ var ContextCommands = {
   // Link specific
 
   openLinkInNewTab: function cc_openLinkInNewTab() {
-    BrowserUI.newTab(ContextMenuUI.popupState.linkURL, Browser.selectedTab);
+    Browser.addTab(ContextMenuUI.popupState.linkURL, false, Browser.selectedTab);
+    ContextUI.peekTabs();
   },
 
   copyLink: function cc_copyLink() {

From 8a5b3f1ccde674e0d4e94b01a5c5407dc4770b64 Mon Sep 17 00:00:00 2001
From: Matt Brubeck 
Date: Mon, 25 Feb 2013 11:08:33 -0800
Subject: [PATCH 114/140] Bug 840360 - Extract reftest-content.js's setTimeout
 implementation into new Timer.jsm [r=jwalker r=cjones sr=gavin]

--HG--
rename : browser/devtools/shared/Browser.jsm => toolkit/modules/Timer.jsm
rename : browser/devtools/shared/test/browser_browser_basic.js => toolkit/modules/tests/xpcshell/test_timer.js
---
 browser/devtools/shared/Browser.jsm           | 74 ++-----------------
 .../shared/test/browser_browser_basic.js      | 11 +--
 layout/tools/reftest/reftest-content.js       | 37 +---------
 toolkit/modules/Makefile.in                   |  1 +
 toolkit/modules/Timer.jsm                     | 41 ++++++++++
 toolkit/modules/tests/xpcshell/test_timer.js  | 28 +++++++
 toolkit/modules/tests/xpcshell/xpcshell.ini   |  1 +
 7 files changed, 80 insertions(+), 113 deletions(-)
 create mode 100644 toolkit/modules/Timer.jsm
 create mode 100644 toolkit/modules/tests/xpcshell/test_timer.js

diff --git a/browser/devtools/shared/Browser.jsm b/browser/devtools/shared/Browser.jsm
index 8ae927c2b03..49e0a194728 100644
--- a/browser/devtools/shared/Browser.jsm
+++ b/browser/devtools/shared/Browser.jsm
@@ -20,73 +20,9 @@ this.EXPORTED_SYMBOLS = [ "Node", "HTMLElement", "setTimeout", "clearTimeout" ];
 this.Node = Components.interfaces.nsIDOMNode;
 this.HTMLElement = Components.interfaces.nsIDOMHTMLElement;
 
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-/**
- * The next value to be returned by setTimeout
+/*
+ * Import and re-export the timeout functions from Timer.jsm.
  */
-let nextID = 1;
-
-/**
- * The map of outstanding timeouts
- */
-const timers = {};
-
-/**
- * Object to be passed to Timer.initWithCallback()
- */
-function TimerCallback(callback) {
-  this._callback = callback;
-  const interfaces = [ Components.interfaces.nsITimerCallback ];
-  this.QueryInterface = XPCOMUtils.generateQI(interfaces);
-}
-
-TimerCallback.prototype.notify = function(timer) {
-  try {
-    for (let timerID in timers) {
-      if (timers[timerID] === timer) {
-        delete timers[timerID];
-        break;
-      }
-    }
-    this._callback.apply(null, []);
-  }
-  catch (ex) {
-    dump(ex +  '\n');
-  }
-};
-
-/**
- * Executes a code snippet or a function after specified delay.
- * This is designed to have the same interface contract as the browser
- * function.
- * @param callback is the function you want to execute after the delay.
- * @param delay is the number of milliseconds that the function call should
- * be delayed by. Note that the actual delay may be longer, see Notes below.
- * @return the ID of the timeout, which can be used later with
- * window.clearTimeout.
- */
-this.setTimeout = function setTimeout(callback, delay) {
-  const timer = Components.classes["@mozilla.org/timer;1"]
-                        .createInstance(Components.interfaces.nsITimer);
-
-  let timerID = nextID++;
-  timers[timerID] = timer;
-
-  timer.initWithCallback(new TimerCallback(callback), delay, timer.TYPE_ONE_SHOT);
-  return timerID;
-};
-
-/**
- * Clears the delay set by window.setTimeout() and prevents the callback from
- * being executed (if it hasn't been executed already)
- * @param timerID the ID of the timeout you wish to clear, as returned by
- * window.setTimeout().
- */
-this.clearTimeout = function clearTimeout(timerID) {
-  let timer = timers[timerID];
-  if (timer) {
-    timer.cancel();
-    delete timers[timerID];
-  }
-};
+let Timer = Components.utils.import("resource://gre/modules/Timer.jsm", {});
+this.setTimeout = Timer.setTimeout;
+this.clearTimeout = Timer.clearTimeout;
diff --git a/browser/devtools/shared/test/browser_browser_basic.js b/browser/devtools/shared/test/browser_browser_basic.js
index 4276672055c..d9b3984b0e7 100644
--- a/browser/devtools/shared/test/browser_browser_basic.js
+++ b/browser/devtools/shared/test/browser_browser_basic.js
@@ -19,19 +19,10 @@ function test() {
 }
 
 function runTest(browser, tab, document) {
-  let timeout = imported.setTimeout(shouldNotBeCalled, 100);
-  imported.clearTimeout(timeout);
-
   var p = document.getElementById("id");
 
   ok(p instanceof imported.Node, "Node correctly defined");
   ok(p instanceof imported.HTMLElement, "HTMLElement correctly defined");
 
-  let timeout = imported.setTimeout(function() {
-    finish();
-  }, 100);
-}
-
-function shouldNotBeCalled() {
-  ok(false, "Timeout cleared");
+  finish();
 }
diff --git a/layout/tools/reftest/reftest-content.js b/layout/tools/reftest/reftest-content.js
index 3e78eddc6c7..014dfcc1b1a 100644
--- a/layout/tools/reftest/reftest-content.js
+++ b/layout/tools/reftest/reftest-content.js
@@ -7,40 +7,7 @@
 const CC = Components.classes;
 const CI = Components.interfaces;
 const CR = Components.results;
-
-/**
- * FIXME/bug 622224: work around lack of reliable setTimeout available
- * to frame scripts.
- */
-// This gives us >=2^30 unique timer IDs, enough for 1 per ms for 12.4
-// days.  Should be fine as a temporary workaround.
-var gNextTimeoutId = 0;
-var gTimeoutTable = { };        // int -> nsITimer
-
-function setTimeout(callbackFn, delayMs) {
-    var id = gNextTimeoutId++;
-    var timer = CC["@mozilla.org/timer;1"].createInstance(CI.nsITimer);
-    timer.initWithCallback({
-        notify: function notify_callback() {
-                    clearTimeout(id);
-                    callbackFn();
-                }
-        },
-        delayMs,
-        timer.TYPE_ONE_SHOT);
-
-    gTimeoutTable[id] = timer;
-
-    return id;
-}
-
-function clearTimeout(id) {
-    var timer = gTimeoutTable[id];
-    if (timer) {
-        timer.cancel();
-        delete gTimeoutTable[id];
-    }
-}
+const CU = Components.utils;
 
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 
@@ -50,6 +17,8 @@ const PRINTSETTINGS_CONTRACTID = "@mozilla.org/gfx/printsettings-service;1";
 // ""
 const BLANK_URL_FOR_CLEARING = "data:text/html;charset=UTF-8,%3C%21%2D%2DCLEAR%2D%2D%3E";
 
+CU.import("resource://gre/modules/Timer.jsm");
+
 var gBrowserIsRemote;
 var gHaveCanvasSnapshot = false;
 // Plugin layers can be updated asynchronously, so to make sure that all
diff --git a/toolkit/modules/Makefile.in b/toolkit/modules/Makefile.in
index ee6ebff299f..338effd08bf 100644
--- a/toolkit/modules/Makefile.in
+++ b/toolkit/modules/Makefile.in
@@ -13,6 +13,7 @@ EXTRA_JS_MODULES := \
   NewTabUtils.jsm \
   Sqlite.jsm \
   TelemetryTimestamps.jsm \
+  Timer.jsm \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
diff --git a/toolkit/modules/Timer.jsm b/toolkit/modules/Timer.jsm
new file mode 100644
index 00000000000..7d100e45c92
--- /dev/null
+++ b/toolkit/modules/Timer.jsm
@@ -0,0 +1,41 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * JS module implementation of nsIDOMJSWindow.setTimeout and clearTimeout.
+ */
+
+this.EXPORTED_SYMBOLS = ["setTimeout", "clearTimeout"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+// This gives us >=2^30 unique timer IDs, enough for 1 per ms for 12.4 days.
+let gNextTimeoutId = 1; // setTimeout must return a positive integer
+
+let gTimeoutTable = new Map(); // int -> nsITimer
+
+this.setTimeout = function setTimeout(aCallback, aMilliseconds) {
+  let id = gNextTimeoutId++;
+  let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+  timer.initWithCallback(function setTimeout_timer() {
+    gTimeoutTable.delete(id);
+    aCallback();
+  }, aMilliseconds, timer.TYPE_ONE_SHOT);
+
+  gTimeoutTable.set(id, timer);
+  return id;
+}
+
+this.clearTimeout = function clearTimeout(aId) {
+  if (gTimeoutTable.has(aId)) {
+    gTimeoutTable.get(aId).cancel();
+    gTimeoutTable.delete(aId);
+  }
+}
diff --git a/toolkit/modules/tests/xpcshell/test_timer.js b/toolkit/modules/tests/xpcshell/test_timer.js
new file mode 100644
index 00000000000..57981bc7585
--- /dev/null
+++ b/toolkit/modules/tests/xpcshell/test_timer.js
@@ -0,0 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests exports from Timer.jsm
+
+let imported = {};
+Components.utils.import("resource://gre/modules/Timer.jsm", imported);
+
+function run_test(browser, tab, document) {
+  do_test_pending();
+
+  let timeout1 = imported.setTimeout(function() do_throw("Should not be called"), 100);
+  do_check_eq(typeof timeout1, "number", "setTimeout returns a number");
+  do_check_true(timeout1 > 0, "setTimeout returns a positive number");
+
+  let timeout2 = imported.setTimeout(function() {
+    do_check_true(true, "Should be called");
+    do_test_finished();
+  }, 100);
+
+  do_check_eq(typeof timeout2, "number", "setTimeout returns a number");
+  do_check_true(timeout2 > 0, "setTimeout returns a positive number");
+  do_check_neq(timeout1, timeout2, "Calling setTimeout again returns a different value");
+
+  imported.clearTimeout(timeout1);
+}
diff --git a/toolkit/modules/tests/xpcshell/xpcshell.ini b/toolkit/modules/tests/xpcshell/xpcshell.ini
index 1255e7d635e..de3b2dcac6e 100644
--- a/toolkit/modules/tests/xpcshell/xpcshell.ini
+++ b/toolkit/modules/tests/xpcshell/xpcshell.ini
@@ -5,3 +5,4 @@ tail =
 [test_newtab-migrate-v1.js]
 [test_sqlite.js]
 [test_TelemetryTimestamps.js]
+[test_timer.js]

From 89c1bddb73f64c996a94550e2f59e37412c4124d Mon Sep 17 00:00:00 2001
From: Josh Matthews 
Date: Fri, 1 Mar 2013 10:35:31 -0500
Subject: [PATCH 115/140] Backed out changeset 61e57c281b9a (bug 844561) CLOSED
 TREE

---
 browser/base/content/browser.js                      |  9 ++++-----
 browser/base/content/test/Makefile.in                |  1 -
 .../base/content/test/browser_private_no_prompt.js   | 12 ------------
 3 files changed, 4 insertions(+), 18 deletions(-)
 delete mode 100644 browser/base/content/test/browser_private_no_prompt.js

diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 0f386835bdd..257ea2ce3ea 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6409,20 +6409,20 @@ function warnAboutClosingWindow() {
   // Figure out if there's at least one other browser window around.
   let e = Services.wm.getEnumerator("navigator:browser");
   let otherPBWindowExists = false;
-  let nonPopupPresent = false;
+  let warnAboutClosingTabs = false;
   while (e.hasMoreElements()) {
     let win = e.getNext();
     if (win != window) {
       if (isPBWindow && PrivateBrowsingUtils.isWindowPrivate(win))
         otherPBWindowExists = true;
       if (win.toolbar.visible)
-        nonPopupPresent = true;
+        warnAboutClosingTabs = true;
       // If the current window is not in private browsing mode we don't need to 
       // look for other pb windows, we can leave the loop when finding the 
       // first non-popup window. If however the current window is in private 
       // browsing mode then we need at least one other pb and one non-popup 
       // window to break out early.
-      if ((!isPBWindow || otherPBWindowExists) && nonPopupPresent)
+      if ((!isPBWindow || otherPBWindowExists) && warnAboutClosingTabs)
         break;
     }
   }
@@ -6437,8 +6437,7 @@ function warnAboutClosingWindow() {
     if (exitingCanceled.data)
       return false;
   }
-
-  if (!isPBWindow && nonPopupPresent)
+  if (warnAboutClosingTabs)
     return gBrowser.warnAboutClosingTabs(true);
 
   let os = Services.obs;
diff --git a/browser/base/content/test/Makefile.in b/browser/base/content/test/Makefile.in
index d45aa5b8548..9158dc26090 100644
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -315,7 +315,6 @@ _BROWSER_FILES = \
                  feed_tab.html \
                  browser_pluginCrashCommentAndURL.js \
                  pluginCrashCommentAndURL.html \
-                 browser_private_no_prompt.js \
                  $(NULL)
 
 ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
diff --git a/browser/base/content/test/browser_private_no_prompt.js b/browser/base/content/test/browser_private_no_prompt.js
deleted file mode 100644
index 1635eddf54e..00000000000
--- a/browser/base/content/test/browser_private_no_prompt.js
+++ /dev/null
@@ -1,12 +0,0 @@
-function test() {
-  waitForExplicitFinish();
-  var privateWin = OpenBrowserWindow({private: true});
-  privateWin.addEventListener("load", function onload() {
-    privateWin.removeEventListener("load", onload, false);
-
-    privateWin.BrowserOpenTab();
-    privateWin.BrowserTryToCloseWindow();
-    ok(true, "didn't prompt");
-    finish();
-  }, false);
-}
\ No newline at end of file

From 7a6618a4fcae15b2ff5ea50f54d0eb4c9ec804ca Mon Sep 17 00:00:00 2001
From: Kartikaya Gupta 
Date: Fri, 1 Mar 2013 10:41:17 -0500
Subject: [PATCH 116/140] Bug 845804 - Reuse OpenTempFile from
 nsMemoryInfoDumper in nsCycleCollector.cpp. r=njn

---
 xpcom/base/nsCycleCollector.cpp   | 28 ++++++----------------------
 xpcom/base/nsMemoryInfoDumper.cpp | 11 +++++++----
 xpcom/base/nsMemoryInfoDumper.h   |  9 +++++++++
 3 files changed, 22 insertions(+), 26 deletions(-)

diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp
index b0bdc0e71cf..d6fdba5d3eb 100644
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -123,6 +123,7 @@
 #include "nsIMemoryReporter.h"
 #include "nsIFile.h"
 #include "nsDirectoryServiceDefs.h"
+#include "nsMemoryInfoDumper.h"
 #include "xpcpublic.h"
 #include "nsXPCOMPrivate.h"
 #include "sampler.h"
@@ -1404,32 +1405,15 @@ private:
             mFilenameIdentifier.IsEmpty() ? "" : ".",
             NS_ConvertUTF16toUTF8(mFilenameIdentifier).get());
 
-        // Get the log directory either from $MOZ_CC_LOG_DIRECTORY or from our
-        // platform's temp directory. For Android, first try the downloads
-        // directory which is world-readable rather than the temp directory
-        // which is not.
+        // Get the log directory either from $MOZ_CC_LOG_DIRECTORY or from
+        // the fallback directories in OpenTempFile.
         nsCOMPtr logFile;
-        char* env;
-        if (env = PR_GetEnv("MOZ_CC_LOG_DIRECTORY")) {
+        if (char* env = PR_GetEnv("MOZ_CC_LOG_DIRECTORY")) {
             NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true,
                                   getter_AddRefs(logFile));
         }
-#ifdef ANDROID
-        if (!logFile && (env = PR_GetEnv("DOWNLOADS_DIRECTORY"))) {
-            NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true,
-                                  getter_AddRefs(logFile));
-        }
-#endif
-        if (!logFile) {
-            // Ask NSPR to point us to the temp directory.
-            NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(logFile));
-        }
-        NS_ENSURE_TRUE(logFile, nullptr);
-
-        nsresult rv = logFile->AppendNative(filename);
-        NS_ENSURE_SUCCESS(rv, nullptr);
-
-        rv = logFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644);
+        nsresult rv = nsMemoryInfoDumper::OpenTempFile(filename,
+                                                       getter_AddRefs(logFile));
         NS_ENSURE_SUCCESS(rv, nullptr);
 
         return logFile.forget();
diff --git a/xpcom/base/nsMemoryInfoDumper.cpp b/xpcom/base/nsMemoryInfoDumper.cpp
index b726095d1aa..273dd192d60 100644
--- a/xpcom/base/nsMemoryInfoDumper.cpp
+++ b/xpcom/base/nsMemoryInfoDumper.cpp
@@ -736,14 +736,17 @@ MakeFilename(const char *aPrefix, const nsAString &aIdentifier,
                             getpid(), aSuffix);
 }
 
-static nsresult
-OpenTempFile(const nsACString &aFilename, nsIFile* *aFile)
+/* static */ nsresult
+nsMemoryInfoDumper::OpenTempFile(const nsACString &aFilename, nsIFile* *aFile)
 {
 #ifdef ANDROID
   // For Android, first try the downloads directory which is world-readable
   // rather than the temp directory which is not.
-  if (char *env = PR_GetEnv("DOWNLOADS_DIRECTORY")) {
-    NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, aFile);
+  if (!*aFile) {
+    char *env = PR_GetEnv("DOWNLOADS_DIRECTORY");
+    if (env) {
+      NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, aFile);
+    }
   }
 #endif
   nsresult rv;
diff --git a/xpcom/base/nsMemoryInfoDumper.h b/xpcom/base/nsMemoryInfoDumper.h
index c3e02614aec..31938a0380b 100644
--- a/xpcom/base/nsMemoryInfoDumper.h
+++ b/xpcom/base/nsMemoryInfoDumper.h
@@ -29,6 +29,15 @@ public:
 public:
   static void Initialize();
 
+  /**
+   * This function creates a new unique file based on |aFilename| in a
+   * world-readable temp directory. This is the system temp directory
+   * or, in the case of Android, the downloads directory. If |aFile| is
+   * non-null, it is assumed to point to a folder, and that folder is used
+   * instead.
+   */
+  static nsresult OpenTempFile(const nsACString &aFilename, nsIFile* *aFile);
+
 private:
   static nsresult
   DumpMemoryReportsToFileImpl(const nsAString& aIdentifier);

From faf2237d714ab028117313c7c8e0069c9b00c132 Mon Sep 17 00:00:00 2001
From: Jonathan Watt 
Date: Fri, 1 Mar 2013 15:34:06 +0000
Subject: [PATCH 117/140] Bug 843725 - Add support for changing the value of
  using the up/down/left/right/pgup/pgdn/home/end keys.
 r=mounir, r=smaug.

---
 .../html/content/src/nsHTMLInputElement.cpp   |  82 ++++++-
 content/html/content/test/forms/Makefile.in   |   1 +
 .../forms/test_input_range_key_events.html    | 206 ++++++++++++++++++
 layout/style/forms.css                        |   6 +-
 4 files changed, 292 insertions(+), 3 deletions(-)
 create mode 100644 content/html/content/test/forms/test_input_range_key_events.html

diff --git a/content/html/content/src/nsHTMLInputElement.cpp b/content/html/content/src/nsHTMLInputElement.cpp
index 44c4cdfded7..4f6164af993 100644
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -2569,6 +2569,17 @@ SelectTextFieldOnFocus()
   return gSelectTextFieldOnFocus == 1;
 }
 
+static bool
+IsLTR(Element* aElement)
+{
+  nsIFrame *frame = aElement->GetPrimaryFrame();
+  if (frame) {
+    return frame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
+  }
+  // at least for HTML, directionality is exclusively LTR or RTL
+  return aElement->GetDirectionality() == eDir_LTR;
+}
+
 nsresult
 nsHTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
 {
@@ -2835,6 +2846,74 @@ nsHTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
             NS_ENSURE_SUCCESS(rv, rv);
           }
 
+          if (aVisitor.mEvent->message == NS_KEY_PRESS &&
+              mType == NS_FORM_INPUT_RANGE && !keyEvent->IsAlt() &&
+              !keyEvent->IsControl() && !keyEvent->IsMeta() &&
+              (keyEvent->keyCode == NS_VK_LEFT ||
+               keyEvent->keyCode == NS_VK_RIGHT ||
+               keyEvent->keyCode == NS_VK_UP ||
+               keyEvent->keyCode == NS_VK_DOWN ||
+               keyEvent->keyCode == NS_VK_PAGE_UP ||
+               keyEvent->keyCode == NS_VK_PAGE_DOWN ||
+               keyEvent->keyCode == NS_VK_HOME ||
+               keyEvent->keyCode == NS_VK_END)) {
+            double minimum = GetMinimum();
+            double maximum = GetMaximum();
+            MOZ_ASSERT(MOZ_DOUBLE_IS_FINITE(minimum) &&
+                       MOZ_DOUBLE_IS_FINITE(maximum));
+            if (minimum < maximum) { // else the value is locked to the minimum
+              double value = GetValueAsDouble();
+              double step = GetStep();
+              if (step == kStepAny) {
+                step = GetDefaultStep();
+              }
+              MOZ_ASSERT(MOZ_DOUBLE_IS_FINITE(value) &&
+                         MOZ_DOUBLE_IS_FINITE(step));
+              double newValue;
+              switch (keyEvent->keyCode) {
+                case  NS_VK_LEFT:
+                  newValue = value + (IsLTR(this) ? -step : step);
+                  break;
+                case  NS_VK_RIGHT:
+                  newValue = value + (IsLTR(this) ? step : -step);
+                  break;
+                case  NS_VK_UP:
+                  // Even for horizontal range, "up" means "increase"
+                  newValue = value + step;
+                  break;
+                case  NS_VK_DOWN:
+                  // Even for horizontal range, "down" means "decrease"
+                  newValue = value - step;
+                  break;
+                case  NS_VK_HOME:
+                  newValue = minimum;
+                  break;
+                case  NS_VK_END:
+                  newValue = maximum;
+                  break;
+                case  NS_VK_PAGE_UP:
+                  // For PgUp/PgDn we jump 10% of the total range, unless step
+                  // requires us to jump more.
+                  newValue = value + std::max(step, 0.1 * (maximum - minimum));
+                  break;
+                case  NS_VK_PAGE_DOWN:
+                  newValue = value - std::max(step, 0.1 * (maximum - minimum));
+                  break;
+              }
+              MOZ_ASSERT(MOZ_DOUBLE_IS_FINITE(newValue));
+              nsAutoString val;
+              ConvertNumberToString(newValue, val);
+              SetValueInternal(val, true, true);
+              nsIFrame* frame = GetPrimaryFrame();
+              if (frame) {
+                // Trigger reflow to update the position of the thumb:
+                frame->PresContext()->GetPresShell()->
+                  FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
+              }
+              aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
+            }
+          }
+
         } break; // NS_KEY_PRESS || NS_KEY_UP
 
         case NS_MOUSE_BUTTON_DOWN:
@@ -4353,7 +4432,8 @@ nsHTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t
     return true;
   }
 
-  if (IsSingleLineTextControl(false)) {
+  if (IsSingleLineTextControl(false) ||
+      mType == NS_FORM_INPUT_RANGE) {
     *aIsFocusable = true;
     return false;
   }
diff --git a/content/html/content/test/forms/Makefile.in b/content/html/content/test/forms/Makefile.in
index 5f7a08373e6..7a7cf86be2f 100644
--- a/content/html/content/test/forms/Makefile.in
+++ b/content/html/content/test/forms/Makefile.in
@@ -18,6 +18,7 @@ MOCHITEST_FILES = \
 		test_input_attributes_reflection.html \
 		test_input_list_attribute.html \
 		test_input_email.html \
+		test_input_range_key_events.html \
 		test_input_url.html \
 		test_pattern_attribute.html \
 		test_required_attribute.html \
diff --git a/content/html/content/test/forms/test_input_range_key_events.html b/content/html/content/test/forms/test_input_range_key_events.html
new file mode 100644
index 00000000000..c8f8d182d4f
--- /dev/null
+++ b/content/html/content/test/forms/test_input_range_key_events.html
@@ -0,0 +1,206 @@
+
+
+
+
+  Test key events for range
+  
+  
+  
+  
+
+
+Mozilla Bug 843725
+

+
+
+
+
+
+ + diff --git a/layout/style/forms.css b/layout/style/forms.css index 0b49e90fb53..a7ad31fe76f 100644 --- a/layout/style/forms.css +++ b/layout/style/forms.css @@ -720,12 +720,14 @@ meter { input[type="range"] { -moz-appearance: none; display: inline-block !important; - cursor: default; width: 12em; height: 1.3em; + margin: 0 0.7em; + /* Override some rules that apply on all input types: */ + cursor: default; background: none; border: none; - margin: 0 0.7em; + -moz-binding: none; /* we don't want any of platformHTMLBindings.xml#inputFields */ } /** From f8fe6e5a6c808858af09732a5cc0a1d7064a68df Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Fri, 1 Mar 2013 15:46:33 +0000 Subject: [PATCH 118/140] Bug 716403 - Dynamically hide the location bar when scrolling. r=kats Intercept touch events and use them to pan the location bar on and off the screen, depending on its current location. Also dynamically show/hide the location bar when loading pages. --- mobile/android/base/BrowserApp.java | 127 +++++++++++++++++- mobile/android/base/BrowserToolbar.java | 59 ++++++++ mobile/android/base/BrowserToolbarLayout.java | 34 +++++ mobile/android/base/GeckoApp.java | 76 ++++++----- mobile/android/base/Makefile.in | 1 + mobile/android/base/PropertyAnimator.java | 19 ++- .../layout-land-v14/browser_toolbar.xml.in | 4 +- .../browser_toolbar_menu.xml.in | 4 +- .../browser_toolbar_menu.xml.in | 4 +- .../resources/layout/browser_toolbar.xml.in | 4 +- .../layout/browser_toolbar_menu.xml.in | 4 +- 11 files changed, 281 insertions(+), 55 deletions(-) create mode 100644 mobile/android/base/BrowserToolbarLayout.java diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index d27ca55b80b..2d488618861 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -8,6 +8,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.db.BrowserContract.Combined; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.gfx.BitmapUtils; +import org.mozilla.gecko.gfx.ImmutableViewportMetrics; import org.mozilla.gecko.util.UiAsyncTask; import org.mozilla.gecko.util.GeckoBackgroundThread; @@ -41,6 +42,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.Toast; import java.io.InputStream; @@ -87,6 +89,12 @@ abstract public class BrowserApp extends GeckoApp // We'll ask for feedback after the user launches the app this many times. private static final int FEEDBACK_LAUNCH_COUNT = 15; + // Variables used for scrolling the toolbar on/off the page. + private static final int TOOLBAR_ONLOAD_HIDE_DELAY = 2000; + private float mLastTouchY = 0.0f; + private float mToolbarSubpixelAccumulation = 0.0f; + private boolean mToolbarLocked = true; + @Override public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { switch(msg) { @@ -97,10 +105,15 @@ abstract public class BrowserApp extends GeckoApp // fall through case SELECTED: if (Tabs.getInstance().isSelectedTab(tab)) { - if ("about:home".equals(tab.getURL())) + if ("about:home".equals(tab.getURL())) { showAboutHome(); - else + + // Show the toolbar immediately. + mBrowserToolbar.animateVisibility(true, 0); + mToolbarLocked = true; + } else { hideAboutHome(); + } // Dismiss any SiteIdentity Popup SiteIdentityPopup.getInstance().dismiss(); @@ -119,9 +132,23 @@ abstract public class BrowserApp extends GeckoApp }); } break; - case LOAD_ERROR: case START: + if (Tabs.getInstance().isSelectedTab(tab)) { + invalidateOptionsMenu(); + + // Show the toolbar. + mBrowserToolbar.animateVisibility(true, 0); + } + break; + case LOAD_ERROR: case STOP: + if (Tabs.getInstance().isSelectedTab(tab)) { + if (!mAboutHomeShowing) { + // Hide the toolbar after a delay. + mBrowserToolbar.animateVisibility(false, TOOLBAR_ONLOAD_HIDE_DELAY); + } + } + // fall through case MENU_UPDATED: if (Tabs.getInstance().isSelectedTab(tab)) { invalidateOptionsMenu(); @@ -149,6 +176,93 @@ abstract public class BrowserApp extends GeckoApp updateAboutHomeTopSites(); } + @Override + public boolean onInterceptTouchEvent(View view, MotionEvent event) { + int action = event.getActionMasked(); + + int pointerCount = event.getPointerCount(); + + View toolbarView = mBrowserToolbar.getLayout(); + if (action == MotionEvent.ACTION_DOWN || + action == MotionEvent.ACTION_POINTER_DOWN) { + if (pointerCount == 1) { + mToolbarLocked = false; + mToolbarSubpixelAccumulation = 0.0f; + mLastTouchY = event.getY(); + return super.onInterceptTouchEvent(view, event); + } + // + // Lock the toolbar until we're back down to one pointer. + mToolbarLocked = true; + + // Animate the toolbar to the fully on/off position. + mBrowserToolbar.animateVisibility( + toolbarView.getScrollY() > toolbarView.getHeight() / 2 ? + false : true, 0); + } + + // If more than one pointer has been tracked, let the event pass + // through and be handled by the PanZoomController for zooming. + if (pointerCount > 1) { + return super.onInterceptTouchEvent(view, event); + } + + // If a pointer has been lifted so that there's only one pointer left, + // unlock the toolbar and track that remaining pointer. + if (pointerCount == 1 && action == MotionEvent.ACTION_POINTER_UP) { + mLastTouchY = event.getY(1 - event.getActionIndex()); + mToolbarLocked = false; + return super.onInterceptTouchEvent(view, event); + } + + // Don't bother doing anything with the events if we're loading - + // the toolbar will be permanently visible in this case. + float eventY = event.getY(); + if (Tabs.getInstance().getSelectedTab().getState() != Tab.STATE_LOADING) { + int toolbarHeight = toolbarView.getHeight(); + if (action == MotionEvent.ACTION_MOVE && !mToolbarLocked) { + // Cancel any ongoing animation before we start moving the toolbar. + mBrowserToolbar.cancelVisibilityAnimation(); + + // Move the toolbar by the amount the touch event has moved, + // clamping to fully visible or fully hidden. + float deltaY = mLastTouchY - eventY; + + // Don't let the toolbar scroll off the top if it's just exposing + // overscroll area. + ImmutableViewportMetrics metrics = + mLayerView.getLayerClient().getViewportMetrics(); + float toolbarMaxY = Math.min(toolbarHeight, + Math.max(0, toolbarHeight - (metrics.pageRectTop - + metrics.viewportRectTop))); + + int toolbarY = toolbarView.getScrollY(); + float newToolbarYf = Math.max(0, Math.min(toolbarMaxY, + toolbarY + deltaY + mToolbarSubpixelAccumulation)); + int newToolbarY = Math.round(newToolbarYf); + mToolbarSubpixelAccumulation = (newToolbarYf - newToolbarY); + + toolbarView.scrollTo(0, newToolbarY); + + // Reset tracking when the toolbar is fully visible or hidden. + if (newToolbarY == 0 || newToolbarY == toolbarHeight) { + mLastTouchY = eventY; + } + } else if (action == MotionEvent.ACTION_UP || + action == MotionEvent.ACTION_CANCEL) { + // Animate the toolbar to fully on or off, depending on how much + // of it is hidden. + mBrowserToolbar.animateVisibility( + toolbarView.getScrollY() > toolbarHeight / 2 ? false : true, 0); + } + } + + // Update the last recorded y position. + mLastTouchY = eventY; + + return super.onInterceptTouchEvent(view, event); + } + void handleReaderAdded(boolean success, final String title, final String url) { if (!success) { showToast(R.string.reading_list_failed, Toast.LENGTH_SHORT); @@ -194,7 +308,7 @@ abstract public class BrowserApp extends GeckoApp super.onCreate(savedInstanceState); LinearLayout actionBar = (LinearLayout) getActionBarLayout(); - mMainLayout.addView(actionBar, 0); + mMainLayout.addView(actionBar, 1); ((GeckoApp.MainLayout) mMainLayout).setOnInterceptTouchListener(new HideTabsTouchListener()); @@ -320,6 +434,10 @@ abstract public class BrowserApp extends GeckoApp mBrowserToolbar.from(actionBar); mBrowserToolbar.refresh(); + if (mAboutHomeContent != null) { + mAboutHomeContent.setPadding(0, mBrowserToolbar.getLayout().getHeight(), 0, 0); + } + // The favicon view is different now, so we need to update the DoorHangerPopup anchor view. if (mDoorHangerPopup != null) mDoorHangerPopup.setAnchor(mBrowserToolbar.mFavicon); @@ -772,6 +890,7 @@ abstract public class BrowserApp extends GeckoApp mAboutHomeStartupTimer.stop(); } }); + mAboutHomeContent.setPadding(0, mBrowserToolbar.getLayout().getHeight(), 0, 0); } else { mAboutHomeContent.update(EnumSet.of(AboutHomeContent.UpdateFlags.TOP_SITES, AboutHomeContent.UpdateFlags.REMOTE_TABS)); diff --git a/mobile/android/base/BrowserToolbar.java b/mobile/android/base/BrowserToolbar.java index 76150024848..1f70d597331 100644 --- a/mobile/android/base/BrowserToolbar.java +++ b/mobile/android/base/BrowserToolbar.java @@ -41,6 +41,7 @@ import android.widget.ViewSwitcher; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.TimerTask; public class BrowserToolbar implements ViewSwitcher.ViewFactory, Tabs.OnTabsChangedListener, @@ -105,11 +106,23 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory, private int mCount; private int mFaviconSize; + private PropertyAnimator mVisibilityAnimator; + private TimerTask mDelayedVisibilityTask; + + private enum ToolbarVisibility { + VISIBLE, + HIDDEN, + INCONSISTENT + }; + private ToolbarVisibility mVisibility; + private static final int TABS_CONTRACTED = 1; private static final int TABS_EXPANDED = 2; private static final int FORWARD_ANIMATION_DURATION = 450; + private static final int VISIBILITY_ANIMATION_DURATION = 250; + public BrowserToolbar(BrowserApp activity) { // BrowserToolbar is attached to BrowserApp only. mActivity = activity; @@ -118,6 +131,8 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory, sActionItems = new ArrayList(); Tabs.registerOnTabsChangedListener(this); mAnimateSiteSecurity = true; + + mVisibility = ToolbarVisibility.INCONSISTENT; } public void from(LinearLayout layout) { @@ -465,6 +480,50 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory, } } + public void animateVisibility(boolean show, long delay) { + // Do nothing if there's a delayed animation pending that does the + // same thing and this request also has a delay. + if (mVisibility != ToolbarVisibility.INCONSISTENT && + ((delay > 0) == (mDelayedVisibilityTask != null)) && + (show == isVisible())) { + return; + } + + cancelVisibilityAnimation(); + mVisibility = show ? ToolbarVisibility.VISIBLE : ToolbarVisibility.HIDDEN; + + mVisibilityAnimator = new PropertyAnimator(VISIBILITY_ANIMATION_DURATION); + mVisibilityAnimator.attach(mLayout, PropertyAnimator.Property.SCROLL_Y, + show ? 0 : mLayout.getHeight()); + if (delay > 0) { + mDelayedVisibilityTask = new TimerTask() { + public void run() { + mVisibilityAnimator.start(); + mDelayedVisibilityTask = null; + } + }; + mLayout.postDelayed(mDelayedVisibilityTask, delay); + } else { + mVisibilityAnimator.start(); + } + } + + public void cancelVisibilityAnimation() { + mVisibility = ToolbarVisibility.INCONSISTENT; + if (mDelayedVisibilityTask != null) { + mLayout.removeCallbacks(mDelayedVisibilityTask); + mDelayedVisibilityTask = null; + } + if (mVisibilityAnimator != null) { + mVisibilityAnimator.stop(false); + mVisibilityAnimator = null; + } + } + + public boolean isVisible() { + return mVisibility == ToolbarVisibility.VISIBLE; + } + @Override public void onAnimationStart(Animation animation) { if (animation.equals(mLockFadeIn)) { diff --git a/mobile/android/base/BrowserToolbarLayout.java b/mobile/android/base/BrowserToolbarLayout.java new file mode 100644 index 00000000000..75fa65c1530 --- /dev/null +++ b/mobile/android/base/BrowserToolbarLayout.java @@ -0,0 +1,34 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko; + +import org.mozilla.gecko.gfx.LayerView; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.widget.LinearLayout; + +public class BrowserToolbarLayout extends LinearLayout { + private static final String LOGTAG = "GeckoToolbarLayout"; + + public BrowserToolbarLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + // If the motion event has occured below the toolbar (due to the scroll + // offset), let it pass through to the page. + if (event != null && event.getY() > getHeight() - getScrollY()) { + return false; + } + + return super.onTouchEvent(event); + } +} + diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 41f9530e560..167b2e8a46f 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -111,7 +111,8 @@ abstract public class GeckoApp extends GeckoActivity implements GeckoEventListener, SensorEventListener, LocationListener, Tabs.OnTabsChangedListener, GeckoEventResponder, - GeckoMenu.Callback, GeckoMenu.MenuPresenter + GeckoMenu.Callback, GeckoMenu.MenuPresenter, + OnInterceptTouchListener { private static final String LOGTAG = "GeckoApp"; @@ -149,7 +150,7 @@ abstract public class GeckoApp static public final int RESTORE_CRASH = 2; StartupMode mStartupMode = null; - protected LinearLayout mMainLayout; + protected RelativeLayout mMainLayout; protected RelativeLayout mGeckoLayout; public View getView() { return mGeckoLayout; } public SurfaceView cameraView; @@ -186,6 +187,8 @@ abstract public class GeckoApp private String mPrivateBrowsingSession; + private PointF mInitialTouchPoint = null; + private static Boolean sIsLargeTablet = null; private static Boolean sIsSmallTablet = null; @@ -1395,7 +1398,7 @@ abstract public class GeckoApp // setup gecko layout mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout); - mMainLayout = (LinearLayout) findViewById(R.id.main_layout); + mMainLayout = (RelativeLayout) findViewById(R.id.main_layout); // setup tabs panel mTabsPanel = (TabsPanel) findViewById(R.id.tabs_panel); @@ -2432,42 +2435,41 @@ abstract public class GeckoApp protected void connectGeckoLayerClient() { mLayerView.getLayerClient().notifyGeckoReady(); - mLayerView.setTouchIntercepter(new OnInterceptTouchListener() { - private PointF initialPoint = null; - - @Override - public boolean onInterceptTouchEvent(View view, MotionEvent event) { - return false; - } - - @Override - public boolean onTouch(View view, MotionEvent event) { - if (event == null) - return true; - - int action = event.getAction(); - PointF point = new PointF(event.getX(), event.getY()); - if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) { - initialPoint = point; - } - - if (initialPoint != null && (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_MOVE) { - if (PointUtils.subtract(point, initialPoint).length() < PanZoomController.PAN_THRESHOLD) { - // Don't send the touchmove event if if the users finger hasn't move far - // Necessary for Google Maps to work correctlly. See bug 771099. - return true; - } else { - initialPoint = null; - } - } - - GeckoAppShell.sendEventToGecko(GeckoEvent.createMotionEvent(event)); - return true; - } - }); + mLayerView.setTouchIntercepter(this); } - public static class MainLayout extends LinearLayout { + @Override + public boolean onInterceptTouchEvent(View view, MotionEvent event) { + return false; + } + + @Override + public boolean onTouch(View view, MotionEvent event) { + if (event == null) + return true; + + int action = event.getActionMasked(); + PointF point = new PointF(event.getX(), event.getY()); + if (action == MotionEvent.ACTION_DOWN) { + mInitialTouchPoint = point; + } + + if (mInitialTouchPoint != null && action == MotionEvent.ACTION_MOVE) { + if (PointUtils.subtract(point, mInitialTouchPoint).length() < + PanZoomController.PAN_THRESHOLD) { + // Don't send the touchmove event if if the users finger hasn't moved far. + // Necessary for Google Maps to work correctly. See bug 771099. + return true; + } else { + mInitialTouchPoint = null; + } + } + + GeckoAppShell.sendEventToGecko(GeckoEvent.createMotionEvent(event)); + return true; + } + + public static class MainLayout extends RelativeLayout { private OnInterceptTouchListener mOnInterceptTouchListener; public MainLayout(Context context, AttributeSet attrs) { diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 33322a4ebee..a340da05401 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -59,6 +59,7 @@ FENNEC_JAVA_FILES = \ BrowserApp.java \ BrowserToolbar.java \ BrowserToolbarBackground.java \ + BrowserToolbarLayout.java \ CameraImageResultHandler.java \ CameraVideoResultHandler.java \ CanvasDelegate.java \ diff --git a/mobile/android/base/PropertyAnimator.java b/mobile/android/base/PropertyAnimator.java index acc98803a8e..b2b6d06cf17 100644 --- a/mobile/android/base/PropertyAnimator.java +++ b/mobile/android/base/PropertyAnimator.java @@ -141,12 +141,18 @@ public class PropertyAnimator implements Runnable { } } - public void stop() { + + /** + * Stop the animation, optionally snapping to the end position. + * onPropertyAnimationEnd is only called when snapping to the end position. + */ + public void stop(boolean snapToEndPosition) { mFramePoster.cancelAnimationFrame(); // Make sure to snap to the end position. - for (ElementHolder element : mElementsList) { - invalidate(element, element.to); + for (ElementHolder element : mElementsList) { + if (snapToEndPosition) + invalidate(element, element.to); if (shouldEnableHardwareLayer(element)) element.view.setLayerType(View.LAYER_TYPE_NONE, null); @@ -157,11 +163,16 @@ public class PropertyAnimator implements Runnable { mElementsList.clear(); if (mListener != null) { - mListener.onPropertyAnimationEnd(); + if (snapToEndPosition) + mListener.onPropertyAnimationEnd(); mListener = null; } } + public void stop() { + stop(true); + } + private boolean shouldEnableHardwareLayer(ElementHolder element) { if (!mUseHardwareLayer) return false; diff --git a/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in b/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in index c417f1ec408..c0fab6ceb35 100644 --- a/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in +++ b/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in @@ -4,7 +4,7 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - @@ -165,4 +165,4 @@ - + diff --git a/mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml.in b/mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml.in index 8337435af03..0050eaa19c4 100644 --- a/mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml.in +++ b/mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml.in @@ -4,7 +4,7 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - @@ -173,4 +173,4 @@ - + diff --git a/mobile/android/base/resources/layout-large-v11/browser_toolbar_menu.xml.in b/mobile/android/base/resources/layout-large-v11/browser_toolbar_menu.xml.in index 2e898734a69..5979fa8e54f 100644 --- a/mobile/android/base/resources/layout-large-v11/browser_toolbar_menu.xml.in +++ b/mobile/android/base/resources/layout-large-v11/browser_toolbar_menu.xml.in @@ -4,7 +4,7 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - @@ -179,4 +179,4 @@ - + diff --git a/mobile/android/base/resources/layout/browser_toolbar.xml.in b/mobile/android/base/resources/layout/browser_toolbar.xml.in index 6d0b93fd6f3..92902bbfd74 100644 --- a/mobile/android/base/resources/layout/browser_toolbar.xml.in +++ b/mobile/android/base/resources/layout/browser_toolbar.xml.in @@ -4,7 +4,7 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - @@ -163,4 +163,4 @@ - + diff --git a/mobile/android/base/resources/layout/browser_toolbar_menu.xml.in b/mobile/android/base/resources/layout/browser_toolbar_menu.xml.in index a655317aa0f..12c803dfc70 100644 --- a/mobile/android/base/resources/layout/browser_toolbar_menu.xml.in +++ b/mobile/android/base/resources/layout/browser_toolbar_menu.xml.in @@ -4,7 +4,7 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - @@ -172,4 +172,4 @@ - + From 077631a00aef377b4526b0ca406383309740800a Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Fri, 1 Mar 2013 15:46:33 +0000 Subject: [PATCH 119/140] Bug 716403 - Offset fixed layers so the toolbar doesn't obscure them. r=nrc,kats Offset fixed layers in the compositor so that the toolbar in Firefox for Android doesn't obscure them. This does not affect layout, so input on the elements in said layers will appear broken. --- gfx/layers/ipc/CompositorParent.cpp | 33 ++++++++--- gfx/layers/ipc/CompositorParent.h | 6 +- mobile/android/base/BrowserApp.java | 1 + mobile/android/base/BrowserToolbarLayout.java | 25 ++++++++ mobile/android/base/gfx/GeckoLayerClient.java | 21 ++++++- .../base/gfx/ImmutableViewportMetrics.java | 57 ++++++++++++++++++- mobile/android/base/gfx/ViewTransform.java | 8 +++ widget/android/AndroidBridge.cpp | 6 +- widget/android/AndroidBridge.h | 3 +- widget/android/AndroidJavaWrappers.cpp | 24 +++++++- widget/android/AndroidJavaWrappers.h | 8 ++- 11 files changed, 175 insertions(+), 17 deletions(-) diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index 48405f89d43..e065c48fac9 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -640,7 +640,8 @@ Translate2D(gfx3DMatrix& aTransform, const gfxPoint& aOffset) void CompositorParent::TransformFixedLayers(Layer* aLayer, const gfxPoint& aTranslation, - const gfxSize& aScaleDiff) + const gfxSize& aScaleDiff, + const gfx::Margin& aFixedLayerMargins) { if (aLayer->GetIsFixedPosition() && !aLayer->GetParent()->GetIsFixedPosition()) { @@ -650,6 +651,20 @@ CompositorParent::TransformFixedLayers(Layer* aLayer, const gfxPoint& anchor = aLayer->GetFixedPositionAnchor(); gfxPoint translation(aTranslation - (anchor - anchor / aScaleDiff)); + // Offset this translation by the fixed layer margins, depending on what + // side of the viewport the layer is anchored to. + if (anchor.x > 0) { + translation.x -= aFixedLayerMargins.right; + } else { + translation.x += aFixedLayerMargins.left; + } + + if (anchor.y > 0) { + translation.y -= aFixedLayerMargins.bottom; + } else { + translation.y += aFixedLayerMargins.top; + } + // The transform already takes the resolution scale into account. Since we // will apply the resolution scale again when computing the effective // transform, we must apply the inverse resolution scale here. @@ -676,7 +691,7 @@ CompositorParent::TransformFixedLayers(Layer* aLayer, for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { - TransformFixedLayers(child, aTranslation, aScaleDiff); + TransformFixedLayers(child, aTranslation, aScaleDiff, aFixedLayerMargins); } } @@ -869,10 +884,12 @@ CompositorParent::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFrame, 1); shadow->SetShadowTransform(transform); + gfx::Margin fixedLayerMargins(0, 0, 0, 0); TransformFixedLayers( aLayer, -treeTransform.mTranslation / treeTransform.mScale, - treeTransform.mScale); + treeTransform.mScale, + fixedLayerMargins); appliedTransform = true; } @@ -937,8 +954,9 @@ CompositorParent::TransformScrollableLayer(Layer* aLayer, const gfx3DMatrix& aRo displayPortDevPixels.x += scrollOffsetDevPixels.x; displayPortDevPixels.y += scrollOffsetDevPixels.y; + gfx::Margin fixedLayerMargins(0, 0, 0, 0); SyncViewportInfo(displayPortDevPixels, 1/rootScaleX, mLayersUpdated, - mScrollOffset, mXScale, mYScale); + mScrollOffset, mXScale, mYScale, fixedLayerMargins); mLayersUpdated = false; // Handle transformations for asynchronous panning and zooming. We determine the @@ -995,7 +1013,7 @@ CompositorParent::TransformScrollableLayer(Layer* aLayer, const gfx3DMatrix& aRo 1.0f/container->GetPostYScale(), 1); shadow->SetShadowTransform(computedTransform); - TransformFixedLayers(aLayer, offset, scaleDiff); + TransformFixedLayers(aLayer, offset, scaleDiff, fixedLayerMargins); } bool @@ -1059,11 +1077,12 @@ CompositorParent::SetPageRect(const gfx::Rect& aCssPageRect) void CompositorParent::SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated, - nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY) + nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY, + gfx::Margin& aFixedLayerMargins) { #ifdef MOZ_WIDGET_ANDROID AndroidBridge::Bridge()->SyncViewportInfo(aDisplayPort, aDisplayResolution, aLayersUpdated, - aScrollOffset, aScaleX, aScaleY); + aScrollOffset, aScaleX, aScaleY, aFixedLayerMargins); #endif } diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index d77f8eab65f..e15d340127c 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -181,7 +181,8 @@ protected: virtual void SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect); virtual void SetPageRect(const gfx::Rect& aCssPageRect); virtual void SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated, - nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY); + nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY, + gfx::Margin& aFixedLayerMargins); void SetEGLSurfaceSize(int width, int height); // If SetPanZoomControllerForLayerTree is not set, Compositor will use // derived class AsyncPanZoomController transformations. @@ -261,7 +262,8 @@ private: */ void TransformFixedLayers(Layer* aLayer, const gfxPoint& aTranslation, - const gfxSize& aScaleDiff); + const gfxSize& aScaleDiff, + const gfx::Margin& aFixedLayerMargins); virtual PGrallocBufferParent* AllocPGrallocBuffer( const gfxIntSize&, const uint32_t&, const uint32_t&, diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 2d488618861..502e0f5b4c8 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -370,6 +370,7 @@ abstract public class BrowserApp extends GeckoApp mBrowserToolbar.updateBackButton(false); mBrowserToolbar.updateForwardButton(false); + ((BrowserToolbarLayout)mBrowserToolbar.getLayout()).refreshMargins(); mDoorHangerPopup.setAnchor(mBrowserToolbar.mFavicon); diff --git a/mobile/android/base/BrowserToolbarLayout.java b/mobile/android/base/BrowserToolbarLayout.java index 75fa65c1530..421e247705a 100644 --- a/mobile/android/base/BrowserToolbarLayout.java +++ b/mobile/android/base/BrowserToolbarLayout.java @@ -30,5 +30,30 @@ public class BrowserToolbarLayout extends LinearLayout { return super.onTouchEvent(event); } + + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + + if (t != oldt) { + refreshMargins(); + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + if (h != oldh) { + refreshMargins(); + } + } + + public void refreshMargins() { + LayerView view = GeckoApp.mAppContext.getLayerView(); + if (view != null) { + view.getLayerClient().setFixedLayerMargins(0, getHeight() - getScrollY(), 0, 0); + } + } } diff --git a/mobile/android/base/gfx/GeckoLayerClient.java b/mobile/android/base/gfx/GeckoLayerClient.java index 551d9147a10..ce8064de301 100644 --- a/mobile/android/base/gfx/GeckoLayerClient.java +++ b/mobile/android/base/gfx/GeckoLayerClient.java @@ -17,6 +17,7 @@ import org.mozilla.gecko.util.FloatUtils; import android.content.Context; import android.graphics.PointF; +import android.graphics.Rect; import android.graphics.RectF; import android.os.SystemClock; import android.util.DisplayMetrics; @@ -303,7 +304,8 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget default: case UPDATE: // Keep the old viewport size - metrics = messageMetrics.setViewportSize(oldMetrics.getWidth(), oldMetrics.getHeight()); + metrics = messageMetrics.setViewportSize(oldMetrics.getWidth(), oldMetrics.getHeight()) + .setFixedLayerMarginsFrom(oldMetrics); if (!oldMetrics.fuzzyEquals(metrics)) { abortPanZoomAnimation(); } @@ -346,6 +348,17 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget } } + /** + * Sets margins on fixed-position layers, to be used when compositing. + * Must be called on the UI thread! + */ + public void setFixedLayerMargins(float left, float top, float right, float bottom) { + ImmutableViewportMetrics oldMetrics = getViewportMetrics(); + setViewportMetrics(oldMetrics.setFixedLayerMargins(left, top, right, bottom), false); + + mView.requestRender(); + } + // This is called on the Gecko thread to determine if we're still interested // in the update of this display-port to continue. We can return true here // to abort the current update and continue with any subsequent ones. This @@ -536,6 +549,10 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget mCurrentViewTransform.x = mFrameMetrics.viewportRectLeft; mCurrentViewTransform.y = mFrameMetrics.viewportRectTop; mCurrentViewTransform.scale = mFrameMetrics.zoomFactor; + mCurrentViewTransform.fixedLayerMarginLeft = mFrameMetrics.fixedLayerMarginLeft; + mCurrentViewTransform.fixedLayerMarginTop = mFrameMetrics.fixedLayerMarginTop; + mCurrentViewTransform.fixedLayerMarginRight = mFrameMetrics.fixedLayerMarginRight; + mCurrentViewTransform.fixedLayerMarginBottom = mFrameMetrics.fixedLayerMarginBottom; mRootLayer.setPositionAndResolution(x, y, x + width, y + height, resolution); @@ -673,7 +690,7 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget return; } ImmutableViewportMetrics m = mViewportMetrics; - BrowserApp.mBrowserToolbar.setShadowVisibility(m.viewportRectTop >= m.pageRectTop); + BrowserApp.mBrowserToolbar.setShadowVisibility(m.viewportRectTop >= m.pageRectTop - m.fixedLayerMarginTop); } }); } diff --git a/mobile/android/base/gfx/ImmutableViewportMetrics.java b/mobile/android/base/gfx/ImmutableViewportMetrics.java index b6d52b2ab8a..d6cc2f71001 100644 --- a/mobile/android/base/gfx/ImmutableViewportMetrics.java +++ b/mobile/android/base/gfx/ImmutableViewportMetrics.java @@ -32,6 +32,10 @@ public class ImmutableViewportMetrics { public final float viewportRectTop; public final float viewportRectRight; public final float viewportRectBottom; + public final float fixedLayerMarginLeft; + public final float fixedLayerMarginTop; + public final float fixedLayerMarginRight; + public final float fixedLayerMarginBottom; public final float zoomFactor; public ImmutableViewportMetrics(DisplayMetrics metrics) { @@ -39,6 +43,7 @@ public class ImmutableViewportMetrics { viewportRectTop = pageRectTop = cssPageRectTop = 0; viewportRectRight = pageRectRight = cssPageRectRight = metrics.widthPixels; viewportRectBottom = pageRectBottom = cssPageRectBottom = metrics.heightPixels; + fixedLayerMarginLeft = fixedLayerMarginTop = fixedLayerMarginRight = fixedLayerMarginBottom = 0; zoomFactor = 1.0f; } @@ -47,6 +52,21 @@ public class ImmutableViewportMetrics { float aCssPageRectTop, float aCssPageRectRight, float aCssPageRectBottom, float aViewportRectLeft, float aViewportRectTop, float aViewportRectRight, float aViewportRectBottom, float aZoomFactor) + { + this(aPageRectLeft, aPageRectTop, + aPageRectRight, aPageRectBottom, aCssPageRectLeft, + aCssPageRectTop, aCssPageRectRight, aCssPageRectBottom, + aViewportRectLeft, aViewportRectTop, aViewportRectRight, + aViewportRectBottom, 0.0f, 0.0f, 0.0f, 0.0f, aZoomFactor); + } + + private ImmutableViewportMetrics(float aPageRectLeft, float aPageRectTop, + float aPageRectRight, float aPageRectBottom, float aCssPageRectLeft, + float aCssPageRectTop, float aCssPageRectRight, float aCssPageRectBottom, + float aViewportRectLeft, float aViewportRectTop, float aViewportRectRight, + float aViewportRectBottom, float aFixedLayerMarginLeft, + float aFixedLayerMarginTop, float aFixedLayerMarginRight, + float aFixedLayerMarginBottom, float aZoomFactor) { pageRectLeft = aPageRectLeft; pageRectTop = aPageRectTop; @@ -60,6 +80,10 @@ public class ImmutableViewportMetrics { viewportRectTop = aViewportRectTop; viewportRectRight = aViewportRectRight; viewportRectBottom = aViewportRectBottom; + fixedLayerMarginLeft = aFixedLayerMarginLeft; + fixedLayerMarginTop = aFixedLayerMarginTop; + fixedLayerMarginRight = aFixedLayerMarginRight; + fixedLayerMarginBottom = aFixedLayerMarginBottom; zoomFactor = aZoomFactor; } @@ -125,6 +149,10 @@ public class ImmutableViewportMetrics { FloatUtils.interpolate(viewportRectTop, to.viewportRectTop, t), FloatUtils.interpolate(viewportRectRight, to.viewportRectRight, t), FloatUtils.interpolate(viewportRectBottom, to.viewportRectBottom, t), + FloatUtils.interpolate(fixedLayerMarginLeft, to.fixedLayerMarginLeft, t), + FloatUtils.interpolate(fixedLayerMarginTop, to.fixedLayerMarginTop, t), + FloatUtils.interpolate(fixedLayerMarginRight, to.fixedLayerMarginRight, t), + FloatUtils.interpolate(fixedLayerMarginBottom, to.fixedLayerMarginBottom, t), FloatUtils.interpolate(zoomFactor, to.zoomFactor, t)); } @@ -133,6 +161,7 @@ public class ImmutableViewportMetrics { pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, viewportRectLeft, viewportRectTop, viewportRectLeft + width, viewportRectTop + height, + fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom, zoomFactor); } @@ -141,6 +170,7 @@ public class ImmutableViewportMetrics { pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, newOriginX, newOriginY, newOriginX + getWidth(), newOriginY + getHeight(), + fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom, zoomFactor); } @@ -149,6 +179,7 @@ public class ImmutableViewportMetrics { pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, + fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom, newZoomFactor); } @@ -161,9 +192,25 @@ public class ImmutableViewportMetrics { pageRect.left, pageRect.top, pageRect.right, pageRect.bottom, cssPageRect.left, cssPageRect.top, cssPageRect.right, cssPageRect.bottom, viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, + fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom, zoomFactor); } + public ImmutableViewportMetrics setFixedLayerMargins(float left, float top, float right, float bottom) { + return new ImmutableViewportMetrics( + pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, + cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, + viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, + left, top, right, bottom, zoomFactor); + } + + public ImmutableViewportMetrics setFixedLayerMarginsFrom(ImmutableViewportMetrics fromMetrics) { + return setFixedLayerMargins(fromMetrics.fixedLayerMarginLeft, + fromMetrics.fixedLayerMarginTop, + fromMetrics.fixedLayerMarginRight, + fromMetrics.fixedLayerMarginBottom); + } + /* This will set the zoom factor and re-scale page-size and viewport offset * accordingly. The given focus will remain at the same point on the screen * after scaling. @@ -185,6 +232,7 @@ public class ImmutableViewportMetrics { newPageRectLeft, newPageRectTop, newPageRectRight, newPageRectBottom, cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, origin.x, origin.y, origin.x + getWidth(), origin.y + getHeight(), + fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom, newZoomFactor); } @@ -207,6 +255,7 @@ public class ImmutableViewportMetrics { pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, newViewport.left, newViewport.top, newViewport.right, newViewport.bottom, + fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom, zoomFactor); } @@ -214,6 +263,10 @@ public class ImmutableViewportMetrics { // Don't bother checking the pageRectXXX values because they are a product // of the cssPageRectXXX values and the zoomFactor, except with more rounding // error. Checking those is both inefficient and can lead to false negatives. + // + // This doesn't return false if the fixed layer margins differ as none + // of the users of this function are interested in the margins in that + // way. return FloatUtils.fuzzyEquals(cssPageRectLeft, other.cssPageRectLeft) && FloatUtils.fuzzyEquals(cssPageRectTop, other.cssPageRectTop) && FloatUtils.fuzzyEquals(cssPageRectRight, other.cssPageRectRight) @@ -231,6 +284,8 @@ public class ImmutableViewportMetrics { + viewportRectRight + "," + viewportRectBottom + ") p=(" + pageRectLeft + "," + pageRectTop + "," + pageRectRight + "," + pageRectBottom + ") c=(" + cssPageRectLeft + "," + cssPageRectTop + "," + cssPageRectRight + "," - + cssPageRectBottom + ") z=" + zoomFactor; + + cssPageRectBottom + ") m=(" + fixedLayerMarginLeft + "," + + fixedLayerMarginTop + "," + fixedLayerMarginRight + "," + + fixedLayerMarginBottom + ") z=" + zoomFactor; } } diff --git a/mobile/android/base/gfx/ViewTransform.java b/mobile/android/base/gfx/ViewTransform.java index edb45f5b90e..f1ac3109f14 100644 --- a/mobile/android/base/gfx/ViewTransform.java +++ b/mobile/android/base/gfx/ViewTransform.java @@ -9,11 +9,19 @@ public class ViewTransform { public float x; public float y; public float scale; + public float fixedLayerMarginLeft; + public float fixedLayerMarginTop; + public float fixedLayerMarginRight; + public float fixedLayerMarginBottom; public ViewTransform(float inX, float inY, float inScale) { x = inX; y = inY; scale = inScale; + fixedLayerMarginLeft = 0; + fixedLayerMarginTop = 0; + fixedLayerMarginRight = 0; + fixedLayerMarginBottom = 0; } } diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index b5a8a64bb3e..6016ce37ba1 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -2110,13 +2110,15 @@ AndroidBridge::SetPageRect(const gfx::Rect& aCssPageRect) void AndroidBridge::SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated, - nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY) + nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY, + gfx::Margin& aFixedLayerMargins) { AndroidGeckoLayerClient *client = mLayerClient; if (!client) return; - client->SyncViewportInfo(aDisplayPort, aDisplayResolution, aLayersUpdated, aScrollOffset, aScaleX, aScaleY); + client->SyncViewportInfo(aDisplayPort, aDisplayResolution, aLayersUpdated, + aScrollOffset, aScaleX, aScaleY, aFixedLayerMargins); } AndroidBridge::AndroidBridge() diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index 5043111b4d4..514d432b61f 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -332,7 +332,8 @@ public: void SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect); void SetPageRect(const gfx::Rect& aCssPageRect); void SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated, - nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY); + nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY, + gfx::Margin& aFixedLayerMargins); void AddPluginView(jobject view, const gfxRect& rect, bool isFullScreen); void RemovePluginView(jobject view, bool isFullScreen); diff --git a/widget/android/AndroidJavaWrappers.cpp b/widget/android/AndroidJavaWrappers.cpp index 889424c7db9..4ecd1e86da6 100644 --- a/widget/android/AndroidJavaWrappers.cpp +++ b/widget/android/AndroidJavaWrappers.cpp @@ -100,6 +100,10 @@ jclass AndroidViewTransform::jViewTransformClass = 0; jfieldID AndroidViewTransform::jXField = 0; jfieldID AndroidViewTransform::jYField = 0; jfieldID AndroidViewTransform::jScaleField = 0; +jfieldID AndroidViewTransform::jFixedLayerMarginLeft = 0; +jfieldID AndroidViewTransform::jFixedLayerMarginTop = 0; +jfieldID AndroidViewTransform::jFixedLayerMarginRight = 0; +jfieldID AndroidViewTransform::jFixedLayerMarginBottom = 0; jclass AndroidProgressiveUpdateData::jProgressiveUpdateDataClass = 0; jfieldID AndroidProgressiveUpdateData::jXField = 0; @@ -365,6 +369,10 @@ AndroidViewTransform::InitViewTransformClass(JNIEnv *jEnv) jXField = getField("x", "F"); jYField = getField("y", "F"); jScaleField = getField("scale", "F"); + jFixedLayerMarginLeft = getField("fixedLayerMarginLeft", "F"); + jFixedLayerMarginTop = getField("fixedLayerMarginTop", "F"); + jFixedLayerMarginRight = getField("fixedLayerMarginRight", "F"); + jFixedLayerMarginBottom = getField("fixedLayerMarginBottom", "F"); } void @@ -739,7 +747,8 @@ AndroidGeckoLayerClient::SetPageRect(const gfx::Rect& aCssPageRect) void AndroidGeckoLayerClient::SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated, - nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY) + nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY, + gfx::Margin& aFixedLayerMargins) { NS_ASSERTION(!isNull(), "SyncViewportInfo called on null layer client!"); JNIEnv *env = GetJNIForThread(); // this is called on the compositor thread @@ -762,6 +771,7 @@ AndroidGeckoLayerClient::SyncViewportInfo(const nsIntRect& aDisplayPort, float a aScrollOffset = nsIntPoint(viewTransform.GetX(env), viewTransform.GetY(env)); aScaleX = aScaleY = viewTransform.GetScale(env); + viewTransform.GetFixedLayerMargins(env, aFixedLayerMargins); } bool @@ -996,6 +1006,18 @@ AndroidViewTransform::GetScale(JNIEnv *env) return env->GetFloatField(wrapped_obj, jScaleField); } +void +AndroidViewTransform::GetFixedLayerMargins(JNIEnv *env, gfx::Margin &aFixedLayerMargins) +{ + if (!env) + return; + + aFixedLayerMargins.left = env->GetFloatField(wrapped_obj, jFixedLayerMarginLeft); + aFixedLayerMargins.top = env->GetFloatField(wrapped_obj, jFixedLayerMarginTop); + aFixedLayerMargins.right = env->GetFloatField(wrapped_obj, jFixedLayerMarginRight); + aFixedLayerMargins.bottom = env->GetFloatField(wrapped_obj, jFixedLayerMarginBottom); +} + float AndroidProgressiveUpdateData::GetX(JNIEnv *env) { diff --git a/widget/android/AndroidJavaWrappers.h b/widget/android/AndroidJavaWrappers.h index fa8d2a1f945..0a67f3bd41b 100644 --- a/widget/android/AndroidJavaWrappers.h +++ b/widget/android/AndroidJavaWrappers.h @@ -191,12 +191,17 @@ public: float GetX(JNIEnv *env); float GetY(JNIEnv *env); float GetScale(JNIEnv *env); + void GetFixedLayerMargins(JNIEnv *env, gfx::Margin &aFixedLayerMargins); private: static jclass jViewTransformClass; static jfieldID jXField; static jfieldID jYField; static jfieldID jScaleField; + static jfieldID jFixedLayerMarginLeft; + static jfieldID jFixedLayerMarginTop; + static jfieldID jFixedLayerMarginRight; + static jfieldID jFixedLayerMarginBottom; }; class AndroidProgressiveUpdateData : public WrappedJavaObject { @@ -257,7 +262,8 @@ public: void SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect); void SetPageRect(const gfx::Rect& aCssPageRect); void SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated, - nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY); + nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY, + gfx::Margin& aFixedLayerMargins); bool ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, const gfx::Rect& aDisplayPort, float aDisplayResolution, bool aDrawingCritical, gfx::Rect& aViewport, float& aScaleX, float& aScaleY); bool CreateFrame(AutoLocalJNIFrame *jniFrame, AndroidLayerRendererFrame& aFrame); bool ActivateProgram(AutoLocalJNIFrame *jniFrame); From 1461d5cf814ddbab44a17683f5a184e9b993fe78 Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Fri, 1 Mar 2013 15:46:34 +0000 Subject: [PATCH 120/140] Bug 716403 - Make the top of the page accessible with the toolbar visible. r=kats This makes it possible to scroll to the top of the page with the toolbar visible in Firefox for Android. It also causes JavaScript scrolling to position 0 to expose the toolbar. --- mobile/android/base/BrowserApp.java | 10 ++++- mobile/android/base/gfx/GeckoLayerClient.java | 43 ++++++++++++++++--- .../base/gfx/ImmutableViewportMetrics.java | 28 ++++++++---- .../base/gfx/JavaPanZoomController.java | 22 +++++++--- 4 files changed, 82 insertions(+), 21 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 502e0f5b4c8..14027bf736f 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -179,9 +179,17 @@ abstract public class BrowserApp extends GeckoApp @Override public boolean onInterceptTouchEvent(View view, MotionEvent event) { int action = event.getActionMasked(); - int pointerCount = event.getPointerCount(); + // Whenever there are no pointers left on the screen, tell the page + // to clamp the viewport on fixed layer margin changes. This lets the + // toolbar scrolling off the top of the page make the page scroll up + // if it'd cause the page to go into overscroll, but only when there + // are no pointers held down. + mLayerView.getLayerClient().setClampOnFixedLayerMarginsChange( + pointerCount == 0 || action == MotionEvent.ACTION_CANCEL || + action == MotionEvent.ACTION_UP); + View toolbarView = mBrowserToolbar.getLayout(); if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) { diff --git a/mobile/android/base/gfx/GeckoLayerClient.java b/mobile/android/base/gfx/GeckoLayerClient.java index ce8064de301..7951b7671ea 100644 --- a/mobile/android/base/gfx/GeckoLayerClient.java +++ b/mobile/android/base/gfx/GeckoLayerClient.java @@ -91,6 +91,8 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget private final PanZoomController mPanZoomController; private LayerView mView; + private boolean mClampOnMarginChange; + public GeckoLayerClient(Context context, LayerView view, EventDispatcher eventDispatcher) { // we can fill these in with dummy values because they are always written // to before being read @@ -105,6 +107,7 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget mProgressiveUpdateDisplayPort = new DisplayPortMetrics(); mLastProgressiveUpdateWasLowPrecision = false; mProgressiveUpdateWasInDanger = false; + mClampOnMarginChange = true; mForceRedraw = true; DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); @@ -354,11 +357,21 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget */ public void setFixedLayerMargins(float left, float top, float right, float bottom) { ImmutableViewportMetrics oldMetrics = getViewportMetrics(); - setViewportMetrics(oldMetrics.setFixedLayerMargins(left, top, right, bottom), false); + ImmutableViewportMetrics newMetrics = oldMetrics.setFixedLayerMargins(left, top, right, bottom); + + if (mClampOnMarginChange) { + newMetrics = newMetrics.clampWithMargins(); + } + + setViewportMetrics(newMetrics, false); mView.requestRender(); } + public void setClampOnFixedLayerMarginsChange(boolean aClamp) { + mClampOnMarginChange = aClamp; + } + // This is called on the Gecko thread to determine if we're still interested // in the update of this display-port to continue. We can return true here // to abort the current update and continue with any subsequent ones. This @@ -470,7 +483,15 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget float pageLeft, float pageTop, float pageRight, float pageBottom, float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) { synchronized (this) { - final ImmutableViewportMetrics newMetrics = getViewportMetrics() + ImmutableViewportMetrics currentMetrics = getViewportMetrics(); + + // If we're meant to be scrolled to the top, take into account any + // margin set on the pan zoom controller. + if (offsetY == 0) { + offsetY = -currentMetrics.fixedLayerMarginTop; + } + + final ImmutableViewportMetrics newMetrics = currentMetrics .setViewportOrigin(offsetX, offsetY) .setZoomFactor(zoom) .setPageRect(new RectF(pageLeft, pageTop, pageRight, pageBottom), @@ -549,10 +570,20 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget mCurrentViewTransform.x = mFrameMetrics.viewportRectLeft; mCurrentViewTransform.y = mFrameMetrics.viewportRectTop; mCurrentViewTransform.scale = mFrameMetrics.zoomFactor; - mCurrentViewTransform.fixedLayerMarginLeft = mFrameMetrics.fixedLayerMarginLeft; - mCurrentViewTransform.fixedLayerMarginTop = mFrameMetrics.fixedLayerMarginTop; - mCurrentViewTransform.fixedLayerMarginRight = mFrameMetrics.fixedLayerMarginRight; - mCurrentViewTransform.fixedLayerMarginBottom = mFrameMetrics.fixedLayerMarginBottom; + + // Adjust the fixed layer margins so that overscroll subtracts from them. + mCurrentViewTransform.fixedLayerMarginLeft = + Math.max(0, mFrameMetrics.fixedLayerMarginLeft + + Math.min(0, mFrameMetrics.viewportRectLeft - mFrameMetrics.pageRectLeft)); + mCurrentViewTransform.fixedLayerMarginTop = + Math.max(0, mFrameMetrics.fixedLayerMarginTop + + Math.min(0, mFrameMetrics.viewportRectTop - mFrameMetrics.pageRectTop)); + mCurrentViewTransform.fixedLayerMarginRight = + Math.max(0, mFrameMetrics.fixedLayerMarginRight + + Math.min(0, (mFrameMetrics.pageRectRight - mFrameMetrics.viewportRectRight))); + mCurrentViewTransform.fixedLayerMarginBottom = + Math.max(0, mFrameMetrics.fixedLayerMarginBottom + + Math.min(0, (mFrameMetrics.pageRectBottom - mFrameMetrics.viewportRectBottom))); mRootLayer.setPositionAndResolution(x, y, x + width, y + height, resolution); diff --git a/mobile/android/base/gfx/ImmutableViewportMetrics.java b/mobile/android/base/gfx/ImmutableViewportMetrics.java index d6cc2f71001..5627d3f8e89 100644 --- a/mobile/android/base/gfx/ImmutableViewportMetrics.java +++ b/mobile/android/base/gfx/ImmutableViewportMetrics.java @@ -237,19 +237,20 @@ public class ImmutableViewportMetrics { } /** Clamps the viewport to remain within the page rect. */ - public ImmutableViewportMetrics clamp() { + private ImmutableViewportMetrics clamp(float marginLeft, float marginTop, + float marginRight, float marginBottom) { RectF newViewport = getViewport(); // The viewport bounds ought to never exceed the page bounds. - if (newViewport.right > pageRectRight) - newViewport.offset(pageRectRight - newViewport.right, 0); - if (newViewport.left < pageRectLeft) - newViewport.offset(pageRectLeft - newViewport.left, 0); + if (newViewport.right > pageRectRight + marginRight) + newViewport.offset((pageRectRight + marginRight) - newViewport.right, 0); + if (newViewport.left < pageRectLeft - marginLeft) + newViewport.offset((pageRectLeft - marginLeft) - newViewport.left, 0); - if (newViewport.bottom > pageRectBottom) - newViewport.offset(0, pageRectBottom - newViewport.bottom); - if (newViewport.top < pageRectTop) - newViewport.offset(0, pageRectTop - newViewport.top); + if (newViewport.bottom > pageRectBottom + marginBottom) + newViewport.offset(0, (pageRectBottom + marginBottom) - newViewport.bottom); + if (newViewport.top < pageRectTop - marginTop) + newViewport.offset(0, (pageRectTop - marginTop) - newViewport.top); return new ImmutableViewportMetrics( pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, @@ -259,6 +260,15 @@ public class ImmutableViewportMetrics { zoomFactor); } + public ImmutableViewportMetrics clamp() { + return clamp(0, 0, 0, 0); + } + + public ImmutableViewportMetrics clampWithMargins() { + return clamp(fixedLayerMarginLeft, fixedLayerMarginTop, + fixedLayerMarginRight, fixedLayerMarginBottom); + } + public boolean fuzzyEquals(ImmutableViewportMetrics other) { // Don't bother checking the pageRectXXX values because they are a product // of the cssPageRectXXX values and the zoomFactor, except with more rounding diff --git a/mobile/android/base/gfx/JavaPanZoomController.java b/mobile/android/base/gfx/JavaPanZoomController.java index bb2c9c3a6a4..695754fec64 100644 --- a/mobile/android/base/gfx/JavaPanZoomController.java +++ b/mobile/android/base/gfx/JavaPanZoomController.java @@ -857,7 +857,7 @@ class JavaPanZoomController } /* Now we pan to the right origin. */ - viewportMetrics = viewportMetrics.clamp(); + viewportMetrics = viewportMetrics.clampWithMargins(); return viewportMetrics; } @@ -869,9 +869,15 @@ class JavaPanZoomController @Override protected float getViewportLength() { return getMetrics().getWidth(); } @Override - protected float getPageStart() { return getMetrics().pageRectLeft; } + protected float getPageStart() { + ImmutableViewportMetrics metrics = getMetrics(); + return metrics.pageRectLeft - metrics.fixedLayerMarginLeft; + } @Override - protected float getPageLength() { return getMetrics().getPageWidth(); } + protected float getPageLength() { + ImmutableViewportMetrics metrics = getMetrics(); + return metrics.getPageWidth() + metrics.fixedLayerMarginLeft + metrics.fixedLayerMarginRight; + } } private class AxisY extends Axis { @@ -881,9 +887,15 @@ class JavaPanZoomController @Override protected float getViewportLength() { return getMetrics().getHeight(); } @Override - protected float getPageStart() { return getMetrics().pageRectTop; } + protected float getPageStart() { + ImmutableViewportMetrics metrics = getMetrics(); + return metrics.pageRectTop - metrics.fixedLayerMarginTop; + } @Override - protected float getPageLength() { return getMetrics().getPageHeight(); } + protected float getPageLength() { + ImmutableViewportMetrics metrics = getMetrics(); + return metrics.getPageHeight() + metrics.fixedLayerMarginTop + metrics.fixedLayerMarginBottom; + } } /* From 413fbf7cd096b563e5d654715cd3cd91c66b5113 Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Fri, 1 Mar 2013 15:46:34 +0000 Subject: [PATCH 121/140] Bug 716403 - Resize viewport dynamically on Android. r=kats,mfinkle This causes the viewport size to differ, depending on the length of the page. This has the effect of pages that size themselves to the size of the window always having the toolbar visible, making sites like Google Maps more usable. --- mobile/android/base/BrowserToolbar.java | 23 ++++- mobile/android/base/BrowserToolbarLayout.java | 7 ++ .../base/gfx/JavaPanZoomController.java | 14 ++- mobile/android/chrome/content/browser.js | 96 ++++++++++++++++--- 4 files changed, 119 insertions(+), 21 deletions(-) diff --git a/mobile/android/base/BrowserToolbar.java b/mobile/android/base/BrowserToolbar.java index 1f70d597331..f583fa5b17f 100644 --- a/mobile/android/base/BrowserToolbar.java +++ b/mobile/android/base/BrowserToolbar.java @@ -43,6 +43,8 @@ import java.util.Arrays; import java.util.List; import java.util.TimerTask; +import org.mozilla.gecko.gfx.ImmutableViewportMetrics; + public class BrowserToolbar implements ViewSwitcher.ViewFactory, Tabs.OnTabsChangedListener, GeckoMenu.ActionItemBarPresenter, @@ -480,6 +482,23 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory, } } + private boolean canToolbarHide() { + // Forbid the toolbar from hiding if hiding the toolbar would cause + // the page to go into overscroll. + ImmutableViewportMetrics metrics = GeckoApp.mAppContext.getLayerView(). + getLayerClient().getViewportMetrics(); + return (metrics.getPageHeight() >= metrics.getHeight()); + } + + private void startVisibilityAnimation() { + // Only start the animation if we're showing the toolbar, or it's ok + // to hide it. + if (mVisibility == ToolbarVisibility.VISIBLE || + canToolbarHide()) { + mVisibilityAnimator.start(); + } + } + public void animateVisibility(boolean show, long delay) { // Do nothing if there's a delayed animation pending that does the // same thing and this request also has a delay. @@ -498,13 +517,13 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory, if (delay > 0) { mDelayedVisibilityTask = new TimerTask() { public void run() { - mVisibilityAnimator.start(); + startVisibilityAnimation(); mDelayedVisibilityTask = null; } }; mLayout.postDelayed(mDelayedVisibilityTask, delay); } else { - mVisibilityAnimator.start(); + startVisibilityAnimation(); } } diff --git a/mobile/android/base/BrowserToolbarLayout.java b/mobile/android/base/BrowserToolbarLayout.java index 421e247705a..5e3afa6235c 100644 --- a/mobile/android/base/BrowserToolbarLayout.java +++ b/mobile/android/base/BrowserToolbarLayout.java @@ -45,6 +45,13 @@ public class BrowserToolbarLayout extends LinearLayout { super.onSizeChanged(w, h, oldw, oldh); if (h != oldh) { + // In the current UI, this is the only place we have need of + // viewport margins (to stop the toolbar from obscuring fixed-pos + // content). + GeckoAppShell.sendEventToGecko( + GeckoEvent.createBroadcastEvent("Viewport:FixedMarginsChanged", + "{ \"top\" : " + h + ", \"right\" : 0, \"bottom\" : 0, \"left\" : 0 }")); + refreshMargins(); } } diff --git a/mobile/android/base/gfx/JavaPanZoomController.java b/mobile/android/base/gfx/JavaPanZoomController.java index 695754fec64..b65ad8e8eca 100644 --- a/mobile/android/base/gfx/JavaPanZoomController.java +++ b/mobile/android/base/gfx/JavaPanZoomController.java @@ -829,15 +829,21 @@ class JavaPanZoomController // Ensure minZoomFactor keeps the page at least as big as the viewport. if (pageRect.width() > 0) { - float scaleFactor = viewport.width() / pageRect.width(); + float pageWidth = pageRect.width() + + viewportMetrics.fixedLayerMarginLeft + + viewportMetrics.fixedLayerMarginRight; + float scaleFactor = viewport.width() / pageWidth; minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor); - if (viewport.width() > pageRect.width()) + if (viewport.width() > pageWidth) focusX = 0.0f; } if (pageRect.height() > 0) { - float scaleFactor = viewport.height() / pageRect.height(); + float pageHeight = pageRect.height() + + viewportMetrics.fixedLayerMarginTop + + viewportMetrics.fixedLayerMarginBottom; + float scaleFactor = viewport.height() / pageHeight; minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor); - if (viewport.height() > pageRect.height()) + if (viewport.height() > pageHeight) focusY = 0.0f; } diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index e99c3470cb8..814333872bd 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -205,6 +205,7 @@ var BrowserApp = { Services.obs.addObserver(this, "FullScreen:Exit", false); Services.obs.addObserver(this, "Viewport:Change", false); Services.obs.addObserver(this, "Viewport:Flush", false); + Services.obs.addObserver(this, "Viewport:FixedMarginsChanged", false); Services.obs.addObserver(this, "Passwords:Init", false); Services.obs.addObserver(this, "FormHistory:Init", false); Services.obs.addObserver(this, "ToggleProfiling", false); @@ -1252,6 +1253,10 @@ var BrowserApp = { sendMessageToJava({ type: "Telemetry:Gather" }); break; + case "Viewport:FixedMarginsChanged": + gViewportMargins = JSON.parse(aData); + break; + default: dump('BrowserApp.observe: unexpected topic "' + aTopic + '"\n'); break; @@ -2781,6 +2786,12 @@ nsBrowserAccess.prototype = { let gScreenWidth = 1; let gScreenHeight = 1; +// The margins that should be applied to the viewport for fixed position +// children. This is used to avoid browser chrome permanently obscuring +// fixed position content, and also to make sure window-sized pages take +// into account said browser chrome. +let gViewportMargins = { top: 0, right: 0, bottom: 0, left: 0}; + function Tab(aURL, aParams) { this.browser = null; this.id = 0; @@ -2789,6 +2800,9 @@ function Tab(aURL, aParams) { this._zoom = 1.0; this._drawZoom = 1.0; this.userScrollPos = { x: 0, y: 0 }; + this.viewportExcludesHorizontalMargins = true; + this.viewportExcludesVerticalMargins = true; + this.updatingViewportForPageSizeChange = false; this.contentDocumentIsDisplayed = true; this.pluginDoorhangerTimeout = null; this.shouldShowPluginDoorhanger = true; @@ -3257,15 +3271,34 @@ Tab.prototype = { setScrollClampingSize: function(zoom) { let viewportWidth = gScreenWidth / zoom; let viewportHeight = gScreenHeight / zoom; + let screenWidth = gScreenWidth; + let screenHeight = gScreenHeight; + let [pageWidth, pageHeight] = this.getPageSize(this.browser.contentDocument, viewportWidth, viewportHeight); + // Check if the page would fit into either of the viewport dimensions minus + // the margins and shrink the screen size accordingly so that the aspect + // ratio calculation below works correctly in these situations. + // We take away the margin size over two to account for rounding errors, + // as the browser size set in updateViewportSize doesn't allow for any + // size between these two values (and thus anything between them is + // attributable to rounding error). + if ((pageHeight * zoom) < gScreenHeight - (gViewportMargins.top + gViewportMargins.bottom) / 2) { + screenHeight = gScreenHeight - gViewportMargins.top - gViewportMargins.bottom; + viewportHeight = screenHeight / zoom; + } + if ((pageWidth * zoom) < gScreenWidth - (gViewportMargins.left + gViewportMargins.right) / 2) { + screenWidth = gScreenWidth - gViewportMargins.left - gViewportMargins.right; + viewportWidth = screenWidth / zoom; + } + // Make sure the aspect ratio of the screen is maintained when setting // the clamping scroll-port size. - let factor = Math.min(viewportWidth / gScreenWidth, pageWidth / gScreenWidth, - viewportHeight / gScreenHeight, pageHeight / gScreenHeight); - let scrollPortWidth = gScreenWidth * factor; - let scrollPortHeight = gScreenHeight * factor; + let factor = Math.min(viewportWidth / screenWidth, pageWidth / screenWidth, + viewportHeight / screenHeight, pageHeight / screenHeight); + let scrollPortWidth = Math.min(screenWidth * factor, pageWidth * zoom); + let scrollPortHeight = Math.min(screenHeight * factor, pageHeight * zoom); let win = this.browser.contentWindow; win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils). @@ -3325,20 +3358,23 @@ Tab.prototype = { }, getViewport: function() { + let screenW = gScreenWidth - gViewportMargins.left - gViewportMargins.right; + let screenH = gScreenHeight - gViewportMargins.top - gViewportMargins.bottom; + let viewport = { - width: gScreenWidth, - height: gScreenHeight, - cssWidth: gScreenWidth / this._zoom, - cssHeight: gScreenHeight / this._zoom, + width: screenW, + height: screenH, + cssWidth: screenW / this._zoom, + cssHeight: screenH / this._zoom, pageLeft: 0, pageTop: 0, - pageRight: gScreenWidth, - pageBottom: gScreenHeight, + pageRight: screenW, + pageBottom: screenH, // We make up matching css page dimensions cssPageLeft: 0, cssPageTop: 0, - cssPageRight: gScreenWidth / this._zoom, - cssPageBottom: gScreenHeight / this._zoom, + cssPageRight: screenW / this._zoom, + cssPageBottom: screenH / this._zoom, zoom: this._zoom, }; @@ -3388,6 +3424,20 @@ Tab.prototype = { let displayPort = getBridge().getDisplayPort(aPageSizeUpdate, BrowserApp.isBrowserContentDocumentDisplayed(), this.id, viewport); if (displayPort != null) this.setDisplayPort(displayPort); + + // If the page size has changed so that it might or might not fit on the + // screen with the margins included, run updateViewportSize to resize the + // browser accordingly. The -1 is to account for rounding errors. + if (!this.updatingViewportForPageSizeChange) { + this.updatingViewportForPageSizeChange = true; + if (((viewport.pageBottom - viewport.pageTop <= gScreenHeight - 1) != + this.viewportExcludesVerticalMargins) || + ((viewport.pageRight - viewport.pageLeft <= gScreenWidth - 1) != + this.viewportExcludesHorizontalMargins)) { + this.updateViewportSize(gScreenWidth); + } + this.updatingViewportForPageSizeChange = false; + } }, handleEvent: function(aEvent) { @@ -3814,9 +3864,11 @@ Tab.prototype = { if (!browser) return; - let screenW = gScreenWidth; - let screenH = gScreenHeight; + let screenW = gScreenWidth - gViewportMargins.left - gViewportMargins.right; + let screenH = gScreenHeight - gViewportMargins.top - gViewportMargins.bottom; let viewportW, viewportH; + this.viewportExcludesHorizontalMargins = true; + this.viewportExcludesVerticalMargins = true; let metadata = this.metadata; if (metadata.autoSize) { @@ -3871,7 +3923,21 @@ Tab.prototype = { // this may get run during a Viewport:Change message while the document // has not yet loaded, so need to guard against a null document. let [pageWidth, pageHeight] = this.getPageSize(this.browser.contentDocument, viewportW, viewportH); - minScale = gScreenWidth / pageWidth; + + minScale = screenW / pageWidth; + + // In the situation the page size exceeds the screen size minus the + // viewport margins on either axis, lengthen the viewport on the + // corresponding axis to include the margins. + // The +1 is to account for rounding errors. + if (pageWidth * this._zoom >= screenW + 1) { + screenW = gScreenWidth; + this.viewportExcludesHorizontalMargins = false; + } + if (pageHeight * this._zoom >= screenH + 1) { + screenH = gScreenHeight; + this.viewportExcludesVerticalMargins = false; + } } minScale = this.clampZoom(minScale); viewportH = Math.max(viewportH, screenH / minScale); From b29bf4713dc21f2ceb55a74b0bb7d23fe4b96c66 Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Fri, 1 Mar 2013 15:46:34 +0000 Subject: [PATCH 122/140] Bug 716403 - Add nsIDOMWindowUtils.setContentDocumentFixedPositionMargins. r=roc,dholbert Add a function to set margins on the viewport that are respected by its absolute containing block. This has the effect of being able to add margins to fixed position content, which will be used on mobile to stop temporarily visible chrome from obscuring content. --- dom/base/nsDOMWindowUtils.cpp | 26 +++++++++++++++ dom/interfaces/base/nsIDOMWindowUtils.idl | 15 ++++++++- layout/base/nsIPresShell.h | 22 +++++++++++-- layout/base/nsPresShell.cpp | 34 ++++++++++++++------ layout/generic/nsAbsoluteContainingBlock.cpp | 12 ++++++- 5 files changed, 95 insertions(+), 14 deletions(-) diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index f069f92269e..f5029e77815 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -2916,6 +2916,32 @@ nsDOMWindowUtils::SetScrollPositionClampingScrollPortSize(float aWidth, float aH return NS_OK; } +NS_IMETHODIMP +nsDOMWindowUtils::SetContentDocumentFixedPositionMargins(float aTop, float aRight, + float aBottom, float aLeft) +{ + if (!nsContentUtils::IsCallerChrome()) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + if (!(aTop >= 0.0f && aRight >= 0.0f && aBottom >= 0.0f && aLeft >= 0.0f)) { + return NS_ERROR_ILLEGAL_VALUE; + } + + nsIPresShell* presShell = GetPresShell(); + if (!presShell) { + return NS_ERROR_FAILURE; + } + + nsMargin margins(nsPresContext::CSSPixelsToAppUnits(aLeft), + nsPresContext::CSSPixelsToAppUnits(aTop), + nsPresContext::CSSPixelsToAppUnits(aRight), + nsPresContext::CSSPixelsToAppUnits(aBottom)); + presShell->SetContentDocumentFixedPositionMargins(margins); + + return NS_OK; +} + nsresult nsDOMWindowUtils::RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement, const nsAString& aNewOrigin) diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 6180695a2b3..b66073b555c 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -41,7 +41,7 @@ interface nsIDOMClientRect; interface nsIURI; interface nsIDOMEventTarget; -[scriptable, uuid(9ab64e24-7f8d-11e2-9a5f-a24379957a39)] +[scriptable, uuid(69b7d908-c8e4-4416-94cf-4fdbe7a80441)] interface nsIDOMWindowUtils : nsISupports { /** @@ -1288,6 +1288,19 @@ interface nsIDOMWindowUtils : nsISupports { */ void setScrollPositionClampingScrollPortSize(in float aWidth, in float aHeight); + /** + * Set margins for the layout of fixed position elements in the content + * document. These are used on mobile, where the viewable area can be + * temporarily obscured by the browser chrome. In this situation, we're ok + * with scrollable page content being obscured, but fixed position content + * cannot be revealed without removing the obscuring chrome, so we use these + * margins so that it can remain visible. + * + * The caller of this method must have chrome privileges. + */ + void setContentDocumentFixedPositionMargins(in float aTop, in float aRight, + in float aBottom, in float aLeft); + /** * Prevent this window (and any child windows) from displaying any further * dialogs (e.g. window.alert()). diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index 5f27d3d92be..47c0393f295 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -121,10 +121,10 @@ typedef struct CapturingContentInfo { nsIContent* mContent; } CapturingContentInfo; -// 835b3946-1a4f-4132-b3ce-2e2e8be377c8 +// bf539e0a-c314-4ea7-ba7c-ebd34e8a4065 #define NS_IPRESSHELL_IID \ -{ 0x835b3946, 0x1a4f, 0x4132, \ - { 0xb3, 0xce, 0x2e, 0x2e, 0x8b, 0xe3, 0x77, 0xc8 } } +{ 0xbf539e0a, 0xc314, 0x4ea7, \ + { 0xba, 0x7c, 0xeb, 0xd3, 0x4e, 0x8a, 0x40, 0x65 } } // debug VerifyReflow flags #define VERIFY_REFLOW_ON 0x01 @@ -498,6 +498,11 @@ public: IntrinsicDirty aIntrinsicDirty, nsFrameState aBitToAdd) = 0; + /** + * Calls FrameNeedsReflow on all fixed position children of the root frame. + */ + virtual void ReflowFixedPositionChildren(IntrinsicDirty aIntrinsicDirty); + /** * Tell the presshell that the given frame's reflow was interrupted. This * will mark as having dirty children a path from the given frame (inclusive) @@ -1381,6 +1386,11 @@ public: return mScrollPositionClampingScrollPortSize; } + void SetContentDocumentFixedPositionMargins(const nsMargin& aMargins); + const nsMargin& GetContentDocumentFixedPositionMargins() { + return mContentDocumentFixedPositionMargins; + } + virtual void WindowSizeMoveDone() = 0; virtual void SysColorChanged() = 0; virtual void ThemeChanged() = 0; @@ -1428,6 +1438,12 @@ protected: nsSize mScrollPositionClampingScrollPortSize; + // This margin is intended to be used when laying out fixed position children + // on this PresShell's viewport frame. See the documentation of + // nsIDOMWindowUtils.setContentDocumentFixedPositionMargins for details of + // their use. + nsMargin mContentDocumentFixedPositionMargins; + // A list of weak frames. This is a pointer to the last item in the list. nsWeakFrame* mWeakFrames; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 95f0062fe8b..fb182d81ca7 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -9429,6 +9429,18 @@ PresShell::SizeOfTextRuns(nsMallocSizeOfFun aMallocSizeOf) const /* clear = */false); } +void +nsIPresShell::ReflowFixedPositionChildren(IntrinsicDirty aIntrinsicDirty) +{ + nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); + if (rootFrame) { + const nsFrameList& childList = rootFrame->GetChildList(nsIFrame::kFixedList); + for (nsFrameList::Enumerator e(childList); !e.AtEnd(); e.Next()) { + FrameNeedsReflow(e.get(), aIntrinsicDirty, NS_FRAME_IS_DIRTY); + } + } +} + void nsIPresShell::SetScrollPositionClampingScrollPortSize(nscoord aWidth, nscoord aHeight) { @@ -9439,18 +9451,22 @@ nsIPresShell::SetScrollPositionClampingScrollPortSize(nscoord aWidth, nscoord aH mScrollPositionClampingScrollPortSize.width = aWidth; mScrollPositionClampingScrollPortSize.height = aHeight; - // Reflow fixed position children. - nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); - if (rootFrame) { - const nsFrameList& childList = rootFrame->GetChildList(nsIFrame::kFixedList); - for (nsIFrame* child = childList.FirstChild(); child; - child = child->GetNextSibling()) { - FrameNeedsReflow(child, eResize, NS_FRAME_IS_DIRTY); - } - } + ReflowFixedPositionChildren(eResize); } } +void +nsIPresShell::SetContentDocumentFixedPositionMargins(const nsMargin& aMargins) +{ + if (mContentDocumentFixedPositionMargins == aMargins) { + return; + } + + mContentDocumentFixedPositionMargins = aMargins; + + ReflowFixedPositionChildren(eResize); +} + void PresShell::SetupFontInflation() { diff --git a/layout/generic/nsAbsoluteContainingBlock.cpp b/layout/generic/nsAbsoluteContainingBlock.cpp index 75ce0417b2c..3b0a34a0fca 100644 --- a/layout/generic/nsAbsoluteContainingBlock.cpp +++ b/layout/generic/nsAbsoluteContainingBlock.cpp @@ -366,7 +366,17 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat nsresult rv; // Get the border values - const nsMargin& border = aReflowState.mStyleBorder->GetComputedBorder(); + nsMargin border = aReflowState.mStyleBorder->GetComputedBorder(); + + // Respect fixed position margins. + if (aDelegatingFrame->GetAbsoluteListID() == nsIFrame::kFixedList) { + const nsMargin& fixedMargins = aPresContext->PresShell()-> + GetContentDocumentFixedPositionMargins(); + + border += fixedMargins; + aContainingBlockWidth -= fixedMargins.left + fixedMargins.right; + aContainingBlockHeight -= fixedMargins.top + fixedMargins.bottom; + } nscoord availWidth = aContainingBlockWidth; if (availWidth == -1) { From fa6c4dbff38969c94a3b8105a9784e4327cd69e1 Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Fri, 1 Mar 2013 15:46:34 +0000 Subject: [PATCH 123/140] Bug 716403 - Annotate layers with the fixed margins from the PresShell. r=nrc,roc This annotates fixed layers with the margins that have been applied from nsIPresShell->GetContentDocumentFixedPositionMargins. Using SyncViewportInfo in CompositorParent, this allows for async fixed position margin setting. --- gfx/2d/Rect.h | 1 + gfx/layers/Layers.cpp | 1 + gfx/layers/Layers.h | 12 ++++++++++++ gfx/layers/ipc/CompositorParent.cpp | 16 +++++++++++----- gfx/layers/ipc/PLayers.ipdl | 2 ++ gfx/layers/ipc/ShadowLayers.cpp | 1 + gfx/layers/ipc/ShadowLayersParent.cpp | 1 + ipc/glue/IPCMessageUtils.h | 22 ++++++++++++++++++++++ layout/base/nsDisplayList.cpp | 13 +++++++++++++ 9 files changed, 64 insertions(+), 5 deletions(-) diff --git a/gfx/2d/Rect.h b/gfx/2d/Rect.h index 00e5fe32001..c46de5576f3 100644 --- a/gfx/2d/Rect.h +++ b/gfx/2d/Rect.h @@ -18,6 +18,7 @@ struct Margin : typedef BaseMargin Super; // Constructors + Margin() : Super(0, 0, 0, 0) {} Margin(const Margin& aMargin) : Super(aMargin) {} Margin(Float aLeft, Float aTop, Float aRight, Float aBottom) : Super(aLeft, aTop, aRight, aBottom) {} diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index b4a464fb9ac..46700468c53 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -320,6 +320,7 @@ Layer::Layer(LayerManager* aManager, void* aImplData) : mUseClipRect(false), mUseTileSourceRect(false), mIsFixedPosition(false), + mMargins(0, 0, 0, 0), mDebugColorIndex(0), mAnimationGeneration(0) {} diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index 2d2184dccc8..6b19c7afc4f 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -844,6 +844,16 @@ public: */ void SetFixedPositionAnchor(const gfxPoint& aAnchor) { mAnchor = aAnchor; } + /** + * CONSTRUCTION PHASE ONLY + * If a layer represents a fixed position element or elements that are on + * a document that has had fixed position element margins set on it, these + * will be mirrored here. This allows for asynchronous animation of the + * margins by reconciling the difference between this value and a value that + * is updated more frequently. + */ + void SetFixedPositionMargins(const gfx::Margin& aMargins) { mMargins = aMargins; } + // These getters can be used anytime. float GetOpacity() { return mOpacity; } const nsIntRect* GetClipRect() { return mUseClipRect ? &mClipRect : nullptr; } @@ -860,6 +870,7 @@ public: float GetPostYScale() { return mPostYScale; } bool GetIsFixedPosition() { return mIsFixedPosition; } gfxPoint GetFixedPositionAnchor() { return mAnchor; } + const gfx::Margin& GetFixedPositionMargins() { return mMargins; } Layer* GetMaskLayer() { return mMaskLayer; } // Note that all lengths in animation data are either in CSS pixels or app @@ -1208,6 +1219,7 @@ protected: bool mUseTileSourceRect; bool mIsFixedPosition; gfxPoint mAnchor; + gfx::Margin mMargins; DebugOnly mDebugColorIndex; // If this layer is used for OMTA, then this counter is used to ensure we // stay in sync with the animation manager diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index e065c48fac9..694e3c9baee 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -652,17 +652,23 @@ CompositorParent::TransformFixedLayers(Layer* aLayer, gfxPoint translation(aTranslation - (anchor - anchor / aScaleDiff)); // Offset this translation by the fixed layer margins, depending on what - // side of the viewport the layer is anchored to. + // side of the viewport the layer is anchored to, reconciling the + // difference between the current fixed layer margins and the Gecko-side + // fixed layer margins. + // aFixedLayerMargins are the margins we expect to be at at the current + // time, obtained via SyncViewportInfo, and fixedMargins are the margins + // that were used during layout. + const gfx::Margin& fixedMargins = aLayer->GetFixedPositionMargins(); if (anchor.x > 0) { - translation.x -= aFixedLayerMargins.right; + translation.x -= aFixedLayerMargins.right - fixedMargins.right; } else { - translation.x += aFixedLayerMargins.left; + translation.x += aFixedLayerMargins.left - fixedMargins.left; } if (anchor.y > 0) { - translation.y -= aFixedLayerMargins.bottom; + translation.y -= aFixedLayerMargins.bottom - fixedMargins.bottom; } else { - translation.y += aFixedLayerMargins.top; + translation.y += aFixedLayerMargins.top - fixedMargins.top; } // The transform already takes the resolution scale into account. Since we diff --git a/gfx/layers/ipc/PLayers.ipdl b/gfx/layers/ipc/PLayers.ipdl index 0d13b935369..2f42fa85688 100644 --- a/gfx/layers/ipc/PLayers.ipdl +++ b/gfx/layers/ipc/PLayers.ipdl @@ -26,6 +26,7 @@ using mozilla::TimeStamp; using mozilla::ScreenRotation; using nsCSSProperty; using mozilla::dom::ScreenOrientation; +using mozilla::gfx::Margin; /** * The layers protocol is spoken between thread contexts that manage @@ -185,6 +186,7 @@ struct CommonLayerAttributes { nsIntRect clipRect; bool isFixedPosition; gfxPoint fixedPositionAnchor; + Margin fixedPositionMargin; nullable PLayer maskLayer; // Animated colors will only honored for ColorLayers. Animation[] animations; diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index d0a9dc6f202..4d365e3d573 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -344,6 +344,7 @@ ShadowLayerForwarder::EndTransaction(InfallibleTArray* aReplies) *mutant->GetClipRect() : nsIntRect()); common.isFixedPosition() = mutant->GetIsFixedPosition(); common.fixedPositionAnchor() = mutant->GetFixedPositionAnchor(); + common.fixedPositionMargin() = mutant->GetFixedPositionMargins(); if (Layer* maskLayer = mutant->GetMaskLayer()) { common.maskLayerChild() = Shadow(maskLayer->AsShadowableLayer()); } else { diff --git a/gfx/layers/ipc/ShadowLayersParent.cpp b/gfx/layers/ipc/ShadowLayersParent.cpp index a2c266aeb2f..7b5847be608 100644 --- a/gfx/layers/ipc/ShadowLayersParent.cpp +++ b/gfx/layers/ipc/ShadowLayersParent.cpp @@ -248,6 +248,7 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray& cset, layer->SetPostScale(common.postXScale(), common.postYScale()); layer->SetIsFixedPosition(common.isFixedPosition()); layer->SetFixedPositionAnchor(common.fixedPositionAnchor()); + layer->SetFixedPositionMargins(common.fixedPositionMargin()); if (PLayerParent* maskLayer = common.maskLayerParent()) { layer->SetMaskLayer(cast(maskLayer)->AsLayer()); } else { diff --git a/ipc/glue/IPCMessageUtils.h b/ipc/glue/IPCMessageUtils.h index 33cfd4d61a2..03c28af43cb 100644 --- a/ipc/glue/IPCMessageUtils.h +++ b/ipc/glue/IPCMessageUtils.h @@ -836,6 +836,28 @@ struct ParamTraits } }; +template<> +struct ParamTraits +{ + typedef mozilla::gfx::Margin paramType; + + static void Write(Message* msg, const paramType& param) + { + WriteParam(msg, param.left); + WriteParam(msg, param.top); + WriteParam(msg, param.right); + WriteParam(msg, param.bottom); + } + + static bool Read(const Message* msg, void** iter, paramType* result) + { + return (ReadParam(msg, iter, &result->left) && + ReadParam(msg, iter, &result->top) && + ReadParam(msg, iter, &result->right) && + ReadParam(msg, iter, &result->bottom)); + } +}; + template<> struct ParamTraits { diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index b0ec5877248..058eb1214e5 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -2978,6 +2978,19 @@ nsDisplayFixedPosition::BuildLayer(nsDisplayListBuilder* aBuilder, layer->SetFixedPositionAnchor(anchor); + // Also make sure the layer is aware of any fixed position margins that have + // been set. + nsMargin fixedMargins = presContext->PresShell()->GetContentDocumentFixedPositionMargins(); + mozilla::gfx::Margin fixedLayerMargins(NSAppUnitsToFloatPixels(fixedMargins.left, factor) * + aContainerParameters.mXScale, + NSAppUnitsToFloatPixels(fixedMargins.top, factor) * + aContainerParameters.mYScale, + NSAppUnitsToFloatPixels(fixedMargins.right, factor) * + aContainerParameters.mXScale, + NSAppUnitsToFloatPixels(fixedMargins.bottom, factor) * + aContainerParameters.mYScale); + layer->SetFixedPositionMargins(fixedLayerMargins); + return layer.forget(); } From 08f9e1089b2a865ba72d42920a50259d04042779 Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Fri, 1 Mar 2013 15:46:34 +0000 Subject: [PATCH 124/140] Bug 716403 - Use setContentDocumentFixedPositionMargins in Android's browser.js. r=kats This uses the aforementioned method on nsIDOMWindowUtils to make sure layout's idea of the fixed position margins matches those used in the compositor. --- mobile/android/base/GeckoEvent.java | 4 ++++ mobile/android/base/gfx/GeckoLayerClient.java | 17 ++++++++++++++- mobile/android/chrome/content/browser.js | 21 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java index 994c9e7688b..6ca04aeb728 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -548,6 +548,10 @@ public class GeckoEvent { sb.append("{ \"x\" : ").append(metrics.viewportRectLeft) .append(", \"y\" : ").append(metrics.viewportRectTop) .append(", \"zoom\" : ").append(metrics.zoomFactor) + .append(", \"fixedMarginLeft\" : ").append(metrics.fixedLayerMarginLeft) + .append(", \"fixedMarginTop\" : ").append(metrics.fixedLayerMarginTop) + .append(", \"fixedMarginRight\" : ").append(metrics.fixedLayerMarginRight) + .append(", \"fixedMarginBottom\" : ").append(metrics.fixedLayerMarginBottom) .append(", \"displayPort\" :").append(displayPort.toJSON()) .append('}'); event.mCharactersExtra = sb.toString(); diff --git a/mobile/android/base/gfx/GeckoLayerClient.java b/mobile/android/base/gfx/GeckoLayerClient.java index 7951b7671ea..2d28b76877e 100644 --- a/mobile/android/base/gfx/GeckoLayerClient.java +++ b/mobile/android/base/gfx/GeckoLayerClient.java @@ -261,6 +261,20 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget ImmutableViewportMetrics metrics = getViewportMetrics(); ImmutableViewportMetrics clampedMetrics = metrics.clamp(); + // See if we need to alter the fixed margins - Fixed margins would + // otherwise be set even when the document is in overscroll and they're + // unnecessary. + if ((metrics.fixedLayerMarginLeft > 0 && metrics.viewportRectLeft < metrics.pageRectLeft) || + (metrics.fixedLayerMarginTop > 0 && metrics.viewportRectTop < metrics.pageRectTop) || + (metrics.fixedLayerMarginRight > 0 && metrics.viewportRectRight > metrics.pageRectRight) || + (metrics.fixedLayerMarginBottom > 0 && metrics.viewportRectBottom > metrics.pageRectBottom)) { + clampedMetrics = clampedMetrics.setFixedLayerMargins( + Math.max(0, metrics.fixedLayerMarginLeft + Math.min(0, metrics.viewportRectLeft - metrics.pageRectLeft)), + Math.max(0, metrics.fixedLayerMarginTop + Math.min(0, metrics.viewportRectTop - metrics.pageRectTop)), + Math.max(0, metrics.fixedLayerMarginRight + Math.min(0, (metrics.pageRectRight - metrics.viewportRectRight))), + Math.max(0, metrics.fixedLayerMarginBottom + Math.min(0, (metrics.pageRectBottom - metrics.viewportRectBottom)))); + } + if (displayPort == null) { displayPort = DisplayPortCalculator.calculate(metrics, mPanZoomController.getVelocityVector()); } @@ -363,7 +377,8 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget newMetrics = newMetrics.clampWithMargins(); } - setViewportMetrics(newMetrics, false); + mForceRedraw = true; + setViewportMetrics(newMetrics, true); mView.requestRender(); } diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 814333872bd..6bae80e2adc 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2799,6 +2799,10 @@ function Tab(aURL, aParams) { this.showProgress = true; this._zoom = 1.0; this._drawZoom = 1.0; + this._fixedMarginLeft = 0; + this._fixedMarginTop = 0; + this._fixedMarginRight = 0; + this._fixedMarginBottom = 0; this.userScrollPos = { x: 0, y: 0 }; this.viewportExcludesHorizontalMargins = true; this.viewportExcludesVerticalMargins = true; @@ -3335,6 +3339,19 @@ Tab.prototype = { if (aViewport.displayPort) this.setDisplayPort(aViewport.displayPort); + // Store fixed margins for later retrieval in getViewport. + this._fixedMarginLeft = aViewport.fixedMarginLeft; + this._fixedMarginTop = aViewport.fixedMarginTop; + this._fixedMarginRight = aViewport.fixedMarginRight; + this._fixedMarginBottom = aViewport.fixedMarginBottom; + + let dwi = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + dwi.setContentDocumentFixedPositionMargins( + aViewport.fixedMarginTop / aViewport.zoom, + aViewport.fixedMarginRight / aViewport.zoom, + aViewport.fixedMarginBottom / aViewport.zoom, + aViewport.fixedMarginLeft / aViewport.zoom); + Services.obs.notifyObservers(null, "after-viewport-change", ""); }, @@ -3375,6 +3392,10 @@ Tab.prototype = { cssPageTop: 0, cssPageRight: screenW / this._zoom, cssPageBottom: screenH / this._zoom, + fixedMarginLeft: this._fixedMarginLeft, + fixedMarginTop: this._fixedMarginTop, + fixedMarginRight: this._fixedMarginRight, + fixedMarginBottom: this._fixedMarginBottom, zoom: this._zoom, }; From 4f03c350fb54dc9d08b852a467bb0d462ba251e0 Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Fri, 1 Mar 2013 15:46:35 +0000 Subject: [PATCH 125/140] Bug 716403 - Hide dynamic toolbar hiding behind a pref. r=kats Disable dynamic toolbar hiding on Android by default, with pref browser.chrome.dynamictoolbar available to enable it. --- mobile/android/app/mobile.js | 3 + mobile/android/base/BrowserApp.java | 93 ++++++++++++++++--- mobile/android/base/BrowserToolbarLayout.java | 14 +-- mobile/android/base/gfx/GeckoLayerClient.java | 8 ++ .../base/resources/layout/gecko_app.xml.in | 9 +- 5 files changed, 101 insertions(+), 26 deletions(-) diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index 39daf44b171..28b6b22db0d 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -677,6 +677,9 @@ pref("memory.free_dirty_pages", true); pref("layout.imagevisibility.enabled", false); +// Disable the dynamic toolbar +pref("browser.chrome.dynamictoolbar", false); + #ifdef MOZ_PKG_SPECIAL // Disable webgl on ARMv6 because running the reftests takes // too long for some reason (bug 843738) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 14027bf736f..2b9511b1198 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -55,6 +55,8 @@ abstract public class BrowserApp extends GeckoApp PropertyAnimator.PropertyAnimationListener { private static final String LOGTAG = "GeckoBrowserApp"; + private static final String PREF_CHROME_DYNAMICTOOLBAR = "browser.chrome.dynamictoolbar"; + public static BrowserToolbar mBrowserToolbar; private AboutHomeContent mAboutHomeContent; private Boolean mAboutHomeShowing = null; @@ -91,6 +93,8 @@ abstract public class BrowserApp extends GeckoApp // Variables used for scrolling the toolbar on/off the page. private static final int TOOLBAR_ONLOAD_HIDE_DELAY = 2000; + private boolean mDynamicToolbarEnabled = false; + private View mToolbarSpacer = null; private float mLastTouchY = 0.0f; private float mToolbarSubpixelAccumulation = 0.0f; private boolean mToolbarLocked = true; @@ -108,9 +112,11 @@ abstract public class BrowserApp extends GeckoApp if ("about:home".equals(tab.getURL())) { showAboutHome(); - // Show the toolbar immediately. - mBrowserToolbar.animateVisibility(true, 0); - mToolbarLocked = true; + if (mDynamicToolbarEnabled) { + // Show the toolbar immediately. + mBrowserToolbar.animateVisibility(true, 0); + mToolbarLocked = true; + } } else { hideAboutHome(); } @@ -136,16 +142,20 @@ abstract public class BrowserApp extends GeckoApp if (Tabs.getInstance().isSelectedTab(tab)) { invalidateOptionsMenu(); - // Show the toolbar. - mBrowserToolbar.animateVisibility(true, 0); + if (mDynamicToolbarEnabled) { + // Show the toolbar. + mBrowserToolbar.animateVisibility(true, 0); + } } break; case LOAD_ERROR: case STOP: if (Tabs.getInstance().isSelectedTab(tab)) { if (!mAboutHomeShowing) { - // Hide the toolbar after a delay. - mBrowserToolbar.animateVisibility(false, TOOLBAR_ONLOAD_HIDE_DELAY); + if (mDynamicToolbarEnabled) { + // Hide the toolbar after a delay. + mBrowserToolbar.animateVisibility(false, TOOLBAR_ONLOAD_HIDE_DELAY); + } } } // fall through @@ -178,6 +188,10 @@ abstract public class BrowserApp extends GeckoApp @Override public boolean onInterceptTouchEvent(View view, MotionEvent event) { + if (!mDynamicToolbarEnabled) { + return super.onInterceptTouchEvent(view, event); + } + int action = event.getActionMasked(); int pointerCount = event.getPointerCount(); @@ -315,8 +329,10 @@ abstract public class BrowserApp extends GeckoApp super.onCreate(savedInstanceState); + mToolbarSpacer = findViewById(R.id.toolbar_spacer); + LinearLayout actionBar = (LinearLayout) getActionBarLayout(); - mMainLayout.addView(actionBar, 1); + mMainLayout.addView(actionBar, 2); ((GeckoApp.MainLayout) mMainLayout).setOnInterceptTouchListener(new HideTabsTouchListener()); @@ -339,6 +355,33 @@ abstract public class BrowserApp extends GeckoApp Distribution.init(this, getPackageResourcePath()); JavaAddonManager.getInstance().init(getApplicationContext()); + + // Load the dynamic toolbar pref + PrefsHelper.getPref(PREF_CHROME_DYNAMICTOOLBAR, new PrefsHelper.PrefHandlerBase() { + @Override + public void prefValue(String pref, boolean value) { + if (value == mDynamicToolbarEnabled) { + return; + } + mDynamicToolbarEnabled = value; + + mMainHandler.post(new Runnable() { + @Override + public void run() { + if (!mDynamicToolbarEnabled) { + // Immediately show the toolbar when disabling the dynamic + // toolbar. + mBrowserToolbar.cancelVisibilityAnimation(); + mBrowserToolbar.getLayout().scrollTo(0, 0); + } + + // Refresh the margins to reset the padding on the spacer and + // make sure that Gecko is in sync. + ((BrowserToolbarLayout)mBrowserToolbar.getLayout()).refreshMargins(); + } + }); + } + }); } @Override @@ -401,6 +444,29 @@ abstract public class BrowserApp extends GeckoApp } } + public void setToolbarHeight(int aHeight, int aVisibleHeight) { + if (!mDynamicToolbarEnabled || Boolean.TRUE.equals(mAboutHomeShowing)) { + // Use aVisibleHeight here so that when the dynamic toolbar is + // enabled, the padding will animate with the toolbar becoming + // visible. + mToolbarSpacer.setPadding(0, aVisibleHeight, 0, 0); + aHeight = aVisibleHeight = 0; + } else { + mToolbarSpacer.setPadding(0, 0, 0, 0); + } + + // In the current UI, this is the only place we have need of + // viewport margins (to stop the toolbar from obscuring fixed-pos + // content). + GeckoAppShell.sendEventToGecko( + GeckoEvent.createBroadcastEvent("Viewport:FixedMarginsChanged", + "{ \"top\" : " + aHeight + ", \"right\" : 0, \"bottom\" : 0, \"left\" : 0 }")); + + if (mLayerView != null) { + mLayerView.getLayerClient().setFixedLayerMargins(0, aVisibleHeight, 0, 0); + } + } + @Override void toggleChrome(final boolean aShow) { mMainHandler.post(new Runnable() { @@ -443,10 +509,6 @@ abstract public class BrowserApp extends GeckoApp mBrowserToolbar.from(actionBar); mBrowserToolbar.refresh(); - if (mAboutHomeContent != null) { - mAboutHomeContent.setPadding(0, mBrowserToolbar.getLayout().getHeight(), 0, 0); - } - // The favicon view is different now, so we need to update the DoorHangerPopup anchor view. if (mDoorHangerPopup != null) mDoorHangerPopup.setAnchor(mBrowserToolbar.mFavicon); @@ -859,6 +921,9 @@ abstract public class BrowserApp extends GeckoApp mAboutHomeShowing = true; Runnable r = new AboutHomeRunnable(true); mMainHandler.postAtFrontOfQueue(r); + + // Refresh margins to possibly remove the toolbar padding + ((BrowserToolbarLayout)mBrowserToolbar.getLayout()).refreshMargins(); } private void hideAboutHome() { @@ -871,6 +936,9 @@ abstract public class BrowserApp extends GeckoApp mAboutHomeShowing = false; Runnable r = new AboutHomeRunnable(false); mMainHandler.postAtFrontOfQueue(r); + + // Refresh margins to possibly restore the toolbar padding + ((BrowserToolbarLayout)mBrowserToolbar.getLayout()).refreshMargins(); } private class AboutHomeRunnable implements Runnable { @@ -899,7 +967,6 @@ abstract public class BrowserApp extends GeckoApp mAboutHomeStartupTimer.stop(); } }); - mAboutHomeContent.setPadding(0, mBrowserToolbar.getLayout().getHeight(), 0, 0); } else { mAboutHomeContent.update(EnumSet.of(AboutHomeContent.UpdateFlags.TOP_SITES, AboutHomeContent.UpdateFlags.REMOTE_TABS)); diff --git a/mobile/android/base/BrowserToolbarLayout.java b/mobile/android/base/BrowserToolbarLayout.java index 5e3afa6235c..03cdaee63e2 100644 --- a/mobile/android/base/BrowserToolbarLayout.java +++ b/mobile/android/base/BrowserToolbarLayout.java @@ -45,22 +45,14 @@ public class BrowserToolbarLayout extends LinearLayout { super.onSizeChanged(w, h, oldw, oldh); if (h != oldh) { - // In the current UI, this is the only place we have need of - // viewport margins (to stop the toolbar from obscuring fixed-pos - // content). - GeckoAppShell.sendEventToGecko( - GeckoEvent.createBroadcastEvent("Viewport:FixedMarginsChanged", - "{ \"top\" : " + h + ", \"right\" : 0, \"bottom\" : 0, \"left\" : 0 }")); - refreshMargins(); } } public void refreshMargins() { - LayerView view = GeckoApp.mAppContext.getLayerView(); - if (view != null) { - view.getLayerClient().setFixedLayerMargins(0, getHeight() - getScrollY(), 0, 0); - } + int height = getHeight(); + int visibleHeight = height - getScrollY(); + ((BrowserApp)GeckoApp.mAppContext).setToolbarHeight(height, visibleHeight); } } diff --git a/mobile/android/base/gfx/GeckoLayerClient.java b/mobile/android/base/gfx/GeckoLayerClient.java index 2d28b76877e..e49d78d4bcf 100644 --- a/mobile/android/base/gfx/GeckoLayerClient.java +++ b/mobile/android/base/gfx/GeckoLayerClient.java @@ -371,6 +371,14 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget */ public void setFixedLayerMargins(float left, float top, float right, float bottom) { ImmutableViewportMetrics oldMetrics = getViewportMetrics(); + if (oldMetrics.fixedLayerMarginLeft == left && + oldMetrics.fixedLayerMarginTop == top && + oldMetrics.fixedLayerMarginRight == right && + oldMetrics.fixedLayerMarginBottom == bottom) { + // Do nothing if the margins haven't changed. + return; + } + ImmutableViewportMetrics newMetrics = oldMetrics.setFixedLayerMargins(left, top, right, bottom); if (mClampOnMarginChange) { diff --git a/mobile/android/base/resources/layout/gecko_app.xml.in b/mobile/android/base/resources/layout/gecko_app.xml.in index 1df7805c2ae..f7ea076cee5 100644 --- a/mobile/android/base/resources/layout/gecko_app.xml.in +++ b/mobile/android/base/resources/layout/gecko_app.xml.in @@ -17,13 +17,18 @@ - + + + + android:layout_weight="1" + android:layout_below="@+id/toolbar_spacer"> From ad31f58dce6426c8a355a3a6185a6d4418e071a0 Mon Sep 17 00:00:00 2001 From: Sam Foster Date: Fri, 1 Mar 2013 15:45:07 +0000 Subject: [PATCH 126/140] Bug 846685 - Add optional, optionally-async setUp, tearDown methods to metro-chrome test fixture objects. r=jimm --- browser/metro/base/tests/head.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/browser/metro/base/tests/head.js b/browser/metro/base/tests/head.js index 2d758ee673e..cd937c904e2 100644 --- a/browser/metro/base/tests/head.js +++ b/browser/metro/base/tests/head.js @@ -236,7 +236,7 @@ function waitForImageLoad(aWindow, aImageId) { */ function waitForObserver(aObsEvent, aTimeoutMs) { try { - + let deferred = Promise.defer(); let timeoutMs = aTimeoutMs || kDefaultWait; let timerID = 0; @@ -276,7 +276,7 @@ function waitForObserver(aObsEvent, aTimeoutMs) { Services.obs.addObserver(observeWatcher, aObsEvent, true); return deferred.promise; - + } catch (ex) { info(ex.message); } @@ -412,10 +412,15 @@ function runTests() { Task.spawn(function() { while((gCurrentTest = gTests.shift())){ info(gCurrentTest.desc); - yield Task.spawn(gCurrentTest.run); + if ('function' == typeof gCurrentTest.setUp) { + yield Task.spawn(gCurrentTest.setUp.bind(gCurrentTest)); + } + yield Task.spawn(gCurrentTest.run.bind(gCurrentTest)); + if ('function' == typeof gCurrentTest.tearDown) { + yield Task.spawn(gCurrentTest.tearDown.bind(gCurrentTest)); + } info("END "+gCurrentTest.desc); } - info("done with gTests while loop, calling finish"); finish(); }); } From 6f002d2d5d35f0c519647a44b8756e8d399eb389 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Fri, 1 Mar 2013 17:08:54 +0100 Subject: [PATCH 127/140] Bug 846769 - Another assertion can happen, and is not windows-only, on a CLOSED TREE r=RyanVM --- content/media/test/test_seek.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/content/media/test/test_seek.html b/content/media/test/test_seek.html index f0cc54a2183..c4bf3f99983 100644 --- a/content/media/test/test_seek.html +++ b/content/media/test/test_seek.html @@ -28,6 +28,9 @@ var manager = new MediaTestManager; // https://bugzilla.mozilla.org/show_bug.cgi?id=634747 if (navigator.platform.startsWith("Win")) { SimpleTest.expectAssertions(0, 5); +} else if (navigator.platform.startWith("Mac")) { + // This is "###!!! ASSERTION: Page read cursor should be inside range: 'mPageOffset <= endOffset'" + SimpleTest.expectAssertions(0, 1); } const NUM_SEEK_TESTS = 13; From 42f5153b7d2022bfd719ad59bc84eb80b2c7d866 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 1 Mar 2013 11:48:04 -0500 Subject: [PATCH 128/140] Backed out changeset 96c03dab9222 (bug 843725) for mochitest-5 failures on a CLOSED TREE. --- .../html/content/src/nsHTMLInputElement.cpp | 82 +------ content/html/content/test/forms/Makefile.in | 1 - .../forms/test_input_range_key_events.html | 206 ------------------ layout/style/forms.css | 6 +- 4 files changed, 3 insertions(+), 292 deletions(-) delete mode 100644 content/html/content/test/forms/test_input_range_key_events.html diff --git a/content/html/content/src/nsHTMLInputElement.cpp b/content/html/content/src/nsHTMLInputElement.cpp index 4f6164af993..44c4cdfded7 100644 --- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -2569,17 +2569,6 @@ SelectTextFieldOnFocus() return gSelectTextFieldOnFocus == 1; } -static bool -IsLTR(Element* aElement) -{ - nsIFrame *frame = aElement->GetPrimaryFrame(); - if (frame) { - return frame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR; - } - // at least for HTML, directionality is exclusively LTR or RTL - return aElement->GetDirectionality() == eDir_LTR; -} - nsresult nsHTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor) { @@ -2846,74 +2835,6 @@ nsHTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor) NS_ENSURE_SUCCESS(rv, rv); } - if (aVisitor.mEvent->message == NS_KEY_PRESS && - mType == NS_FORM_INPUT_RANGE && !keyEvent->IsAlt() && - !keyEvent->IsControl() && !keyEvent->IsMeta() && - (keyEvent->keyCode == NS_VK_LEFT || - keyEvent->keyCode == NS_VK_RIGHT || - keyEvent->keyCode == NS_VK_UP || - keyEvent->keyCode == NS_VK_DOWN || - keyEvent->keyCode == NS_VK_PAGE_UP || - keyEvent->keyCode == NS_VK_PAGE_DOWN || - keyEvent->keyCode == NS_VK_HOME || - keyEvent->keyCode == NS_VK_END)) { - double minimum = GetMinimum(); - double maximum = GetMaximum(); - MOZ_ASSERT(MOZ_DOUBLE_IS_FINITE(minimum) && - MOZ_DOUBLE_IS_FINITE(maximum)); - if (minimum < maximum) { // else the value is locked to the minimum - double value = GetValueAsDouble(); - double step = GetStep(); - if (step == kStepAny) { - step = GetDefaultStep(); - } - MOZ_ASSERT(MOZ_DOUBLE_IS_FINITE(value) && - MOZ_DOUBLE_IS_FINITE(step)); - double newValue; - switch (keyEvent->keyCode) { - case NS_VK_LEFT: - newValue = value + (IsLTR(this) ? -step : step); - break; - case NS_VK_RIGHT: - newValue = value + (IsLTR(this) ? step : -step); - break; - case NS_VK_UP: - // Even for horizontal range, "up" means "increase" - newValue = value + step; - break; - case NS_VK_DOWN: - // Even for horizontal range, "down" means "decrease" - newValue = value - step; - break; - case NS_VK_HOME: - newValue = minimum; - break; - case NS_VK_END: - newValue = maximum; - break; - case NS_VK_PAGE_UP: - // For PgUp/PgDn we jump 10% of the total range, unless step - // requires us to jump more. - newValue = value + std::max(step, 0.1 * (maximum - minimum)); - break; - case NS_VK_PAGE_DOWN: - newValue = value - std::max(step, 0.1 * (maximum - minimum)); - break; - } - MOZ_ASSERT(MOZ_DOUBLE_IS_FINITE(newValue)); - nsAutoString val; - ConvertNumberToString(newValue, val); - SetValueInternal(val, true, true); - nsIFrame* frame = GetPrimaryFrame(); - if (frame) { - // Trigger reflow to update the position of the thumb: - frame->PresContext()->GetPresShell()-> - FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY); - } - aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; - } - } - } break; // NS_KEY_PRESS || NS_KEY_UP case NS_MOUSE_BUTTON_DOWN: @@ -4432,8 +4353,7 @@ nsHTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t return true; } - if (IsSingleLineTextControl(false) || - mType == NS_FORM_INPUT_RANGE) { + if (IsSingleLineTextControl(false)) { *aIsFocusable = true; return false; } diff --git a/content/html/content/test/forms/Makefile.in b/content/html/content/test/forms/Makefile.in index 7a7cf86be2f..5f7a08373e6 100644 --- a/content/html/content/test/forms/Makefile.in +++ b/content/html/content/test/forms/Makefile.in @@ -18,7 +18,6 @@ MOCHITEST_FILES = \ test_input_attributes_reflection.html \ test_input_list_attribute.html \ test_input_email.html \ - test_input_range_key_events.html \ test_input_url.html \ test_pattern_attribute.html \ test_required_attribute.html \ diff --git a/content/html/content/test/forms/test_input_range_key_events.html b/content/html/content/test/forms/test_input_range_key_events.html deleted file mode 100644 index c8f8d182d4f..00000000000 --- a/content/html/content/test/forms/test_input_range_key_events.html +++ /dev/null @@ -1,206 +0,0 @@ - - - - - Test key events for range - - - - - - -Mozilla Bug 843725 -

-
-
-
-
-
- - diff --git a/layout/style/forms.css b/layout/style/forms.css index a7ad31fe76f..0b49e90fb53 100644 --- a/layout/style/forms.css +++ b/layout/style/forms.css @@ -720,14 +720,12 @@ meter { input[type="range"] { -moz-appearance: none; display: inline-block !important; + cursor: default; width: 12em; height: 1.3em; - margin: 0 0.7em; - /* Override some rules that apply on all input types: */ - cursor: default; background: none; border: none; - -moz-binding: none; /* we don't want any of platformHTMLBindings.xml#inputFields */ + margin: 0 0.7em; } /** From b735d97a22f6f156a6b0b1ad5397ec41e799baec Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Fri, 1 Mar 2013 17:58:12 +0100 Subject: [PATCH 129/140] Bug 846769 - The assertion also happens on Linux, pushing on a CLOSED TREE. r=RyanVM --- content/media/test/test_seek.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/content/media/test/test_seek.html b/content/media/test/test_seek.html index c4bf3f99983..d34b1cf9778 100644 --- a/content/media/test/test_seek.html +++ b/content/media/test/test_seek.html @@ -28,8 +28,9 @@ var manager = new MediaTestManager; // https://bugzilla.mozilla.org/show_bug.cgi?id=634747 if (navigator.platform.startsWith("Win")) { SimpleTest.expectAssertions(0, 5); -} else if (navigator.platform.startWith("Mac")) { +} else { // This is "###!!! ASSERTION: Page read cursor should be inside range: 'mPageOffset <= endOffset'" + // https://bugzilla.mozilla.org/show_bug.cgi?id=846769 SimpleTest.expectAssertions(0, 1); } From ea8b153d1cecc53583a64e1ac7c2dbbfecf833d7 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Fri, 1 Mar 2013 17:31:38 +0000 Subject: [PATCH 130/140] Backed out changeset 1eda65741914 (bug 845804) for xpcshell failures on a CLOSED TREE --- xpcom/base/nsCycleCollector.cpp | 28 ++++++++++++++++++++++------ xpcom/base/nsMemoryInfoDumper.cpp | 11 ++++------- xpcom/base/nsMemoryInfoDumper.h | 9 --------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index d6fdba5d3eb..b0bdc0e71cf 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -123,7 +123,6 @@ #include "nsIMemoryReporter.h" #include "nsIFile.h" #include "nsDirectoryServiceDefs.h" -#include "nsMemoryInfoDumper.h" #include "xpcpublic.h" #include "nsXPCOMPrivate.h" #include "sampler.h" @@ -1405,15 +1404,32 @@ private: mFilenameIdentifier.IsEmpty() ? "" : ".", NS_ConvertUTF16toUTF8(mFilenameIdentifier).get()); - // Get the log directory either from $MOZ_CC_LOG_DIRECTORY or from - // the fallback directories in OpenTempFile. + // Get the log directory either from $MOZ_CC_LOG_DIRECTORY or from our + // platform's temp directory. For Android, first try the downloads + // directory which is world-readable rather than the temp directory + // which is not. nsCOMPtr logFile; - if (char* env = PR_GetEnv("MOZ_CC_LOG_DIRECTORY")) { + char* env; + if (env = PR_GetEnv("MOZ_CC_LOG_DIRECTORY")) { NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, getter_AddRefs(logFile)); } - nsresult rv = nsMemoryInfoDumper::OpenTempFile(filename, - getter_AddRefs(logFile)); +#ifdef ANDROID + if (!logFile && (env = PR_GetEnv("DOWNLOADS_DIRECTORY"))) { + NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, + getter_AddRefs(logFile)); + } +#endif + if (!logFile) { + // Ask NSPR to point us to the temp directory. + NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(logFile)); + } + NS_ENSURE_TRUE(logFile, nullptr); + + nsresult rv = logFile->AppendNative(filename); + NS_ENSURE_SUCCESS(rv, nullptr); + + rv = logFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644); NS_ENSURE_SUCCESS(rv, nullptr); return logFile.forget(); diff --git a/xpcom/base/nsMemoryInfoDumper.cpp b/xpcom/base/nsMemoryInfoDumper.cpp index 273dd192d60..b726095d1aa 100644 --- a/xpcom/base/nsMemoryInfoDumper.cpp +++ b/xpcom/base/nsMemoryInfoDumper.cpp @@ -736,17 +736,14 @@ MakeFilename(const char *aPrefix, const nsAString &aIdentifier, getpid(), aSuffix); } -/* static */ nsresult -nsMemoryInfoDumper::OpenTempFile(const nsACString &aFilename, nsIFile* *aFile) +static nsresult +OpenTempFile(const nsACString &aFilename, nsIFile* *aFile) { #ifdef ANDROID // For Android, first try the downloads directory which is world-readable // rather than the temp directory which is not. - if (!*aFile) { - char *env = PR_GetEnv("DOWNLOADS_DIRECTORY"); - if (env) { - NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, aFile); - } + if (char *env = PR_GetEnv("DOWNLOADS_DIRECTORY")) { + NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, aFile); } #endif nsresult rv; diff --git a/xpcom/base/nsMemoryInfoDumper.h b/xpcom/base/nsMemoryInfoDumper.h index 31938a0380b..c3e02614aec 100644 --- a/xpcom/base/nsMemoryInfoDumper.h +++ b/xpcom/base/nsMemoryInfoDumper.h @@ -29,15 +29,6 @@ public: public: static void Initialize(); - /** - * This function creates a new unique file based on |aFilename| in a - * world-readable temp directory. This is the system temp directory - * or, in the case of Android, the downloads directory. If |aFile| is - * non-null, it is assumed to point to a folder, and that folder is used - * instead. - */ - static nsresult OpenTempFile(const nsACString &aFilename, nsIFile* *aFile); - private: static nsresult DumpMemoryReportsToFileImpl(const nsAString& aIdentifier); From 19ab90c5985683c57c44d99b430b7cc2b3d1d700 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 1 Mar 2013 10:41:17 -0500 Subject: [PATCH 131/140] Bug 845804 - Reuse OpenTempFile from nsMemoryInfoDumper in nsCycleCollector.cpp. r=njn CLOSED TREE --- xpcom/base/nsCycleCollector.cpp | 28 ++++++---------------------- xpcom/base/nsMemoryInfoDumper.cpp | 11 +++++++---- xpcom/base/nsMemoryInfoDumper.h | 9 +++++++++ 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index b0bdc0e71cf..d6fdba5d3eb 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -123,6 +123,7 @@ #include "nsIMemoryReporter.h" #include "nsIFile.h" #include "nsDirectoryServiceDefs.h" +#include "nsMemoryInfoDumper.h" #include "xpcpublic.h" #include "nsXPCOMPrivate.h" #include "sampler.h" @@ -1404,32 +1405,15 @@ private: mFilenameIdentifier.IsEmpty() ? "" : ".", NS_ConvertUTF16toUTF8(mFilenameIdentifier).get()); - // Get the log directory either from $MOZ_CC_LOG_DIRECTORY or from our - // platform's temp directory. For Android, first try the downloads - // directory which is world-readable rather than the temp directory - // which is not. + // Get the log directory either from $MOZ_CC_LOG_DIRECTORY or from + // the fallback directories in OpenTempFile. nsCOMPtr logFile; - char* env; - if (env = PR_GetEnv("MOZ_CC_LOG_DIRECTORY")) { + if (char* env = PR_GetEnv("MOZ_CC_LOG_DIRECTORY")) { NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, getter_AddRefs(logFile)); } -#ifdef ANDROID - if (!logFile && (env = PR_GetEnv("DOWNLOADS_DIRECTORY"))) { - NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, - getter_AddRefs(logFile)); - } -#endif - if (!logFile) { - // Ask NSPR to point us to the temp directory. - NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(logFile)); - } - NS_ENSURE_TRUE(logFile, nullptr); - - nsresult rv = logFile->AppendNative(filename); - NS_ENSURE_SUCCESS(rv, nullptr); - - rv = logFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0644); + nsresult rv = nsMemoryInfoDumper::OpenTempFile(filename, + getter_AddRefs(logFile)); NS_ENSURE_SUCCESS(rv, nullptr); return logFile.forget(); diff --git a/xpcom/base/nsMemoryInfoDumper.cpp b/xpcom/base/nsMemoryInfoDumper.cpp index b726095d1aa..273dd192d60 100644 --- a/xpcom/base/nsMemoryInfoDumper.cpp +++ b/xpcom/base/nsMemoryInfoDumper.cpp @@ -736,14 +736,17 @@ MakeFilename(const char *aPrefix, const nsAString &aIdentifier, getpid(), aSuffix); } -static nsresult -OpenTempFile(const nsACString &aFilename, nsIFile* *aFile) +/* static */ nsresult +nsMemoryInfoDumper::OpenTempFile(const nsACString &aFilename, nsIFile* *aFile) { #ifdef ANDROID // For Android, first try the downloads directory which is world-readable // rather than the temp directory which is not. - if (char *env = PR_GetEnv("DOWNLOADS_DIRECTORY")) { - NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, aFile); + if (!*aFile) { + char *env = PR_GetEnv("DOWNLOADS_DIRECTORY"); + if (env) { + NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, aFile); + } } #endif nsresult rv; diff --git a/xpcom/base/nsMemoryInfoDumper.h b/xpcom/base/nsMemoryInfoDumper.h index c3e02614aec..31938a0380b 100644 --- a/xpcom/base/nsMemoryInfoDumper.h +++ b/xpcom/base/nsMemoryInfoDumper.h @@ -29,6 +29,15 @@ public: public: static void Initialize(); + /** + * This function creates a new unique file based on |aFilename| in a + * world-readable temp directory. This is the system temp directory + * or, in the case of Android, the downloads directory. If |aFile| is + * non-null, it is assumed to point to a folder, and that folder is used + * instead. + */ + static nsresult OpenTempFile(const nsACString &aFilename, nsIFile* *aFile); + private: static nsresult DumpMemoryReportsToFileImpl(const nsAString& aIdentifier); From c5c9bf5ca149f69b4c988c0a069f8802e9ab4e1c Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 1 Mar 2013 15:21:43 -0500 Subject: [PATCH 132/140] Bug 846862 - Disable test_sts_preloadlist_perwindowpb.js and test_sts_preloadlist_selfdestruct.js on a CLOSED TREE. --- security/manager/ssl/tests/unit/xpcshell.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/security/manager/ssl/tests/unit/xpcshell.ini b/security/manager/ssl/tests/unit/xpcshell.ini index 45990cf501c..8c94eeed48f 100644 --- a/security/manager/ssl/tests/unit/xpcshell.ini +++ b/security/manager/ssl/tests/unit/xpcshell.ini @@ -13,5 +13,9 @@ skip-if = os == "android" [test_hmac.js] # Bug 676972: test hangs consistently on Android skip-if = os == "android" +# Bug 846862: disable test until bug 836097 is resolved [test_sts_preloadlist_perwindowpb.js] +skip-if = true +# Bug 846862: disable test until bug 836097 is resolved [test_sts_preloadlist_selfdestruct.js] +skip-if = true From 75acd49b0e8975782bdd0024601ced85c159e381 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 1 Mar 2013 16:09:59 -0500 Subject: [PATCH 133/140] Backed out 8 changesets (bug 716403) for frequent Android mochitest-8 failures on a CLOSED TREE. --- dom/base/nsDOMWindowUtils.cpp | 26 --- dom/interfaces/base/nsIDOMWindowUtils.idl | 15 +- gfx/2d/Rect.h | 1 - gfx/layers/Layers.cpp | 1 - gfx/layers/Layers.h | 12 -- gfx/layers/ipc/CompositorParent.cpp | 39 +--- gfx/layers/ipc/CompositorParent.h | 6 +- gfx/layers/ipc/PLayers.ipdl | 2 - gfx/layers/ipc/ShadowLayers.cpp | 1 - gfx/layers/ipc/ShadowLayersParent.cpp | 1 - ipc/glue/IPCMessageUtils.h | 22 -- layout/base/nsDisplayList.cpp | 13 -- layout/base/nsIPresShell.h | 22 +- layout/base/nsPresShell.cpp | 34 +-- layout/generic/nsAbsoluteContainingBlock.cpp | 12 +- mobile/android/app/mobile.js | 3 - mobile/android/base/BrowserApp.java | 203 +----------------- mobile/android/base/BrowserToolbar.java | 78 ------- mobile/android/base/BrowserToolbarLayout.java | 58 ----- mobile/android/base/GeckoApp.java | 70 +++--- mobile/android/base/GeckoEvent.java | 4 - mobile/android/base/Makefile.in | 1 - mobile/android/base/PropertyAnimator.java | 19 +- mobile/android/base/gfx/GeckoLayerClient.java | 77 +------ .../base/gfx/ImmutableViewportMetrics.java | 85 +------- .../base/gfx/JavaPanZoomController.java | 36 +--- mobile/android/base/gfx/ViewTransform.java | 8 - .../layout-land-v14/browser_toolbar.xml.in | 4 +- .../browser_toolbar_menu.xml.in | 4 +- .../browser_toolbar_menu.xml.in | 4 +- .../resources/layout/browser_toolbar.xml.in | 4 +- .../layout/browser_toolbar_menu.xml.in | 4 +- .../base/resources/layout/gecko_app.xml.in | 9 +- mobile/android/chrome/content/browser.js | 117 ++-------- widget/android/AndroidBridge.cpp | 6 +- widget/android/AndroidBridge.h | 3 +- widget/android/AndroidJavaWrappers.cpp | 24 +-- widget/android/AndroidJavaWrappers.h | 8 +- 38 files changed, 119 insertions(+), 917 deletions(-) delete mode 100644 mobile/android/base/BrowserToolbarLayout.java diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index f5029e77815..f069f92269e 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -2916,32 +2916,6 @@ nsDOMWindowUtils::SetScrollPositionClampingScrollPortSize(float aWidth, float aH return NS_OK; } -NS_IMETHODIMP -nsDOMWindowUtils::SetContentDocumentFixedPositionMargins(float aTop, float aRight, - float aBottom, float aLeft) -{ - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } - - if (!(aTop >= 0.0f && aRight >= 0.0f && aBottom >= 0.0f && aLeft >= 0.0f)) { - return NS_ERROR_ILLEGAL_VALUE; - } - - nsIPresShell* presShell = GetPresShell(); - if (!presShell) { - return NS_ERROR_FAILURE; - } - - nsMargin margins(nsPresContext::CSSPixelsToAppUnits(aLeft), - nsPresContext::CSSPixelsToAppUnits(aTop), - nsPresContext::CSSPixelsToAppUnits(aRight), - nsPresContext::CSSPixelsToAppUnits(aBottom)); - presShell->SetContentDocumentFixedPositionMargins(margins); - - return NS_OK; -} - nsresult nsDOMWindowUtils::RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement, const nsAString& aNewOrigin) diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index b66073b555c..6180695a2b3 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -41,7 +41,7 @@ interface nsIDOMClientRect; interface nsIURI; interface nsIDOMEventTarget; -[scriptable, uuid(69b7d908-c8e4-4416-94cf-4fdbe7a80441)] +[scriptable, uuid(9ab64e24-7f8d-11e2-9a5f-a24379957a39)] interface nsIDOMWindowUtils : nsISupports { /** @@ -1288,19 +1288,6 @@ interface nsIDOMWindowUtils : nsISupports { */ void setScrollPositionClampingScrollPortSize(in float aWidth, in float aHeight); - /** - * Set margins for the layout of fixed position elements in the content - * document. These are used on mobile, where the viewable area can be - * temporarily obscured by the browser chrome. In this situation, we're ok - * with scrollable page content being obscured, but fixed position content - * cannot be revealed without removing the obscuring chrome, so we use these - * margins so that it can remain visible. - * - * The caller of this method must have chrome privileges. - */ - void setContentDocumentFixedPositionMargins(in float aTop, in float aRight, - in float aBottom, in float aLeft); - /** * Prevent this window (and any child windows) from displaying any further * dialogs (e.g. window.alert()). diff --git a/gfx/2d/Rect.h b/gfx/2d/Rect.h index c46de5576f3..00e5fe32001 100644 --- a/gfx/2d/Rect.h +++ b/gfx/2d/Rect.h @@ -18,7 +18,6 @@ struct Margin : typedef BaseMargin Super; // Constructors - Margin() : Super(0, 0, 0, 0) {} Margin(const Margin& aMargin) : Super(aMargin) {} Margin(Float aLeft, Float aTop, Float aRight, Float aBottom) : Super(aLeft, aTop, aRight, aBottom) {} diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index 46700468c53..b4a464fb9ac 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -320,7 +320,6 @@ Layer::Layer(LayerManager* aManager, void* aImplData) : mUseClipRect(false), mUseTileSourceRect(false), mIsFixedPosition(false), - mMargins(0, 0, 0, 0), mDebugColorIndex(0), mAnimationGeneration(0) {} diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index 6b19c7afc4f..2d2184dccc8 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -844,16 +844,6 @@ public: */ void SetFixedPositionAnchor(const gfxPoint& aAnchor) { mAnchor = aAnchor; } - /** - * CONSTRUCTION PHASE ONLY - * If a layer represents a fixed position element or elements that are on - * a document that has had fixed position element margins set on it, these - * will be mirrored here. This allows for asynchronous animation of the - * margins by reconciling the difference between this value and a value that - * is updated more frequently. - */ - void SetFixedPositionMargins(const gfx::Margin& aMargins) { mMargins = aMargins; } - // These getters can be used anytime. float GetOpacity() { return mOpacity; } const nsIntRect* GetClipRect() { return mUseClipRect ? &mClipRect : nullptr; } @@ -870,7 +860,6 @@ public: float GetPostYScale() { return mPostYScale; } bool GetIsFixedPosition() { return mIsFixedPosition; } gfxPoint GetFixedPositionAnchor() { return mAnchor; } - const gfx::Margin& GetFixedPositionMargins() { return mMargins; } Layer* GetMaskLayer() { return mMaskLayer; } // Note that all lengths in animation data are either in CSS pixels or app @@ -1219,7 +1208,6 @@ protected: bool mUseTileSourceRect; bool mIsFixedPosition; gfxPoint mAnchor; - gfx::Margin mMargins; DebugOnly mDebugColorIndex; // If this layer is used for OMTA, then this counter is used to ensure we // stay in sync with the animation manager diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index 694e3c9baee..48405f89d43 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -640,8 +640,7 @@ Translate2D(gfx3DMatrix& aTransform, const gfxPoint& aOffset) void CompositorParent::TransformFixedLayers(Layer* aLayer, const gfxPoint& aTranslation, - const gfxSize& aScaleDiff, - const gfx::Margin& aFixedLayerMargins) + const gfxSize& aScaleDiff) { if (aLayer->GetIsFixedPosition() && !aLayer->GetParent()->GetIsFixedPosition()) { @@ -651,26 +650,6 @@ CompositorParent::TransformFixedLayers(Layer* aLayer, const gfxPoint& anchor = aLayer->GetFixedPositionAnchor(); gfxPoint translation(aTranslation - (anchor - anchor / aScaleDiff)); - // Offset this translation by the fixed layer margins, depending on what - // side of the viewport the layer is anchored to, reconciling the - // difference between the current fixed layer margins and the Gecko-side - // fixed layer margins. - // aFixedLayerMargins are the margins we expect to be at at the current - // time, obtained via SyncViewportInfo, and fixedMargins are the margins - // that were used during layout. - const gfx::Margin& fixedMargins = aLayer->GetFixedPositionMargins(); - if (anchor.x > 0) { - translation.x -= aFixedLayerMargins.right - fixedMargins.right; - } else { - translation.x += aFixedLayerMargins.left - fixedMargins.left; - } - - if (anchor.y > 0) { - translation.y -= aFixedLayerMargins.bottom - fixedMargins.bottom; - } else { - translation.y += aFixedLayerMargins.top - fixedMargins.top; - } - // The transform already takes the resolution scale into account. Since we // will apply the resolution scale again when computing the effective // transform, we must apply the inverse resolution scale here. @@ -697,7 +676,7 @@ CompositorParent::TransformFixedLayers(Layer* aLayer, for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { - TransformFixedLayers(child, aTranslation, aScaleDiff, aFixedLayerMargins); + TransformFixedLayers(child, aTranslation, aScaleDiff); } } @@ -890,12 +869,10 @@ CompositorParent::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFrame, 1); shadow->SetShadowTransform(transform); - gfx::Margin fixedLayerMargins(0, 0, 0, 0); TransformFixedLayers( aLayer, -treeTransform.mTranslation / treeTransform.mScale, - treeTransform.mScale, - fixedLayerMargins); + treeTransform.mScale); appliedTransform = true; } @@ -960,9 +937,8 @@ CompositorParent::TransformScrollableLayer(Layer* aLayer, const gfx3DMatrix& aRo displayPortDevPixels.x += scrollOffsetDevPixels.x; displayPortDevPixels.y += scrollOffsetDevPixels.y; - gfx::Margin fixedLayerMargins(0, 0, 0, 0); SyncViewportInfo(displayPortDevPixels, 1/rootScaleX, mLayersUpdated, - mScrollOffset, mXScale, mYScale, fixedLayerMargins); + mScrollOffset, mXScale, mYScale); mLayersUpdated = false; // Handle transformations for asynchronous panning and zooming. We determine the @@ -1019,7 +995,7 @@ CompositorParent::TransformScrollableLayer(Layer* aLayer, const gfx3DMatrix& aRo 1.0f/container->GetPostYScale(), 1); shadow->SetShadowTransform(computedTransform); - TransformFixedLayers(aLayer, offset, scaleDiff, fixedLayerMargins); + TransformFixedLayers(aLayer, offset, scaleDiff); } bool @@ -1083,12 +1059,11 @@ CompositorParent::SetPageRect(const gfx::Rect& aCssPageRect) void CompositorParent::SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated, - nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY, - gfx::Margin& aFixedLayerMargins) + nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY) { #ifdef MOZ_WIDGET_ANDROID AndroidBridge::Bridge()->SyncViewportInfo(aDisplayPort, aDisplayResolution, aLayersUpdated, - aScrollOffset, aScaleX, aScaleY, aFixedLayerMargins); + aScrollOffset, aScaleX, aScaleY); #endif } diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index e15d340127c..d77f8eab65f 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -181,8 +181,7 @@ protected: virtual void SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect); virtual void SetPageRect(const gfx::Rect& aCssPageRect); virtual void SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated, - nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY, - gfx::Margin& aFixedLayerMargins); + nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY); void SetEGLSurfaceSize(int width, int height); // If SetPanZoomControllerForLayerTree is not set, Compositor will use // derived class AsyncPanZoomController transformations. @@ -262,8 +261,7 @@ private: */ void TransformFixedLayers(Layer* aLayer, const gfxPoint& aTranslation, - const gfxSize& aScaleDiff, - const gfx::Margin& aFixedLayerMargins); + const gfxSize& aScaleDiff); virtual PGrallocBufferParent* AllocPGrallocBuffer( const gfxIntSize&, const uint32_t&, const uint32_t&, diff --git a/gfx/layers/ipc/PLayers.ipdl b/gfx/layers/ipc/PLayers.ipdl index 2f42fa85688..0d13b935369 100644 --- a/gfx/layers/ipc/PLayers.ipdl +++ b/gfx/layers/ipc/PLayers.ipdl @@ -26,7 +26,6 @@ using mozilla::TimeStamp; using mozilla::ScreenRotation; using nsCSSProperty; using mozilla::dom::ScreenOrientation; -using mozilla::gfx::Margin; /** * The layers protocol is spoken between thread contexts that manage @@ -186,7 +185,6 @@ struct CommonLayerAttributes { nsIntRect clipRect; bool isFixedPosition; gfxPoint fixedPositionAnchor; - Margin fixedPositionMargin; nullable PLayer maskLayer; // Animated colors will only honored for ColorLayers. Animation[] animations; diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index 4d365e3d573..d0a9dc6f202 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -344,7 +344,6 @@ ShadowLayerForwarder::EndTransaction(InfallibleTArray* aReplies) *mutant->GetClipRect() : nsIntRect()); common.isFixedPosition() = mutant->GetIsFixedPosition(); common.fixedPositionAnchor() = mutant->GetFixedPositionAnchor(); - common.fixedPositionMargin() = mutant->GetFixedPositionMargins(); if (Layer* maskLayer = mutant->GetMaskLayer()) { common.maskLayerChild() = Shadow(maskLayer->AsShadowableLayer()); } else { diff --git a/gfx/layers/ipc/ShadowLayersParent.cpp b/gfx/layers/ipc/ShadowLayersParent.cpp index 7b5847be608..a2c266aeb2f 100644 --- a/gfx/layers/ipc/ShadowLayersParent.cpp +++ b/gfx/layers/ipc/ShadowLayersParent.cpp @@ -248,7 +248,6 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray& cset, layer->SetPostScale(common.postXScale(), common.postYScale()); layer->SetIsFixedPosition(common.isFixedPosition()); layer->SetFixedPositionAnchor(common.fixedPositionAnchor()); - layer->SetFixedPositionMargins(common.fixedPositionMargin()); if (PLayerParent* maskLayer = common.maskLayerParent()) { layer->SetMaskLayer(cast(maskLayer)->AsLayer()); } else { diff --git a/ipc/glue/IPCMessageUtils.h b/ipc/glue/IPCMessageUtils.h index 03c28af43cb..33cfd4d61a2 100644 --- a/ipc/glue/IPCMessageUtils.h +++ b/ipc/glue/IPCMessageUtils.h @@ -836,28 +836,6 @@ struct ParamTraits } }; -template<> -struct ParamTraits -{ - typedef mozilla::gfx::Margin paramType; - - static void Write(Message* msg, const paramType& param) - { - WriteParam(msg, param.left); - WriteParam(msg, param.top); - WriteParam(msg, param.right); - WriteParam(msg, param.bottom); - } - - static bool Read(const Message* msg, void** iter, paramType* result) - { - return (ReadParam(msg, iter, &result->left) && - ReadParam(msg, iter, &result->top) && - ReadParam(msg, iter, &result->right) && - ReadParam(msg, iter, &result->bottom)); - } -}; - template<> struct ParamTraits { diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 058eb1214e5..b0ec5877248 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -2978,19 +2978,6 @@ nsDisplayFixedPosition::BuildLayer(nsDisplayListBuilder* aBuilder, layer->SetFixedPositionAnchor(anchor); - // Also make sure the layer is aware of any fixed position margins that have - // been set. - nsMargin fixedMargins = presContext->PresShell()->GetContentDocumentFixedPositionMargins(); - mozilla::gfx::Margin fixedLayerMargins(NSAppUnitsToFloatPixels(fixedMargins.left, factor) * - aContainerParameters.mXScale, - NSAppUnitsToFloatPixels(fixedMargins.top, factor) * - aContainerParameters.mYScale, - NSAppUnitsToFloatPixels(fixedMargins.right, factor) * - aContainerParameters.mXScale, - NSAppUnitsToFloatPixels(fixedMargins.bottom, factor) * - aContainerParameters.mYScale); - layer->SetFixedPositionMargins(fixedLayerMargins); - return layer.forget(); } diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index 47c0393f295..5f27d3d92be 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -121,10 +121,10 @@ typedef struct CapturingContentInfo { nsIContent* mContent; } CapturingContentInfo; -// bf539e0a-c314-4ea7-ba7c-ebd34e8a4065 +// 835b3946-1a4f-4132-b3ce-2e2e8be377c8 #define NS_IPRESSHELL_IID \ -{ 0xbf539e0a, 0xc314, 0x4ea7, \ - { 0xba, 0x7c, 0xeb, 0xd3, 0x4e, 0x8a, 0x40, 0x65 } } +{ 0x835b3946, 0x1a4f, 0x4132, \ + { 0xb3, 0xce, 0x2e, 0x2e, 0x8b, 0xe3, 0x77, 0xc8 } } // debug VerifyReflow flags #define VERIFY_REFLOW_ON 0x01 @@ -498,11 +498,6 @@ public: IntrinsicDirty aIntrinsicDirty, nsFrameState aBitToAdd) = 0; - /** - * Calls FrameNeedsReflow on all fixed position children of the root frame. - */ - virtual void ReflowFixedPositionChildren(IntrinsicDirty aIntrinsicDirty); - /** * Tell the presshell that the given frame's reflow was interrupted. This * will mark as having dirty children a path from the given frame (inclusive) @@ -1386,11 +1381,6 @@ public: return mScrollPositionClampingScrollPortSize; } - void SetContentDocumentFixedPositionMargins(const nsMargin& aMargins); - const nsMargin& GetContentDocumentFixedPositionMargins() { - return mContentDocumentFixedPositionMargins; - } - virtual void WindowSizeMoveDone() = 0; virtual void SysColorChanged() = 0; virtual void ThemeChanged() = 0; @@ -1438,12 +1428,6 @@ protected: nsSize mScrollPositionClampingScrollPortSize; - // This margin is intended to be used when laying out fixed position children - // on this PresShell's viewport frame. See the documentation of - // nsIDOMWindowUtils.setContentDocumentFixedPositionMargins for details of - // their use. - nsMargin mContentDocumentFixedPositionMargins; - // A list of weak frames. This is a pointer to the last item in the list. nsWeakFrame* mWeakFrames; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index fb182d81ca7..95f0062fe8b 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -9429,18 +9429,6 @@ PresShell::SizeOfTextRuns(nsMallocSizeOfFun aMallocSizeOf) const /* clear = */false); } -void -nsIPresShell::ReflowFixedPositionChildren(IntrinsicDirty aIntrinsicDirty) -{ - nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); - if (rootFrame) { - const nsFrameList& childList = rootFrame->GetChildList(nsIFrame::kFixedList); - for (nsFrameList::Enumerator e(childList); !e.AtEnd(); e.Next()) { - FrameNeedsReflow(e.get(), aIntrinsicDirty, NS_FRAME_IS_DIRTY); - } - } -} - void nsIPresShell::SetScrollPositionClampingScrollPortSize(nscoord aWidth, nscoord aHeight) { @@ -9451,22 +9439,18 @@ nsIPresShell::SetScrollPositionClampingScrollPortSize(nscoord aWidth, nscoord aH mScrollPositionClampingScrollPortSize.width = aWidth; mScrollPositionClampingScrollPortSize.height = aHeight; - ReflowFixedPositionChildren(eResize); + // Reflow fixed position children. + nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); + if (rootFrame) { + const nsFrameList& childList = rootFrame->GetChildList(nsIFrame::kFixedList); + for (nsIFrame* child = childList.FirstChild(); child; + child = child->GetNextSibling()) { + FrameNeedsReflow(child, eResize, NS_FRAME_IS_DIRTY); + } + } } } -void -nsIPresShell::SetContentDocumentFixedPositionMargins(const nsMargin& aMargins) -{ - if (mContentDocumentFixedPositionMargins == aMargins) { - return; - } - - mContentDocumentFixedPositionMargins = aMargins; - - ReflowFixedPositionChildren(eResize); -} - void PresShell::SetupFontInflation() { diff --git a/layout/generic/nsAbsoluteContainingBlock.cpp b/layout/generic/nsAbsoluteContainingBlock.cpp index 3b0a34a0fca..75ce0417b2c 100644 --- a/layout/generic/nsAbsoluteContainingBlock.cpp +++ b/layout/generic/nsAbsoluteContainingBlock.cpp @@ -366,17 +366,7 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat nsresult rv; // Get the border values - nsMargin border = aReflowState.mStyleBorder->GetComputedBorder(); - - // Respect fixed position margins. - if (aDelegatingFrame->GetAbsoluteListID() == nsIFrame::kFixedList) { - const nsMargin& fixedMargins = aPresContext->PresShell()-> - GetContentDocumentFixedPositionMargins(); - - border += fixedMargins; - aContainingBlockWidth -= fixedMargins.left + fixedMargins.right; - aContainingBlockHeight -= fixedMargins.top + fixedMargins.bottom; - } + const nsMargin& border = aReflowState.mStyleBorder->GetComputedBorder(); nscoord availWidth = aContainingBlockWidth; if (availWidth == -1) { diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index 28b6b22db0d..39daf44b171 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -677,9 +677,6 @@ pref("memory.free_dirty_pages", true); pref("layout.imagevisibility.enabled", false); -// Disable the dynamic toolbar -pref("browser.chrome.dynamictoolbar", false); - #ifdef MOZ_PKG_SPECIAL // Disable webgl on ARMv6 because running the reftests takes // too long for some reason (bug 843738) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 2b9511b1198..d27ca55b80b 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -8,7 +8,6 @@ package org.mozilla.gecko; import org.mozilla.gecko.db.BrowserContract.Combined; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.gfx.BitmapUtils; -import org.mozilla.gecko.gfx.ImmutableViewportMetrics; import org.mozilla.gecko.util.UiAsyncTask; import org.mozilla.gecko.util.GeckoBackgroundThread; @@ -42,7 +41,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; import android.widget.LinearLayout; -import android.widget.RelativeLayout; import android.widget.Toast; import java.io.InputStream; @@ -55,8 +53,6 @@ abstract public class BrowserApp extends GeckoApp PropertyAnimator.PropertyAnimationListener { private static final String LOGTAG = "GeckoBrowserApp"; - private static final String PREF_CHROME_DYNAMICTOOLBAR = "browser.chrome.dynamictoolbar"; - public static BrowserToolbar mBrowserToolbar; private AboutHomeContent mAboutHomeContent; private Boolean mAboutHomeShowing = null; @@ -91,14 +87,6 @@ abstract public class BrowserApp extends GeckoApp // We'll ask for feedback after the user launches the app this many times. private static final int FEEDBACK_LAUNCH_COUNT = 15; - // Variables used for scrolling the toolbar on/off the page. - private static final int TOOLBAR_ONLOAD_HIDE_DELAY = 2000; - private boolean mDynamicToolbarEnabled = false; - private View mToolbarSpacer = null; - private float mLastTouchY = 0.0f; - private float mToolbarSubpixelAccumulation = 0.0f; - private boolean mToolbarLocked = true; - @Override public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { switch(msg) { @@ -109,17 +97,10 @@ abstract public class BrowserApp extends GeckoApp // fall through case SELECTED: if (Tabs.getInstance().isSelectedTab(tab)) { - if ("about:home".equals(tab.getURL())) { + if ("about:home".equals(tab.getURL())) showAboutHome(); - - if (mDynamicToolbarEnabled) { - // Show the toolbar immediately. - mBrowserToolbar.animateVisibility(true, 0); - mToolbarLocked = true; - } - } else { + else hideAboutHome(); - } // Dismiss any SiteIdentity Popup SiteIdentityPopup.getInstance().dismiss(); @@ -138,27 +119,9 @@ abstract public class BrowserApp extends GeckoApp }); } break; - case START: - if (Tabs.getInstance().isSelectedTab(tab)) { - invalidateOptionsMenu(); - - if (mDynamicToolbarEnabled) { - // Show the toolbar. - mBrowserToolbar.animateVisibility(true, 0); - } - } - break; case LOAD_ERROR: + case START: case STOP: - if (Tabs.getInstance().isSelectedTab(tab)) { - if (!mAboutHomeShowing) { - if (mDynamicToolbarEnabled) { - // Hide the toolbar after a delay. - mBrowserToolbar.animateVisibility(false, TOOLBAR_ONLOAD_HIDE_DELAY); - } - } - } - // fall through case MENU_UPDATED: if (Tabs.getInstance().isSelectedTab(tab)) { invalidateOptionsMenu(); @@ -186,105 +149,6 @@ abstract public class BrowserApp extends GeckoApp updateAboutHomeTopSites(); } - @Override - public boolean onInterceptTouchEvent(View view, MotionEvent event) { - if (!mDynamicToolbarEnabled) { - return super.onInterceptTouchEvent(view, event); - } - - int action = event.getActionMasked(); - int pointerCount = event.getPointerCount(); - - // Whenever there are no pointers left on the screen, tell the page - // to clamp the viewport on fixed layer margin changes. This lets the - // toolbar scrolling off the top of the page make the page scroll up - // if it'd cause the page to go into overscroll, but only when there - // are no pointers held down. - mLayerView.getLayerClient().setClampOnFixedLayerMarginsChange( - pointerCount == 0 || action == MotionEvent.ACTION_CANCEL || - action == MotionEvent.ACTION_UP); - - View toolbarView = mBrowserToolbar.getLayout(); - if (action == MotionEvent.ACTION_DOWN || - action == MotionEvent.ACTION_POINTER_DOWN) { - if (pointerCount == 1) { - mToolbarLocked = false; - mToolbarSubpixelAccumulation = 0.0f; - mLastTouchY = event.getY(); - return super.onInterceptTouchEvent(view, event); - } - // - // Lock the toolbar until we're back down to one pointer. - mToolbarLocked = true; - - // Animate the toolbar to the fully on/off position. - mBrowserToolbar.animateVisibility( - toolbarView.getScrollY() > toolbarView.getHeight() / 2 ? - false : true, 0); - } - - // If more than one pointer has been tracked, let the event pass - // through and be handled by the PanZoomController for zooming. - if (pointerCount > 1) { - return super.onInterceptTouchEvent(view, event); - } - - // If a pointer has been lifted so that there's only one pointer left, - // unlock the toolbar and track that remaining pointer. - if (pointerCount == 1 && action == MotionEvent.ACTION_POINTER_UP) { - mLastTouchY = event.getY(1 - event.getActionIndex()); - mToolbarLocked = false; - return super.onInterceptTouchEvent(view, event); - } - - // Don't bother doing anything with the events if we're loading - - // the toolbar will be permanently visible in this case. - float eventY = event.getY(); - if (Tabs.getInstance().getSelectedTab().getState() != Tab.STATE_LOADING) { - int toolbarHeight = toolbarView.getHeight(); - if (action == MotionEvent.ACTION_MOVE && !mToolbarLocked) { - // Cancel any ongoing animation before we start moving the toolbar. - mBrowserToolbar.cancelVisibilityAnimation(); - - // Move the toolbar by the amount the touch event has moved, - // clamping to fully visible or fully hidden. - float deltaY = mLastTouchY - eventY; - - // Don't let the toolbar scroll off the top if it's just exposing - // overscroll area. - ImmutableViewportMetrics metrics = - mLayerView.getLayerClient().getViewportMetrics(); - float toolbarMaxY = Math.min(toolbarHeight, - Math.max(0, toolbarHeight - (metrics.pageRectTop - - metrics.viewportRectTop))); - - int toolbarY = toolbarView.getScrollY(); - float newToolbarYf = Math.max(0, Math.min(toolbarMaxY, - toolbarY + deltaY + mToolbarSubpixelAccumulation)); - int newToolbarY = Math.round(newToolbarYf); - mToolbarSubpixelAccumulation = (newToolbarYf - newToolbarY); - - toolbarView.scrollTo(0, newToolbarY); - - // Reset tracking when the toolbar is fully visible or hidden. - if (newToolbarY == 0 || newToolbarY == toolbarHeight) { - mLastTouchY = eventY; - } - } else if (action == MotionEvent.ACTION_UP || - action == MotionEvent.ACTION_CANCEL) { - // Animate the toolbar to fully on or off, depending on how much - // of it is hidden. - mBrowserToolbar.animateVisibility( - toolbarView.getScrollY() > toolbarHeight / 2 ? false : true, 0); - } - } - - // Update the last recorded y position. - mLastTouchY = eventY; - - return super.onInterceptTouchEvent(view, event); - } - void handleReaderAdded(boolean success, final String title, final String url) { if (!success) { showToast(R.string.reading_list_failed, Toast.LENGTH_SHORT); @@ -329,10 +193,8 @@ abstract public class BrowserApp extends GeckoApp super.onCreate(savedInstanceState); - mToolbarSpacer = findViewById(R.id.toolbar_spacer); - LinearLayout actionBar = (LinearLayout) getActionBarLayout(); - mMainLayout.addView(actionBar, 2); + mMainLayout.addView(actionBar, 0); ((GeckoApp.MainLayout) mMainLayout).setOnInterceptTouchListener(new HideTabsTouchListener()); @@ -355,33 +217,6 @@ abstract public class BrowserApp extends GeckoApp Distribution.init(this, getPackageResourcePath()); JavaAddonManager.getInstance().init(getApplicationContext()); - - // Load the dynamic toolbar pref - PrefsHelper.getPref(PREF_CHROME_DYNAMICTOOLBAR, new PrefsHelper.PrefHandlerBase() { - @Override - public void prefValue(String pref, boolean value) { - if (value == mDynamicToolbarEnabled) { - return; - } - mDynamicToolbarEnabled = value; - - mMainHandler.post(new Runnable() { - @Override - public void run() { - if (!mDynamicToolbarEnabled) { - // Immediately show the toolbar when disabling the dynamic - // toolbar. - mBrowserToolbar.cancelVisibilityAnimation(); - mBrowserToolbar.getLayout().scrollTo(0, 0); - } - - // Refresh the margins to reset the padding on the spacer and - // make sure that Gecko is in sync. - ((BrowserToolbarLayout)mBrowserToolbar.getLayout()).refreshMargins(); - } - }); - } - }); } @Override @@ -421,7 +256,6 @@ abstract public class BrowserApp extends GeckoApp mBrowserToolbar.updateBackButton(false); mBrowserToolbar.updateForwardButton(false); - ((BrowserToolbarLayout)mBrowserToolbar.getLayout()).refreshMargins(); mDoorHangerPopup.setAnchor(mBrowserToolbar.mFavicon); @@ -444,29 +278,6 @@ abstract public class BrowserApp extends GeckoApp } } - public void setToolbarHeight(int aHeight, int aVisibleHeight) { - if (!mDynamicToolbarEnabled || Boolean.TRUE.equals(mAboutHomeShowing)) { - // Use aVisibleHeight here so that when the dynamic toolbar is - // enabled, the padding will animate with the toolbar becoming - // visible. - mToolbarSpacer.setPadding(0, aVisibleHeight, 0, 0); - aHeight = aVisibleHeight = 0; - } else { - mToolbarSpacer.setPadding(0, 0, 0, 0); - } - - // In the current UI, this is the only place we have need of - // viewport margins (to stop the toolbar from obscuring fixed-pos - // content). - GeckoAppShell.sendEventToGecko( - GeckoEvent.createBroadcastEvent("Viewport:FixedMarginsChanged", - "{ \"top\" : " + aHeight + ", \"right\" : 0, \"bottom\" : 0, \"left\" : 0 }")); - - if (mLayerView != null) { - mLayerView.getLayerClient().setFixedLayerMargins(0, aVisibleHeight, 0, 0); - } - } - @Override void toggleChrome(final boolean aShow) { mMainHandler.post(new Runnable() { @@ -921,9 +732,6 @@ abstract public class BrowserApp extends GeckoApp mAboutHomeShowing = true; Runnable r = new AboutHomeRunnable(true); mMainHandler.postAtFrontOfQueue(r); - - // Refresh margins to possibly remove the toolbar padding - ((BrowserToolbarLayout)mBrowserToolbar.getLayout()).refreshMargins(); } private void hideAboutHome() { @@ -936,9 +744,6 @@ abstract public class BrowserApp extends GeckoApp mAboutHomeShowing = false; Runnable r = new AboutHomeRunnable(false); mMainHandler.postAtFrontOfQueue(r); - - // Refresh margins to possibly restore the toolbar padding - ((BrowserToolbarLayout)mBrowserToolbar.getLayout()).refreshMargins(); } private class AboutHomeRunnable implements Runnable { diff --git a/mobile/android/base/BrowserToolbar.java b/mobile/android/base/BrowserToolbar.java index f583fa5b17f..76150024848 100644 --- a/mobile/android/base/BrowserToolbar.java +++ b/mobile/android/base/BrowserToolbar.java @@ -41,9 +41,6 @@ import android.widget.ViewSwitcher; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.TimerTask; - -import org.mozilla.gecko.gfx.ImmutableViewportMetrics; public class BrowserToolbar implements ViewSwitcher.ViewFactory, Tabs.OnTabsChangedListener, @@ -108,23 +105,11 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory, private int mCount; private int mFaviconSize; - private PropertyAnimator mVisibilityAnimator; - private TimerTask mDelayedVisibilityTask; - - private enum ToolbarVisibility { - VISIBLE, - HIDDEN, - INCONSISTENT - }; - private ToolbarVisibility mVisibility; - private static final int TABS_CONTRACTED = 1; private static final int TABS_EXPANDED = 2; private static final int FORWARD_ANIMATION_DURATION = 450; - private static final int VISIBILITY_ANIMATION_DURATION = 250; - public BrowserToolbar(BrowserApp activity) { // BrowserToolbar is attached to BrowserApp only. mActivity = activity; @@ -133,8 +118,6 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory, sActionItems = new ArrayList(); Tabs.registerOnTabsChangedListener(this); mAnimateSiteSecurity = true; - - mVisibility = ToolbarVisibility.INCONSISTENT; } public void from(LinearLayout layout) { @@ -482,67 +465,6 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory, } } - private boolean canToolbarHide() { - // Forbid the toolbar from hiding if hiding the toolbar would cause - // the page to go into overscroll. - ImmutableViewportMetrics metrics = GeckoApp.mAppContext.getLayerView(). - getLayerClient().getViewportMetrics(); - return (metrics.getPageHeight() >= metrics.getHeight()); - } - - private void startVisibilityAnimation() { - // Only start the animation if we're showing the toolbar, or it's ok - // to hide it. - if (mVisibility == ToolbarVisibility.VISIBLE || - canToolbarHide()) { - mVisibilityAnimator.start(); - } - } - - public void animateVisibility(boolean show, long delay) { - // Do nothing if there's a delayed animation pending that does the - // same thing and this request also has a delay. - if (mVisibility != ToolbarVisibility.INCONSISTENT && - ((delay > 0) == (mDelayedVisibilityTask != null)) && - (show == isVisible())) { - return; - } - - cancelVisibilityAnimation(); - mVisibility = show ? ToolbarVisibility.VISIBLE : ToolbarVisibility.HIDDEN; - - mVisibilityAnimator = new PropertyAnimator(VISIBILITY_ANIMATION_DURATION); - mVisibilityAnimator.attach(mLayout, PropertyAnimator.Property.SCROLL_Y, - show ? 0 : mLayout.getHeight()); - if (delay > 0) { - mDelayedVisibilityTask = new TimerTask() { - public void run() { - startVisibilityAnimation(); - mDelayedVisibilityTask = null; - } - }; - mLayout.postDelayed(mDelayedVisibilityTask, delay); - } else { - startVisibilityAnimation(); - } - } - - public void cancelVisibilityAnimation() { - mVisibility = ToolbarVisibility.INCONSISTENT; - if (mDelayedVisibilityTask != null) { - mLayout.removeCallbacks(mDelayedVisibilityTask); - mDelayedVisibilityTask = null; - } - if (mVisibilityAnimator != null) { - mVisibilityAnimator.stop(false); - mVisibilityAnimator = null; - } - } - - public boolean isVisible() { - return mVisibility == ToolbarVisibility.VISIBLE; - } - @Override public void onAnimationStart(Animation animation) { if (animation.equals(mLockFadeIn)) { diff --git a/mobile/android/base/BrowserToolbarLayout.java b/mobile/android/base/BrowserToolbarLayout.java deleted file mode 100644 index 03cdaee63e2..00000000000 --- a/mobile/android/base/BrowserToolbarLayout.java +++ /dev/null @@ -1,58 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko; - -import org.mozilla.gecko.gfx.LayerView; - -import android.content.Context; -import android.util.AttributeSet; -import android.util.Log; -import android.view.MotionEvent; -import android.widget.LinearLayout; - -public class BrowserToolbarLayout extends LinearLayout { - private static final String LOGTAG = "GeckoToolbarLayout"; - - public BrowserToolbarLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - // If the motion event has occured below the toolbar (due to the scroll - // offset), let it pass through to the page. - if (event != null && event.getY() > getHeight() - getScrollY()) { - return false; - } - - return super.onTouchEvent(event); - } - - @Override - protected void onScrollChanged(int l, int t, int oldl, int oldt) { - super.onScrollChanged(l, t, oldl, oldt); - - if (t != oldt) { - refreshMargins(); - } - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - - if (h != oldh) { - refreshMargins(); - } - } - - public void refreshMargins() { - int height = getHeight(); - int visibleHeight = height - getScrollY(); - ((BrowserApp)GeckoApp.mAppContext).setToolbarHeight(height, visibleHeight); - } -} - diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 167b2e8a46f..41f9530e560 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -111,8 +111,7 @@ abstract public class GeckoApp extends GeckoActivity implements GeckoEventListener, SensorEventListener, LocationListener, Tabs.OnTabsChangedListener, GeckoEventResponder, - GeckoMenu.Callback, GeckoMenu.MenuPresenter, - OnInterceptTouchListener + GeckoMenu.Callback, GeckoMenu.MenuPresenter { private static final String LOGTAG = "GeckoApp"; @@ -150,7 +149,7 @@ abstract public class GeckoApp static public final int RESTORE_CRASH = 2; StartupMode mStartupMode = null; - protected RelativeLayout mMainLayout; + protected LinearLayout mMainLayout; protected RelativeLayout mGeckoLayout; public View getView() { return mGeckoLayout; } public SurfaceView cameraView; @@ -187,8 +186,6 @@ abstract public class GeckoApp private String mPrivateBrowsingSession; - private PointF mInitialTouchPoint = null; - private static Boolean sIsLargeTablet = null; private static Boolean sIsSmallTablet = null; @@ -1398,7 +1395,7 @@ abstract public class GeckoApp // setup gecko layout mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout); - mMainLayout = (RelativeLayout) findViewById(R.id.main_layout); + mMainLayout = (LinearLayout) findViewById(R.id.main_layout); // setup tabs panel mTabsPanel = (TabsPanel) findViewById(R.id.tabs_panel); @@ -2435,41 +2432,42 @@ abstract public class GeckoApp protected void connectGeckoLayerClient() { mLayerView.getLayerClient().notifyGeckoReady(); - mLayerView.setTouchIntercepter(this); - } + mLayerView.setTouchIntercepter(new OnInterceptTouchListener() { + private PointF initialPoint = null; - @Override - public boolean onInterceptTouchEvent(View view, MotionEvent event) { - return false; - } - - @Override - public boolean onTouch(View view, MotionEvent event) { - if (event == null) - return true; - - int action = event.getActionMasked(); - PointF point = new PointF(event.getX(), event.getY()); - if (action == MotionEvent.ACTION_DOWN) { - mInitialTouchPoint = point; - } - - if (mInitialTouchPoint != null && action == MotionEvent.ACTION_MOVE) { - if (PointUtils.subtract(point, mInitialTouchPoint).length() < - PanZoomController.PAN_THRESHOLD) { - // Don't send the touchmove event if if the users finger hasn't moved far. - // Necessary for Google Maps to work correctly. See bug 771099. - return true; - } else { - mInitialTouchPoint = null; + @Override + public boolean onInterceptTouchEvent(View view, MotionEvent event) { + return false; } - } - GeckoAppShell.sendEventToGecko(GeckoEvent.createMotionEvent(event)); - return true; + @Override + public boolean onTouch(View view, MotionEvent event) { + if (event == null) + return true; + + int action = event.getAction(); + PointF point = new PointF(event.getX(), event.getY()); + if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) { + initialPoint = point; + } + + if (initialPoint != null && (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_MOVE) { + if (PointUtils.subtract(point, initialPoint).length() < PanZoomController.PAN_THRESHOLD) { + // Don't send the touchmove event if if the users finger hasn't move far + // Necessary for Google Maps to work correctlly. See bug 771099. + return true; + } else { + initialPoint = null; + } + } + + GeckoAppShell.sendEventToGecko(GeckoEvent.createMotionEvent(event)); + return true; + } + }); } - public static class MainLayout extends RelativeLayout { + public static class MainLayout extends LinearLayout { private OnInterceptTouchListener mOnInterceptTouchListener; public MainLayout(Context context, AttributeSet attrs) { diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java index 6ca04aeb728..994c9e7688b 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -548,10 +548,6 @@ public class GeckoEvent { sb.append("{ \"x\" : ").append(metrics.viewportRectLeft) .append(", \"y\" : ").append(metrics.viewportRectTop) .append(", \"zoom\" : ").append(metrics.zoomFactor) - .append(", \"fixedMarginLeft\" : ").append(metrics.fixedLayerMarginLeft) - .append(", \"fixedMarginTop\" : ").append(metrics.fixedLayerMarginTop) - .append(", \"fixedMarginRight\" : ").append(metrics.fixedLayerMarginRight) - .append(", \"fixedMarginBottom\" : ").append(metrics.fixedLayerMarginBottom) .append(", \"displayPort\" :").append(displayPort.toJSON()) .append('}'); event.mCharactersExtra = sb.toString(); diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index a340da05401..33322a4ebee 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -59,7 +59,6 @@ FENNEC_JAVA_FILES = \ BrowserApp.java \ BrowserToolbar.java \ BrowserToolbarBackground.java \ - BrowserToolbarLayout.java \ CameraImageResultHandler.java \ CameraVideoResultHandler.java \ CanvasDelegate.java \ diff --git a/mobile/android/base/PropertyAnimator.java b/mobile/android/base/PropertyAnimator.java index b2b6d06cf17..acc98803a8e 100644 --- a/mobile/android/base/PropertyAnimator.java +++ b/mobile/android/base/PropertyAnimator.java @@ -141,18 +141,12 @@ public class PropertyAnimator implements Runnable { } } - - /** - * Stop the animation, optionally snapping to the end position. - * onPropertyAnimationEnd is only called when snapping to the end position. - */ - public void stop(boolean snapToEndPosition) { + public void stop() { mFramePoster.cancelAnimationFrame(); // Make sure to snap to the end position. - for (ElementHolder element : mElementsList) { - if (snapToEndPosition) - invalidate(element, element.to); + for (ElementHolder element : mElementsList) { + invalidate(element, element.to); if (shouldEnableHardwareLayer(element)) element.view.setLayerType(View.LAYER_TYPE_NONE, null); @@ -163,16 +157,11 @@ public class PropertyAnimator implements Runnable { mElementsList.clear(); if (mListener != null) { - if (snapToEndPosition) - mListener.onPropertyAnimationEnd(); + mListener.onPropertyAnimationEnd(); mListener = null; } } - public void stop() { - stop(true); - } - private boolean shouldEnableHardwareLayer(ElementHolder element) { if (!mUseHardwareLayer) return false; diff --git a/mobile/android/base/gfx/GeckoLayerClient.java b/mobile/android/base/gfx/GeckoLayerClient.java index e49d78d4bcf..551d9147a10 100644 --- a/mobile/android/base/gfx/GeckoLayerClient.java +++ b/mobile/android/base/gfx/GeckoLayerClient.java @@ -17,7 +17,6 @@ import org.mozilla.gecko.util.FloatUtils; import android.content.Context; import android.graphics.PointF; -import android.graphics.Rect; import android.graphics.RectF; import android.os.SystemClock; import android.util.DisplayMetrics; @@ -91,8 +90,6 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget private final PanZoomController mPanZoomController; private LayerView mView; - private boolean mClampOnMarginChange; - public GeckoLayerClient(Context context, LayerView view, EventDispatcher eventDispatcher) { // we can fill these in with dummy values because they are always written // to before being read @@ -107,7 +104,6 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget mProgressiveUpdateDisplayPort = new DisplayPortMetrics(); mLastProgressiveUpdateWasLowPrecision = false; mProgressiveUpdateWasInDanger = false; - mClampOnMarginChange = true; mForceRedraw = true; DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); @@ -261,20 +257,6 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget ImmutableViewportMetrics metrics = getViewportMetrics(); ImmutableViewportMetrics clampedMetrics = metrics.clamp(); - // See if we need to alter the fixed margins - Fixed margins would - // otherwise be set even when the document is in overscroll and they're - // unnecessary. - if ((metrics.fixedLayerMarginLeft > 0 && metrics.viewportRectLeft < metrics.pageRectLeft) || - (metrics.fixedLayerMarginTop > 0 && metrics.viewportRectTop < metrics.pageRectTop) || - (metrics.fixedLayerMarginRight > 0 && metrics.viewportRectRight > metrics.pageRectRight) || - (metrics.fixedLayerMarginBottom > 0 && metrics.viewportRectBottom > metrics.pageRectBottom)) { - clampedMetrics = clampedMetrics.setFixedLayerMargins( - Math.max(0, metrics.fixedLayerMarginLeft + Math.min(0, metrics.viewportRectLeft - metrics.pageRectLeft)), - Math.max(0, metrics.fixedLayerMarginTop + Math.min(0, metrics.viewportRectTop - metrics.pageRectTop)), - Math.max(0, metrics.fixedLayerMarginRight + Math.min(0, (metrics.pageRectRight - metrics.viewportRectRight))), - Math.max(0, metrics.fixedLayerMarginBottom + Math.min(0, (metrics.pageRectBottom - metrics.viewportRectBottom)))); - } - if (displayPort == null) { displayPort = DisplayPortCalculator.calculate(metrics, mPanZoomController.getVelocityVector()); } @@ -321,8 +303,7 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget default: case UPDATE: // Keep the old viewport size - metrics = messageMetrics.setViewportSize(oldMetrics.getWidth(), oldMetrics.getHeight()) - .setFixedLayerMarginsFrom(oldMetrics); + metrics = messageMetrics.setViewportSize(oldMetrics.getWidth(), oldMetrics.getHeight()); if (!oldMetrics.fuzzyEquals(metrics)) { abortPanZoomAnimation(); } @@ -365,36 +346,6 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget } } - /** - * Sets margins on fixed-position layers, to be used when compositing. - * Must be called on the UI thread! - */ - public void setFixedLayerMargins(float left, float top, float right, float bottom) { - ImmutableViewportMetrics oldMetrics = getViewportMetrics(); - if (oldMetrics.fixedLayerMarginLeft == left && - oldMetrics.fixedLayerMarginTop == top && - oldMetrics.fixedLayerMarginRight == right && - oldMetrics.fixedLayerMarginBottom == bottom) { - // Do nothing if the margins haven't changed. - return; - } - - ImmutableViewportMetrics newMetrics = oldMetrics.setFixedLayerMargins(left, top, right, bottom); - - if (mClampOnMarginChange) { - newMetrics = newMetrics.clampWithMargins(); - } - - mForceRedraw = true; - setViewportMetrics(newMetrics, true); - - mView.requestRender(); - } - - public void setClampOnFixedLayerMarginsChange(boolean aClamp) { - mClampOnMarginChange = aClamp; - } - // This is called on the Gecko thread to determine if we're still interested // in the update of this display-port to continue. We can return true here // to abort the current update and continue with any subsequent ones. This @@ -506,15 +457,7 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget float pageLeft, float pageTop, float pageRight, float pageBottom, float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) { synchronized (this) { - ImmutableViewportMetrics currentMetrics = getViewportMetrics(); - - // If we're meant to be scrolled to the top, take into account any - // margin set on the pan zoom controller. - if (offsetY == 0) { - offsetY = -currentMetrics.fixedLayerMarginTop; - } - - final ImmutableViewportMetrics newMetrics = currentMetrics + final ImmutableViewportMetrics newMetrics = getViewportMetrics() .setViewportOrigin(offsetX, offsetY) .setZoomFactor(zoom) .setPageRect(new RectF(pageLeft, pageTop, pageRight, pageBottom), @@ -594,20 +537,6 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget mCurrentViewTransform.y = mFrameMetrics.viewportRectTop; mCurrentViewTransform.scale = mFrameMetrics.zoomFactor; - // Adjust the fixed layer margins so that overscroll subtracts from them. - mCurrentViewTransform.fixedLayerMarginLeft = - Math.max(0, mFrameMetrics.fixedLayerMarginLeft + - Math.min(0, mFrameMetrics.viewportRectLeft - mFrameMetrics.pageRectLeft)); - mCurrentViewTransform.fixedLayerMarginTop = - Math.max(0, mFrameMetrics.fixedLayerMarginTop + - Math.min(0, mFrameMetrics.viewportRectTop - mFrameMetrics.pageRectTop)); - mCurrentViewTransform.fixedLayerMarginRight = - Math.max(0, mFrameMetrics.fixedLayerMarginRight + - Math.min(0, (mFrameMetrics.pageRectRight - mFrameMetrics.viewportRectRight))); - mCurrentViewTransform.fixedLayerMarginBottom = - Math.max(0, mFrameMetrics.fixedLayerMarginBottom + - Math.min(0, (mFrameMetrics.pageRectBottom - mFrameMetrics.viewportRectBottom))); - mRootLayer.setPositionAndResolution(x, y, x + width, y + height, resolution); if (layersUpdated && mRecordDrawTimes) { @@ -744,7 +673,7 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget return; } ImmutableViewportMetrics m = mViewportMetrics; - BrowserApp.mBrowserToolbar.setShadowVisibility(m.viewportRectTop >= m.pageRectTop - m.fixedLayerMarginTop); + BrowserApp.mBrowserToolbar.setShadowVisibility(m.viewportRectTop >= m.pageRectTop); } }); } diff --git a/mobile/android/base/gfx/ImmutableViewportMetrics.java b/mobile/android/base/gfx/ImmutableViewportMetrics.java index 5627d3f8e89..b6d52b2ab8a 100644 --- a/mobile/android/base/gfx/ImmutableViewportMetrics.java +++ b/mobile/android/base/gfx/ImmutableViewportMetrics.java @@ -32,10 +32,6 @@ public class ImmutableViewportMetrics { public final float viewportRectTop; public final float viewportRectRight; public final float viewportRectBottom; - public final float fixedLayerMarginLeft; - public final float fixedLayerMarginTop; - public final float fixedLayerMarginRight; - public final float fixedLayerMarginBottom; public final float zoomFactor; public ImmutableViewportMetrics(DisplayMetrics metrics) { @@ -43,7 +39,6 @@ public class ImmutableViewportMetrics { viewportRectTop = pageRectTop = cssPageRectTop = 0; viewportRectRight = pageRectRight = cssPageRectRight = metrics.widthPixels; viewportRectBottom = pageRectBottom = cssPageRectBottom = metrics.heightPixels; - fixedLayerMarginLeft = fixedLayerMarginTop = fixedLayerMarginRight = fixedLayerMarginBottom = 0; zoomFactor = 1.0f; } @@ -52,21 +47,6 @@ public class ImmutableViewportMetrics { float aCssPageRectTop, float aCssPageRectRight, float aCssPageRectBottom, float aViewportRectLeft, float aViewportRectTop, float aViewportRectRight, float aViewportRectBottom, float aZoomFactor) - { - this(aPageRectLeft, aPageRectTop, - aPageRectRight, aPageRectBottom, aCssPageRectLeft, - aCssPageRectTop, aCssPageRectRight, aCssPageRectBottom, - aViewportRectLeft, aViewportRectTop, aViewportRectRight, - aViewportRectBottom, 0.0f, 0.0f, 0.0f, 0.0f, aZoomFactor); - } - - private ImmutableViewportMetrics(float aPageRectLeft, float aPageRectTop, - float aPageRectRight, float aPageRectBottom, float aCssPageRectLeft, - float aCssPageRectTop, float aCssPageRectRight, float aCssPageRectBottom, - float aViewportRectLeft, float aViewportRectTop, float aViewportRectRight, - float aViewportRectBottom, float aFixedLayerMarginLeft, - float aFixedLayerMarginTop, float aFixedLayerMarginRight, - float aFixedLayerMarginBottom, float aZoomFactor) { pageRectLeft = aPageRectLeft; pageRectTop = aPageRectTop; @@ -80,10 +60,6 @@ public class ImmutableViewportMetrics { viewportRectTop = aViewportRectTop; viewportRectRight = aViewportRectRight; viewportRectBottom = aViewportRectBottom; - fixedLayerMarginLeft = aFixedLayerMarginLeft; - fixedLayerMarginTop = aFixedLayerMarginTop; - fixedLayerMarginRight = aFixedLayerMarginRight; - fixedLayerMarginBottom = aFixedLayerMarginBottom; zoomFactor = aZoomFactor; } @@ -149,10 +125,6 @@ public class ImmutableViewportMetrics { FloatUtils.interpolate(viewportRectTop, to.viewportRectTop, t), FloatUtils.interpolate(viewportRectRight, to.viewportRectRight, t), FloatUtils.interpolate(viewportRectBottom, to.viewportRectBottom, t), - FloatUtils.interpolate(fixedLayerMarginLeft, to.fixedLayerMarginLeft, t), - FloatUtils.interpolate(fixedLayerMarginTop, to.fixedLayerMarginTop, t), - FloatUtils.interpolate(fixedLayerMarginRight, to.fixedLayerMarginRight, t), - FloatUtils.interpolate(fixedLayerMarginBottom, to.fixedLayerMarginBottom, t), FloatUtils.interpolate(zoomFactor, to.zoomFactor, t)); } @@ -161,7 +133,6 @@ public class ImmutableViewportMetrics { pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, viewportRectLeft, viewportRectTop, viewportRectLeft + width, viewportRectTop + height, - fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom, zoomFactor); } @@ -170,7 +141,6 @@ public class ImmutableViewportMetrics { pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, newOriginX, newOriginY, newOriginX + getWidth(), newOriginY + getHeight(), - fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom, zoomFactor); } @@ -179,7 +149,6 @@ public class ImmutableViewportMetrics { pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, - fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom, newZoomFactor); } @@ -192,25 +161,9 @@ public class ImmutableViewportMetrics { pageRect.left, pageRect.top, pageRect.right, pageRect.bottom, cssPageRect.left, cssPageRect.top, cssPageRect.right, cssPageRect.bottom, viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, - fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom, zoomFactor); } - public ImmutableViewportMetrics setFixedLayerMargins(float left, float top, float right, float bottom) { - return new ImmutableViewportMetrics( - pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, - cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, - viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, - left, top, right, bottom, zoomFactor); - } - - public ImmutableViewportMetrics setFixedLayerMarginsFrom(ImmutableViewportMetrics fromMetrics) { - return setFixedLayerMargins(fromMetrics.fixedLayerMarginLeft, - fromMetrics.fixedLayerMarginTop, - fromMetrics.fixedLayerMarginRight, - fromMetrics.fixedLayerMarginBottom); - } - /* This will set the zoom factor and re-scale page-size and viewport offset * accordingly. The given focus will remain at the same point on the screen * after scaling. @@ -232,51 +185,35 @@ public class ImmutableViewportMetrics { newPageRectLeft, newPageRectTop, newPageRectRight, newPageRectBottom, cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, origin.x, origin.y, origin.x + getWidth(), origin.y + getHeight(), - fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom, newZoomFactor); } /** Clamps the viewport to remain within the page rect. */ - private ImmutableViewportMetrics clamp(float marginLeft, float marginTop, - float marginRight, float marginBottom) { + public ImmutableViewportMetrics clamp() { RectF newViewport = getViewport(); // The viewport bounds ought to never exceed the page bounds. - if (newViewport.right > pageRectRight + marginRight) - newViewport.offset((pageRectRight + marginRight) - newViewport.right, 0); - if (newViewport.left < pageRectLeft - marginLeft) - newViewport.offset((pageRectLeft - marginLeft) - newViewport.left, 0); + if (newViewport.right > pageRectRight) + newViewport.offset(pageRectRight - newViewport.right, 0); + if (newViewport.left < pageRectLeft) + newViewport.offset(pageRectLeft - newViewport.left, 0); - if (newViewport.bottom > pageRectBottom + marginBottom) - newViewport.offset(0, (pageRectBottom + marginBottom) - newViewport.bottom); - if (newViewport.top < pageRectTop - marginTop) - newViewport.offset(0, (pageRectTop - marginTop) - newViewport.top); + if (newViewport.bottom > pageRectBottom) + newViewport.offset(0, pageRectBottom - newViewport.bottom); + if (newViewport.top < pageRectTop) + newViewport.offset(0, pageRectTop - newViewport.top); return new ImmutableViewportMetrics( pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, newViewport.left, newViewport.top, newViewport.right, newViewport.bottom, - fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom, zoomFactor); } - public ImmutableViewportMetrics clamp() { - return clamp(0, 0, 0, 0); - } - - public ImmutableViewportMetrics clampWithMargins() { - return clamp(fixedLayerMarginLeft, fixedLayerMarginTop, - fixedLayerMarginRight, fixedLayerMarginBottom); - } - public boolean fuzzyEquals(ImmutableViewportMetrics other) { // Don't bother checking the pageRectXXX values because they are a product // of the cssPageRectXXX values and the zoomFactor, except with more rounding // error. Checking those is both inefficient and can lead to false negatives. - // - // This doesn't return false if the fixed layer margins differ as none - // of the users of this function are interested in the margins in that - // way. return FloatUtils.fuzzyEquals(cssPageRectLeft, other.cssPageRectLeft) && FloatUtils.fuzzyEquals(cssPageRectTop, other.cssPageRectTop) && FloatUtils.fuzzyEquals(cssPageRectRight, other.cssPageRectRight) @@ -294,8 +231,6 @@ public class ImmutableViewportMetrics { + viewportRectRight + "," + viewportRectBottom + ") p=(" + pageRectLeft + "," + pageRectTop + "," + pageRectRight + "," + pageRectBottom + ") c=(" + cssPageRectLeft + "," + cssPageRectTop + "," + cssPageRectRight + "," - + cssPageRectBottom + ") m=(" + fixedLayerMarginLeft + "," - + fixedLayerMarginTop + "," + fixedLayerMarginRight + "," - + fixedLayerMarginBottom + ") z=" + zoomFactor; + + cssPageRectBottom + ") z=" + zoomFactor; } } diff --git a/mobile/android/base/gfx/JavaPanZoomController.java b/mobile/android/base/gfx/JavaPanZoomController.java index b65ad8e8eca..bb2c9c3a6a4 100644 --- a/mobile/android/base/gfx/JavaPanZoomController.java +++ b/mobile/android/base/gfx/JavaPanZoomController.java @@ -829,21 +829,15 @@ class JavaPanZoomController // Ensure minZoomFactor keeps the page at least as big as the viewport. if (pageRect.width() > 0) { - float pageWidth = pageRect.width() + - viewportMetrics.fixedLayerMarginLeft + - viewportMetrics.fixedLayerMarginRight; - float scaleFactor = viewport.width() / pageWidth; + float scaleFactor = viewport.width() / pageRect.width(); minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor); - if (viewport.width() > pageWidth) + if (viewport.width() > pageRect.width()) focusX = 0.0f; } if (pageRect.height() > 0) { - float pageHeight = pageRect.height() + - viewportMetrics.fixedLayerMarginTop + - viewportMetrics.fixedLayerMarginBottom; - float scaleFactor = viewport.height() / pageHeight; + float scaleFactor = viewport.height() / pageRect.height(); minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor); - if (viewport.height() > pageHeight) + if (viewport.height() > pageRect.height()) focusY = 0.0f; } @@ -863,7 +857,7 @@ class JavaPanZoomController } /* Now we pan to the right origin. */ - viewportMetrics = viewportMetrics.clampWithMargins(); + viewportMetrics = viewportMetrics.clamp(); return viewportMetrics; } @@ -875,15 +869,9 @@ class JavaPanZoomController @Override protected float getViewportLength() { return getMetrics().getWidth(); } @Override - protected float getPageStart() { - ImmutableViewportMetrics metrics = getMetrics(); - return metrics.pageRectLeft - metrics.fixedLayerMarginLeft; - } + protected float getPageStart() { return getMetrics().pageRectLeft; } @Override - protected float getPageLength() { - ImmutableViewportMetrics metrics = getMetrics(); - return metrics.getPageWidth() + metrics.fixedLayerMarginLeft + metrics.fixedLayerMarginRight; - } + protected float getPageLength() { return getMetrics().getPageWidth(); } } private class AxisY extends Axis { @@ -893,15 +881,9 @@ class JavaPanZoomController @Override protected float getViewportLength() { return getMetrics().getHeight(); } @Override - protected float getPageStart() { - ImmutableViewportMetrics metrics = getMetrics(); - return metrics.pageRectTop - metrics.fixedLayerMarginTop; - } + protected float getPageStart() { return getMetrics().pageRectTop; } @Override - protected float getPageLength() { - ImmutableViewportMetrics metrics = getMetrics(); - return metrics.getPageHeight() + metrics.fixedLayerMarginTop + metrics.fixedLayerMarginBottom; - } + protected float getPageLength() { return getMetrics().getPageHeight(); } } /* diff --git a/mobile/android/base/gfx/ViewTransform.java b/mobile/android/base/gfx/ViewTransform.java index f1ac3109f14..edb45f5b90e 100644 --- a/mobile/android/base/gfx/ViewTransform.java +++ b/mobile/android/base/gfx/ViewTransform.java @@ -9,19 +9,11 @@ public class ViewTransform { public float x; public float y; public float scale; - public float fixedLayerMarginLeft; - public float fixedLayerMarginTop; - public float fixedLayerMarginRight; - public float fixedLayerMarginBottom; public ViewTransform(float inX, float inY, float inScale) { x = inX; y = inY; scale = inScale; - fixedLayerMarginLeft = 0; - fixedLayerMarginTop = 0; - fixedLayerMarginRight = 0; - fixedLayerMarginBottom = 0; } } diff --git a/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in b/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in index c0fab6ceb35..c417f1ec408 100644 --- a/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in +++ b/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in @@ -4,7 +4,7 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - @@ -165,4 +165,4 @@
- + diff --git a/mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml.in b/mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml.in index 0050eaa19c4..8337435af03 100644 --- a/mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml.in +++ b/mobile/android/base/resources/layout-land-v14/browser_toolbar_menu.xml.in @@ -4,7 +4,7 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - @@ -173,4 +173,4 @@ - + diff --git a/mobile/android/base/resources/layout-large-v11/browser_toolbar_menu.xml.in b/mobile/android/base/resources/layout-large-v11/browser_toolbar_menu.xml.in index 5979fa8e54f..2e898734a69 100644 --- a/mobile/android/base/resources/layout-large-v11/browser_toolbar_menu.xml.in +++ b/mobile/android/base/resources/layout-large-v11/browser_toolbar_menu.xml.in @@ -4,7 +4,7 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - @@ -179,4 +179,4 @@ - + diff --git a/mobile/android/base/resources/layout/browser_toolbar.xml.in b/mobile/android/base/resources/layout/browser_toolbar.xml.in index 92902bbfd74..6d0b93fd6f3 100644 --- a/mobile/android/base/resources/layout/browser_toolbar.xml.in +++ b/mobile/android/base/resources/layout/browser_toolbar.xml.in @@ -4,7 +4,7 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - @@ -163,4 +163,4 @@ - + diff --git a/mobile/android/base/resources/layout/browser_toolbar_menu.xml.in b/mobile/android/base/resources/layout/browser_toolbar_menu.xml.in index 12c803dfc70..a655317aa0f 100644 --- a/mobile/android/base/resources/layout/browser_toolbar_menu.xml.in +++ b/mobile/android/base/resources/layout/browser_toolbar_menu.xml.in @@ -4,7 +4,7 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - @@ -172,4 +172,4 @@ - + diff --git a/mobile/android/base/resources/layout/gecko_app.xml.in b/mobile/android/base/resources/layout/gecko_app.xml.in index f7ea076cee5..1df7805c2ae 100644 --- a/mobile/android/base/resources/layout/gecko_app.xml.in +++ b/mobile/android/base/resources/layout/gecko_app.xml.in @@ -17,18 +17,13 @@ - + - - + android:layout_weight="1"> diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 6bae80e2adc..e99c3470cb8 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -205,7 +205,6 @@ var BrowserApp = { Services.obs.addObserver(this, "FullScreen:Exit", false); Services.obs.addObserver(this, "Viewport:Change", false); Services.obs.addObserver(this, "Viewport:Flush", false); - Services.obs.addObserver(this, "Viewport:FixedMarginsChanged", false); Services.obs.addObserver(this, "Passwords:Init", false); Services.obs.addObserver(this, "FormHistory:Init", false); Services.obs.addObserver(this, "ToggleProfiling", false); @@ -1253,10 +1252,6 @@ var BrowserApp = { sendMessageToJava({ type: "Telemetry:Gather" }); break; - case "Viewport:FixedMarginsChanged": - gViewportMargins = JSON.parse(aData); - break; - default: dump('BrowserApp.observe: unexpected topic "' + aTopic + '"\n'); break; @@ -2786,12 +2781,6 @@ nsBrowserAccess.prototype = { let gScreenWidth = 1; let gScreenHeight = 1; -// The margins that should be applied to the viewport for fixed position -// children. This is used to avoid browser chrome permanently obscuring -// fixed position content, and also to make sure window-sized pages take -// into account said browser chrome. -let gViewportMargins = { top: 0, right: 0, bottom: 0, left: 0}; - function Tab(aURL, aParams) { this.browser = null; this.id = 0; @@ -2799,14 +2788,7 @@ function Tab(aURL, aParams) { this.showProgress = true; this._zoom = 1.0; this._drawZoom = 1.0; - this._fixedMarginLeft = 0; - this._fixedMarginTop = 0; - this._fixedMarginRight = 0; - this._fixedMarginBottom = 0; this.userScrollPos = { x: 0, y: 0 }; - this.viewportExcludesHorizontalMargins = true; - this.viewportExcludesVerticalMargins = true; - this.updatingViewportForPageSizeChange = false; this.contentDocumentIsDisplayed = true; this.pluginDoorhangerTimeout = null; this.shouldShowPluginDoorhanger = true; @@ -3275,34 +3257,15 @@ Tab.prototype = { setScrollClampingSize: function(zoom) { let viewportWidth = gScreenWidth / zoom; let viewportHeight = gScreenHeight / zoom; - let screenWidth = gScreenWidth; - let screenHeight = gScreenHeight; - let [pageWidth, pageHeight] = this.getPageSize(this.browser.contentDocument, viewportWidth, viewportHeight); - // Check if the page would fit into either of the viewport dimensions minus - // the margins and shrink the screen size accordingly so that the aspect - // ratio calculation below works correctly in these situations. - // We take away the margin size over two to account for rounding errors, - // as the browser size set in updateViewportSize doesn't allow for any - // size between these two values (and thus anything between them is - // attributable to rounding error). - if ((pageHeight * zoom) < gScreenHeight - (gViewportMargins.top + gViewportMargins.bottom) / 2) { - screenHeight = gScreenHeight - gViewportMargins.top - gViewportMargins.bottom; - viewportHeight = screenHeight / zoom; - } - if ((pageWidth * zoom) < gScreenWidth - (gViewportMargins.left + gViewportMargins.right) / 2) { - screenWidth = gScreenWidth - gViewportMargins.left - gViewportMargins.right; - viewportWidth = screenWidth / zoom; - } - // Make sure the aspect ratio of the screen is maintained when setting // the clamping scroll-port size. - let factor = Math.min(viewportWidth / screenWidth, pageWidth / screenWidth, - viewportHeight / screenHeight, pageHeight / screenHeight); - let scrollPortWidth = Math.min(screenWidth * factor, pageWidth * zoom); - let scrollPortHeight = Math.min(screenHeight * factor, pageHeight * zoom); + let factor = Math.min(viewportWidth / gScreenWidth, pageWidth / gScreenWidth, + viewportHeight / gScreenHeight, pageHeight / gScreenHeight); + let scrollPortWidth = gScreenWidth * factor; + let scrollPortHeight = gScreenHeight * factor; let win = this.browser.contentWindow; win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils). @@ -3339,19 +3302,6 @@ Tab.prototype = { if (aViewport.displayPort) this.setDisplayPort(aViewport.displayPort); - // Store fixed margins for later retrieval in getViewport. - this._fixedMarginLeft = aViewport.fixedMarginLeft; - this._fixedMarginTop = aViewport.fixedMarginTop; - this._fixedMarginRight = aViewport.fixedMarginRight; - this._fixedMarginBottom = aViewport.fixedMarginBottom; - - let dwi = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - dwi.setContentDocumentFixedPositionMargins( - aViewport.fixedMarginTop / aViewport.zoom, - aViewport.fixedMarginRight / aViewport.zoom, - aViewport.fixedMarginBottom / aViewport.zoom, - aViewport.fixedMarginLeft / aViewport.zoom); - Services.obs.notifyObservers(null, "after-viewport-change", ""); }, @@ -3375,27 +3325,20 @@ Tab.prototype = { }, getViewport: function() { - let screenW = gScreenWidth - gViewportMargins.left - gViewportMargins.right; - let screenH = gScreenHeight - gViewportMargins.top - gViewportMargins.bottom; - let viewport = { - width: screenW, - height: screenH, - cssWidth: screenW / this._zoom, - cssHeight: screenH / this._zoom, + width: gScreenWidth, + height: gScreenHeight, + cssWidth: gScreenWidth / this._zoom, + cssHeight: gScreenHeight / this._zoom, pageLeft: 0, pageTop: 0, - pageRight: screenW, - pageBottom: screenH, + pageRight: gScreenWidth, + pageBottom: gScreenHeight, // We make up matching css page dimensions cssPageLeft: 0, cssPageTop: 0, - cssPageRight: screenW / this._zoom, - cssPageBottom: screenH / this._zoom, - fixedMarginLeft: this._fixedMarginLeft, - fixedMarginTop: this._fixedMarginTop, - fixedMarginRight: this._fixedMarginRight, - fixedMarginBottom: this._fixedMarginBottom, + cssPageRight: gScreenWidth / this._zoom, + cssPageBottom: gScreenHeight / this._zoom, zoom: this._zoom, }; @@ -3445,20 +3388,6 @@ Tab.prototype = { let displayPort = getBridge().getDisplayPort(aPageSizeUpdate, BrowserApp.isBrowserContentDocumentDisplayed(), this.id, viewport); if (displayPort != null) this.setDisplayPort(displayPort); - - // If the page size has changed so that it might or might not fit on the - // screen with the margins included, run updateViewportSize to resize the - // browser accordingly. The -1 is to account for rounding errors. - if (!this.updatingViewportForPageSizeChange) { - this.updatingViewportForPageSizeChange = true; - if (((viewport.pageBottom - viewport.pageTop <= gScreenHeight - 1) != - this.viewportExcludesVerticalMargins) || - ((viewport.pageRight - viewport.pageLeft <= gScreenWidth - 1) != - this.viewportExcludesHorizontalMargins)) { - this.updateViewportSize(gScreenWidth); - } - this.updatingViewportForPageSizeChange = false; - } }, handleEvent: function(aEvent) { @@ -3885,11 +3814,9 @@ Tab.prototype = { if (!browser) return; - let screenW = gScreenWidth - gViewportMargins.left - gViewportMargins.right; - let screenH = gScreenHeight - gViewportMargins.top - gViewportMargins.bottom; + let screenW = gScreenWidth; + let screenH = gScreenHeight; let viewportW, viewportH; - this.viewportExcludesHorizontalMargins = true; - this.viewportExcludesVerticalMargins = true; let metadata = this.metadata; if (metadata.autoSize) { @@ -3944,21 +3871,7 @@ Tab.prototype = { // this may get run during a Viewport:Change message while the document // has not yet loaded, so need to guard against a null document. let [pageWidth, pageHeight] = this.getPageSize(this.browser.contentDocument, viewportW, viewportH); - - minScale = screenW / pageWidth; - - // In the situation the page size exceeds the screen size minus the - // viewport margins on either axis, lengthen the viewport on the - // corresponding axis to include the margins. - // The +1 is to account for rounding errors. - if (pageWidth * this._zoom >= screenW + 1) { - screenW = gScreenWidth; - this.viewportExcludesHorizontalMargins = false; - } - if (pageHeight * this._zoom >= screenH + 1) { - screenH = gScreenHeight; - this.viewportExcludesVerticalMargins = false; - } + minScale = gScreenWidth / pageWidth; } minScale = this.clampZoom(minScale); viewportH = Math.max(viewportH, screenH / minScale); diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 6016ce37ba1..b5a8a64bb3e 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -2110,15 +2110,13 @@ AndroidBridge::SetPageRect(const gfx::Rect& aCssPageRect) void AndroidBridge::SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated, - nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY, - gfx::Margin& aFixedLayerMargins) + nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY) { AndroidGeckoLayerClient *client = mLayerClient; if (!client) return; - client->SyncViewportInfo(aDisplayPort, aDisplayResolution, aLayersUpdated, - aScrollOffset, aScaleX, aScaleY, aFixedLayerMargins); + client->SyncViewportInfo(aDisplayPort, aDisplayResolution, aLayersUpdated, aScrollOffset, aScaleX, aScaleY); } AndroidBridge::AndroidBridge() diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index 514d432b61f..5043111b4d4 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -332,8 +332,7 @@ public: void SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect); void SetPageRect(const gfx::Rect& aCssPageRect); void SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated, - nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY, - gfx::Margin& aFixedLayerMargins); + nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY); void AddPluginView(jobject view, const gfxRect& rect, bool isFullScreen); void RemovePluginView(jobject view, bool isFullScreen); diff --git a/widget/android/AndroidJavaWrappers.cpp b/widget/android/AndroidJavaWrappers.cpp index 4ecd1e86da6..889424c7db9 100644 --- a/widget/android/AndroidJavaWrappers.cpp +++ b/widget/android/AndroidJavaWrappers.cpp @@ -100,10 +100,6 @@ jclass AndroidViewTransform::jViewTransformClass = 0; jfieldID AndroidViewTransform::jXField = 0; jfieldID AndroidViewTransform::jYField = 0; jfieldID AndroidViewTransform::jScaleField = 0; -jfieldID AndroidViewTransform::jFixedLayerMarginLeft = 0; -jfieldID AndroidViewTransform::jFixedLayerMarginTop = 0; -jfieldID AndroidViewTransform::jFixedLayerMarginRight = 0; -jfieldID AndroidViewTransform::jFixedLayerMarginBottom = 0; jclass AndroidProgressiveUpdateData::jProgressiveUpdateDataClass = 0; jfieldID AndroidProgressiveUpdateData::jXField = 0; @@ -369,10 +365,6 @@ AndroidViewTransform::InitViewTransformClass(JNIEnv *jEnv) jXField = getField("x", "F"); jYField = getField("y", "F"); jScaleField = getField("scale", "F"); - jFixedLayerMarginLeft = getField("fixedLayerMarginLeft", "F"); - jFixedLayerMarginTop = getField("fixedLayerMarginTop", "F"); - jFixedLayerMarginRight = getField("fixedLayerMarginRight", "F"); - jFixedLayerMarginBottom = getField("fixedLayerMarginBottom", "F"); } void @@ -747,8 +739,7 @@ AndroidGeckoLayerClient::SetPageRect(const gfx::Rect& aCssPageRect) void AndroidGeckoLayerClient::SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated, - nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY, - gfx::Margin& aFixedLayerMargins) + nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY) { NS_ASSERTION(!isNull(), "SyncViewportInfo called on null layer client!"); JNIEnv *env = GetJNIForThread(); // this is called on the compositor thread @@ -771,7 +762,6 @@ AndroidGeckoLayerClient::SyncViewportInfo(const nsIntRect& aDisplayPort, float a aScrollOffset = nsIntPoint(viewTransform.GetX(env), viewTransform.GetY(env)); aScaleX = aScaleY = viewTransform.GetScale(env); - viewTransform.GetFixedLayerMargins(env, aFixedLayerMargins); } bool @@ -1006,18 +996,6 @@ AndroidViewTransform::GetScale(JNIEnv *env) return env->GetFloatField(wrapped_obj, jScaleField); } -void -AndroidViewTransform::GetFixedLayerMargins(JNIEnv *env, gfx::Margin &aFixedLayerMargins) -{ - if (!env) - return; - - aFixedLayerMargins.left = env->GetFloatField(wrapped_obj, jFixedLayerMarginLeft); - aFixedLayerMargins.top = env->GetFloatField(wrapped_obj, jFixedLayerMarginTop); - aFixedLayerMargins.right = env->GetFloatField(wrapped_obj, jFixedLayerMarginRight); - aFixedLayerMargins.bottom = env->GetFloatField(wrapped_obj, jFixedLayerMarginBottom); -} - float AndroidProgressiveUpdateData::GetX(JNIEnv *env) { diff --git a/widget/android/AndroidJavaWrappers.h b/widget/android/AndroidJavaWrappers.h index 0a67f3bd41b..fa8d2a1f945 100644 --- a/widget/android/AndroidJavaWrappers.h +++ b/widget/android/AndroidJavaWrappers.h @@ -191,17 +191,12 @@ public: float GetX(JNIEnv *env); float GetY(JNIEnv *env); float GetScale(JNIEnv *env); - void GetFixedLayerMargins(JNIEnv *env, gfx::Margin &aFixedLayerMargins); private: static jclass jViewTransformClass; static jfieldID jXField; static jfieldID jYField; static jfieldID jScaleField; - static jfieldID jFixedLayerMarginLeft; - static jfieldID jFixedLayerMarginTop; - static jfieldID jFixedLayerMarginRight; - static jfieldID jFixedLayerMarginBottom; }; class AndroidProgressiveUpdateData : public WrappedJavaObject { @@ -262,8 +257,7 @@ public: void SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect); void SetPageRect(const gfx::Rect& aCssPageRect); void SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated, - nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY, - gfx::Margin& aFixedLayerMargins); + nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY); bool ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, const gfx::Rect& aDisplayPort, float aDisplayResolution, bool aDrawingCritical, gfx::Rect& aViewport, float& aScaleX, float& aScaleY); bool CreateFrame(AutoLocalJNIFrame *jniFrame, AndroidLayerRendererFrame& aFrame); bool ActivateProgram(AutoLocalJNIFrame *jniFrame); From 19177faf48d705a562dcb6ed7a2134c78153c1fd Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 1 Mar 2013 18:36:39 -0500 Subject: [PATCH 134/140] Backed out changesets a99be5669498 (bug 831489) and 140221368247 (bug 821208) for frequent Linux64 debug b-c failures on a CLOSED TREE. --- browser/base/content/browser-social.js | 8 +- browser/base/content/socialchat.xml | 115 +++---- browser/base/content/test/social/Makefile.in | 1 - .../social/browser_social_chatwindowfocus.js | 299 ------------------ .../base/content/test/social/social_chat.html | 4 - .../content/test/social/social_sidebar.html | 6 - 6 files changed, 52 insertions(+), 381 deletions(-) delete mode 100644 browser/base/content/test/social/browser_social_chatwindowfocus.js diff --git a/browser/base/content/browser-social.js b/browser/base/content/browser-social.js index dedbbbf245c..70c7f8a07db 100644 --- a/browser/base/content/browser-social.js +++ b/browser/base/content/browser-social.js @@ -351,14 +351,8 @@ let SocialChatBar = { return !!this.chatbar.firstElementChild; }, openChat: function(aProvider, aURL, aCallback, aMode) { - if (this.isAvailable) { + if (this.isAvailable) this.chatbar.openChat(aProvider, aURL, aCallback, aMode); - // We only want to focus the chat if it is as a result of user input. - let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - if (dwu.isHandlingUserInput) - this.chatbar.focus(); - } }, update: function() { let command = document.getElementById("Social:FocusChat"); diff --git a/browser/base/content/socialchat.xml b/browser/base/content/socialchat.xml index 57c257f9937..6ca1d44be29 100644 --- a/browser/base/content/socialchat.xml +++ b/browser/base/content/socialchat.xml @@ -20,37 +20,6 @@ - - document.getAnonymousElementByAttribute(this, "anonid", "iframe"); @@ -92,12 +61,9 @@ @@ -179,25 +145,8 @@ - - - - - @@ -219,6 +168,7 @@ this._selectedChat = val; if (val) { this._selectedChat.setAttribute("selected", "true"); + this.focus(); } } if (val) { @@ -281,12 +231,9 @@ // It's possible in the future we will track most-recently-selected // chats or similar to find the "best" candidate - for now though // the choice is somewhat arbitrary. - let moveFocus = this.selectedChat && this._isChatFocused(this.selectedChat); for (let other of this.children) { if (other != this.selectedChat && !other.minimized && !other.collapsed) { this.selectedChat = other; - if (moveFocus) - this.focus(); return; } } @@ -434,6 +381,51 @@ ]]> + + + + + + + + @@ -459,16 +451,11 @@ this.chatboxForURL.delete(aURL); } cb = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "chatbox"); - // _callbacks is a javascript property instead of a as it - // must exist before the (possibly delayed) bindings are created. - cb._callbacks = [aCallback]; - // src also a javascript property; the src attribute is set in the ctor. - cb.src = aURL; if (aMode == "minimized") cb.setAttribute("minimized", "true"); - cb.setAttribute("origin", aProvider.origin); this.insertBefore(cb, this.firstChild); this.selectedChat = cb; + this.initChatBox(cb, aProvider, aURL, aCallback); this.chatboxForURL.set(aURL, Cu.getWeakReference(cb)); this.resize(); ]]> diff --git a/browser/base/content/test/social/Makefile.in b/browser/base/content/test/social/Makefile.in index aef598e9e6c..7e3b1bce21d 100644 --- a/browser/base/content/test/social/Makefile.in +++ b/browser/base/content/test/social/Makefile.in @@ -24,7 +24,6 @@ _BROWSER_FILES = \ browser_social_mozSocial_API.js \ browser_social_isVisible.js \ browser_social_chatwindow.js \ - browser_social_chatwindowfocus.js \ browser_social_multiprovider.js \ browser_social_errorPage.js \ social_panel.html \ diff --git a/browser/base/content/test/social/browser_social_chatwindowfocus.js b/browser/base/content/test/social/browser_social_chatwindowfocus.js deleted file mode 100644 index 294a0d1d083..00000000000 --- a/browser/base/content/test/social/browser_social_chatwindowfocus.js +++ /dev/null @@ -1,299 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// Is the currently opened tab focused? -function isTabFocused() { - let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab); - return Services.focus.focusedWindow == tabb.contentWindow; -} - -function isChatFocused(chat) { - return SocialChatBar.chatbar._isChatFocused(chat); -} - -function openChatViaUser() { - let sidebarDoc = document.getElementById("social-sidebar-browser").contentDocument; - let button = sidebarDoc.getElementById("chat-opener"); - // Note we must use synthesizeMouseAtCenter() rather than calling - // .click() directly as this causes nsIDOMWindowUtils.isHandlingUserInput - // to be true. - EventUtils.synthesizeMouseAtCenter(button, {}, sidebarDoc.defaultView); -} - -function openChatViaSidebarMessage(port, data, callback) { - port.onmessage = function (e) { - if (e.data.topic == "chatbox-opened") - callback(); - } - port.postMessage({topic: "test-chatbox-open", data: data}); -} - -function openChatViaWorkerMessage(port, data, callback) { - // sadly there is no message coming back to tell us when the chat has - // been opened, so we wait until one appears. - let chatbar = SocialChatBar.chatbar; - let numExpected = chatbar.childElementCount + 1; - port.postMessage({topic: "test-worker-chat", data: data}); - waitForCondition(function() chatbar.childElementCount == numExpected, - callback, - "No new chat appeared"); -} - - -let isSidebarLoaded = false; - -function startTestAndWaitForSidebar(callback) { - let doneCallback; - let port = Social.provider.getWorkerPort(); - function maybeCallback() { - if (!doneCallback) - callback(port); - doneCallback = true; - } - port.onmessage = function(e) { - let topic = e.data.topic; - switch (topic) { - case "got-sidebar-message": - isSidebarLoaded = true; - maybeCallback(); - break; - case "test-init-done": - if (isSidebarLoaded) - maybeCallback(); - break; - } - } - port.postMessage({topic: "test-init"}); -} - -let manifest = { // normal provider - name: "provider 1", - origin: "https://example.com", - sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html", - workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js", - iconURL: "https://example.com/browser/browser/base/content/test/moz.png" -}; - -function test() { - waitForExplicitFinish(); - - // Note that (probably) due to bug 604289, if a tab is focused but the - // focused element is null, our chat windows can "steal" focus. This is - // avoided if we explicitly focus an element in the tab. - // So we load a page with an field and focus that before testing. - let url = "data:text/html;charset=utf-8," + encodeURI(''); - let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true}); - tab.linkedBrowser.addEventListener("load", function tabLoad(event) { - tab.linkedBrowser.removeEventListener("load", tabLoad, true); - // before every test we focus the input field. - let preSubTest = function(cb) { - // XXX - when bug 604289 is fixed it should be possible to just do: - // tab.linkedBrowser.contentWindow.focus() - // but instead we must do: - tab.linkedBrowser.contentDocument.getElementById("theinput").focus(); - cb(); - } - let postSubTest = function(cb) { - window.SocialChatBar.chatbar.removeAll(); - cb(); - } - // and run the tests. - runSocialTestWithProvider(manifest, function (finishcb) { - runSocialTests(tests, preSubTest, postSubTest, function () { - finishcb(); - }); - }); - }, true); - registerCleanupFunction(function() { - gBrowser.removeTab(tab); - }); - -} - -var tests = { - // In this test the worker asks the sidebar to open a chat. As that means - // we aren't handling user-input we will not focus the chatbar. - // Then we do it again - should still not be focused. - // Then we perform a user-initiated request - it should get focus. - testNoFocusWhenViaWorker: function(next) { - startTestAndWaitForSidebar(function(port) { - openChatViaSidebarMessage(port, {stealFocus: 1}, function() { - ok(true, "got chatbox message"); - is(SocialChatBar.chatbar.childElementCount, 1, "exactly 1 chat open"); - ok(isTabFocused(), "tab should still be focused"); - // re-request the same chat via a message. - openChatViaSidebarMessage(port, {stealFocus: 1}, function() { - is(SocialChatBar.chatbar.childElementCount, 1, "still exactly 1 chat open"); - ok(isTabFocused(), "tab should still be focused"); - // re-request the same chat via user event. - openChatViaUser(); - is(SocialChatBar.chatbar.childElementCount, 1, "still exactly 1 chat open"); - // should now be focused - ok(isChatFocused(SocialChatBar.chatbar.firstElementChild), "chat should be focused"); - next(); - }); - }); - }); - }, - - // In this test we arrange for the sidebar to open the chat via a simulated - // click. This should cause the new chat to be opened and focused. - testFocusWhenViaUser: function(next) { - startTestAndWaitForSidebar(function(port) { - openChatViaUser(); - ok(SocialChatBar.chatbar.firstElementChild, "chat opened"); - ok(isChatFocused(SocialChatBar.chatbar.firstElementChild), "chat should be focused"); - next(); - }); - }, - - // Open a chat via the worker - it will open minimized and not have focus. - // Then open the same chat via a sidebar message - it will be restored but - // should still not have grabbed focus. - testNoFocusOnAutoRestore: function(next) { - const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html?id=1"; - let chatbar = SocialChatBar.chatbar; - startTestAndWaitForSidebar(function(port) { - openChatViaWorkerMessage(port, chatUrl, function() { - is(chatbar.childElementCount, 1, "exactly 1 chat open"); - ok(chatbar.firstElementChild.minimized, "chat is minimized"); - ok(isTabFocused(), "tab should be focused"); - openChatViaSidebarMessage(port, {stealFocus: 1, id: 1}, function() { - is(chatbar.childElementCount, 1, "still 1 chat open"); - ok(!chatbar.firstElementChild.minimized, "chat no longer minimized"); - ok(isTabFocused(), "tab should still be focused"); - next(); - }); - }); - }); - }, - - // Here we open a chat, which will not be focused. Then we minimize it and - // restore it via a titlebar clock - it should get focus at that point. - testFocusOnExplicitRestore: function(next) { - startTestAndWaitForSidebar(function(port) { - openChatViaSidebarMessage(port, {stealFocus: 1}, function() { - ok(true, "got chatbox message"); - ok(isTabFocused(), "tab should still be focused"); - let chatbox = SocialChatBar.chatbar.firstElementChild; - ok(chatbox, "chat opened"); - chatbox.minimized = true; - ok(isTabFocused(), "tab should still be focused"); - // pretend we clicked on the titlebar - chatbox.onTitlebarClick({button: 0}); - ok(!chatbox.minimized, "chat should have been restored"); - ok(isChatFocused(chatbox), "chat should be focused"); - next(); - }); - }); - }, - - // Open 2 chats and give 1 focus. Minimize the focused one - the second - // should get focus. - testMinimizeFocused: function(next) { - let chatbar = SocialChatBar.chatbar; - startTestAndWaitForSidebar(function(port) { - openChatViaSidebarMessage(port, {stealFocus: 1, id: 1}, function() { - let chat1 = chatbar.firstElementChild; - openChatViaSidebarMessage(port, {stealFocus: 1, id: 2}, function() { - is(chatbar.childElementCount, 2, "exactly 2 chats open"); - let chat2 = chat1.nextElementSibling || chat1.previousElementSibling; - chatbar.selectedChat = chat1; - chatbar.focus(); - ok(isChatFocused(chat1), "first chat should be focused"); - chat1.minimized = true; - // minimizing the chat with focus should give it to another. - ok(isChatFocused(chat2), "second chat should be focused"); - next(); - }); - }); - }); - }, - - // Open 2 chats, select (but not focus) one, then re-request it be - // opened via a message. Focus should not move. - testReopenNonFocused: function(next) { - let chatbar = SocialChatBar.chatbar; - startTestAndWaitForSidebar(function(port) { - openChatViaSidebarMessage(port, {id: 1}, function() { - let chat1 = chatbar.firstElementChild; - openChatViaSidebarMessage(port, {id: 2}, function() { - let chat2 = chat1.nextElementSibling || chat1.previousElementSibling; - chatbar.selectedChat = chat2; - // tab still has focus - ok(isTabFocused(), "tab should still be focused"); - // re-request the first. - openChatViaSidebarMessage(port, {id: 1}, function() { - is(chatbar.selectedChat, chat1, "chat1 now selected"); - ok(isTabFocused(), "tab should still be focused"); - next(); - }); - }); - }); - }); - }, - - // Open 2 chats, select and focus the second. Pressing the TAB key should - // cause focus to move between all elements in our chat window before moving - // to the next chat window. - testTab: function(next) { - let chatbar = SocialChatBar.chatbar; - startTestAndWaitForSidebar(function(port) { - openChatViaSidebarMessage(port, {id: 1}, function() { - let chat1 = chatbar.firstElementChild; - openChatViaSidebarMessage(port, {id: 2}, function() { - let chat2 = chat1.nextElementSibling || chat1.previousElementSibling; - chatbar.selectedChat = chat2; - chatbar.focus(); - ok(isChatFocused(chat2), "new chat is focused"); - // Our chats have 3 focusable elements, so it takes 4 TABs to move - // to the new chat. - EventUtils.sendKey("tab"); - ok(isChatFocused(chat2), "new chat still focused after first tab"); - is(chat2.iframe.contentDocument.activeElement.getAttribute("id"), "input1", - "first input field has focus"); - EventUtils.sendKey("tab"); - ok(isChatFocused(chat2), "new chat still focused after tab"); - is(chat2.iframe.contentDocument.activeElement.getAttribute("id"), "input2", - "second input field has focus"); - EventUtils.sendKey("tab"); - ok(isChatFocused(chat2), "new chat still focused after tab"); - is(chat2.iframe.contentDocument.activeElement.getAttribute("id"), "iframe", - "iframe has focus"); - // this tab now should move to the next chat. - EventUtils.sendKey("tab"); - ok(isChatFocused(chat1), "first chat is focused"); - next(); - }); - }); - }); - }, - - // Open a chat and focus an element other than the first. Move focus to some - // other item (the tab itself in this case), then focus the chatbar - the - // same element that was previously focused should still have focus. - testFocusedElement: function(next) { - let chatbar = SocialChatBar.chatbar; - startTestAndWaitForSidebar(function(port) { - openChatViaUser(); - let chat = chatbar.firstElementChild; - // need to wait for the content to load before we can focus it. - chat.addEventListener("DOMContentLoaded", function DOMContentLoaded() { - chat.removeEventListener("DOMContentLoaded", DOMContentLoaded); - chat.iframe.contentDocument.getElementById("input2").focus(); - is(chat.iframe.contentDocument.activeElement.getAttribute("id"), "input2", - "correct input field has focus"); - // set focus to the tab. - let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab); - Services.focus.moveFocus(tabb.contentWindow, null, Services.focus.MOVEFOCUS_ROOT, 0); - ok(isTabFocused(), "tab took focus"); - chatbar.focus(); - ok(isChatFocused(chat), "chat took focus"); - is(chat.iframe.contentDocument.activeElement.getAttribute("id"), "input2", - "correct input field still has focus"); - next(); - }); - }); - }, -}; diff --git a/browser/base/content/test/social/social_chat.html b/browser/base/content/test/social/social_chat.html index e9099c13621..ab722481c3c 100644 --- a/browser/base/content/test/social/social_chat.html +++ b/browser/base/content/test/social/social_chat.html @@ -21,10 +21,6 @@

This is a test social chat window.

- - - - diff --git a/browser/base/content/test/social/social_sidebar.html b/browser/base/content/test/social/social_sidebar.html index dc66b5d315d..e7e378f640e 100644 --- a/browser/base/content/test/social/social_sidebar.html +++ b/browser/base/content/test/social/social_sidebar.html @@ -21,11 +21,6 @@ url = url + "?id="+data.id; } navigator.mozSocial.openChatWindow(url, function(chatwin) { - // Note that the following .focus() call should *not* arrange - // to steal focus - see browser_social_chatwindowfocus.js - if (data && data.stealFocus && chatwin) { - chatwin.focus(); - } port.postMessage({topic: "chatbox-opened", result: chatwin ? "ok" : "failed"}); }); @@ -42,6 +37,5 @@

This is a test social sidebar.

-