Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 24 Aug 2015 20:37:14 -0400
changeset 259106 08015770c9d687f93fc7b13970d32e457b680d2a
parent 258970 ef0614cdd437fadc77d7140bbc7d377a4121834a (current diff)
parent 259105 ac055dab935a651472e6301240001e758406466d (diff)
child 259145 04b8c412d9f58fb6194c58dcaa66bf278bbd53cf
push id29268
push userryanvm@gmail.com
push dateTue, 25 Aug 2015 00:37:23 +0000
treeherdermozilla-central@08015770c9d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissallowed.cert
security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissblocked.cert
security/manager/ssl/tests/unit/test_name_constraints/ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-a.a.us-b.a.us-int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-a.a.us-b.a.us-int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-a.a.us-b.a.us-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-a.a.us-b.a.us-int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-a.a.us-b.a.us-int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-a.a.us-b.a.us-int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-a.a.us-b.a.us-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-a.a.us-b.a.us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-a.a.us-b.a.us-int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-a.a.us-b.a.us-int-nc-perm-foo.com_c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.com-int-nc-perm-foo.com_c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.org-int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.org-int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.org-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.org-int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.org-int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.org-int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.org-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.org-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.org-int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-alt-foo.org-int-nc-perm-foo.com_c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com-int-nc-perm-foo.com_c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-a.a.us-b.a.us-int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-a.a.us-b.a.us-int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-a.a.us-b.a.us-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-a.a.us-b.a.us-int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-a.a.us-b.a.us-int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-a.a.us-b.a.us-int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-a.a.us-b.a.us-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-a.a.us-b.a.us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-a.a.us-b.a.us-int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-a.a.us-b.a.us-int-nc-perm-foo.com_c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.com-int-nc-perm-foo.com_c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.org-int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.org-int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.org-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.org-int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.org-int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.org-int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.org-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.org-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.org-int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-alt-foo.org-int-nc-perm-foo.com_c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.com_o-bar_c-us-int-nc-perm-foo.com_c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.com-int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.com-int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.com-int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.com-int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.com-int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.com-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.com-int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.com-int-nc-perm-foo.com_c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.org-int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.org-int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.org-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.org-int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.org-int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.org-int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.org-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.org-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.org-int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-alt-foo.org-int-nc-perm-foo.com_c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org-int-nc-perm-foo.com_c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.com-int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.com-int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.com-int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.com-int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.com-int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.com-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.com-int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.com-int-nc-perm-foo.com_c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.org-int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.org-int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.org-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.org-int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.org-int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.org-int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.org-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.org-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.org-int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-alt-foo.org-int-nc-perm-foo.com_c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/cn-www.foo.org_o-bar_c-us-int-nc-perm-foo.com_c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/dcisscopy.der
security/manager/ssl/tests/unit/test_name_constraints/int-c-us-int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/int-ca-nc-perm-foo.com.der
security/manager/ssl/tests/unit/test_name_constraints/int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/int-nc-excl-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/int-nc-foo.com-int-nc-c-us-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/int-nc-foo.com-int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/int-nc-foo.com_a.us.der
security/manager/ssl/tests/unit/test_name_constraints/int-nc-perm-c-uk-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/int-nc-perm-foo.com-ca-nc.der
security/manager/ssl/tests/unit/test_name_constraints/int-nc-perm-foo.com_c-us-ca-nc.der
testing/config/mozharness/android_arm_4_3_config.py
testing/config/mozharness/android_arm_config.py
testing/config/mozharness/android_panda_config.py
testing/config/mozharness/android_x86_config.py
testing/config/mozharness/b2g_desktop_config.py
testing/config/mozharness/b2g_emulator_config.py
testing/config/mozharness/mac_config.py
testing/config/mozharness/marionette.py
testing/config/mozharness/taskcluster_linux_config.py
testing/config/mozharness/try_arguments.py
testing/config/mozharness/web_platform_tests_config.py
testing/config/mozharness/windows_config.py
testing/marionette/driver/marionette_driver/marionette.py
testing/web-platform/meta/media-source/URL-createObjectURL-revoke.html.ini
testing/web-platform/meta/media-source/mediasource-appendwindow.html.ini
testing/web-platform/meta/media-source/mediasource-closed.html.ini
testing/web-platform/meta/media-source/mediasource-config-change-webm-a-bitrate.html.ini
testing/web-platform/meta/media-source/mediasource-config-change-webm-av-audio-bitrate.html.ini
testing/web-platform/meta/media-source/mediasource-config-change-webm-av-framesize.html.ini
testing/web-platform/meta/media-source/mediasource-config-change-webm-av-video-bitrate.html.ini
testing/web-platform/meta/media-source/mediasource-config-change-webm-v-bitrate.html.ini
testing/web-platform/meta/media-source/mediasource-config-change-webm-v-framerate.html.ini
testing/web-platform/meta/media-source/mediasource-config-change-webm-v-framesize.html.ini
testing/web-platform/meta/media-source/mediasource-duration-boundaryconditions.html.ini
testing/web-platform/meta/media-source/mediasource-endofstream-invaliderror.html.ini
testing/web-platform/meta/media-source/mediasource-getvideoplaybackquality.html.ini
testing/web-platform/meta/media-source/mediasource-multiple-attach.html.ini
testing/web-platform/meta/media-source/mediasource-play.html.ini
testing/web-platform/meta/media-source/mediasource-removesourcebuffer.html.ini
testing/web-platform/meta/media-source/mediasource-sourcebuffer-mode.html.ini
testing/web-platform/meta/media-source/mediasource-sourcebufferlist.html.ini
testing/web-platform/meta/media-source/mediasource-timestamp-offset.html.ini
--- a/addon-sdk/source/lib/sdk/indexed-db.js
+++ b/addon-sdk/source/lib/sdk/indexed-db.js
@@ -27,19 +27,19 @@ let sanitizeId = function(id){
 
 const PSEUDOURI = "indexeddb://" + sanitizeId(id) // https://bugzilla.mozilla.org/show_bug.cgi?id=779197
 
 // Use XPCOM because `require("./url").URL` doesn't expose the raw uri object.
 let principaluri = Cc["@mozilla.org/network/io-service;1"].
               getService(Ci.nsIIOService).
               newURI(PSEUDOURI, null, null);
 
-let principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
-	               getService(Ci.nsIScriptSecurityManager).
-	               getCodebasePrincipal(principaluri);
+let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
+            .getService(Ci.nsIScriptSecurityManager);
+let principal = ssm.createCodebasePrincipal(principaluri, {});
 
 function toArray(args) {
   return Array.prototype.slice.call(args);
 }
 
 function openInternal(args, forPrincipal, deleting) {
   if (forPrincipal) {
     args = toArray(args);
--- a/b2g/components/AboutServiceWorkers.jsm
+++ b/b2g/components/AboutServiceWorkers.jsm
@@ -149,21 +149,20 @@ this.AboutServiceWorkers = {
             !message.principal.origin ||
             !message.principal.originAttributes ||
             !message.principal.originAttributes.appId ||
             (message.principal.originAttributes.inBrowser == null)) {
           self.sendError(message.id, "MissingPrincipal");
           return;
         }
 
-        let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(
+        let principal = Services.scriptSecurityManager.createCodebasePrincipal(
+          // TODO: Bug 1196652. use originNoSuffix
           Services.io.newURI(message.principal.origin, null, null),
-          message.principal.originAttributes.appId,
-          message.principal.originAttributes.inBrowser
-        );
+          message.principal.originAttributes);
 
         if (!message.scope) {
           self.sendError(message.id, "MissingScope");
           return;
         }
 
         let serviceWorkerUnregisterCallback = {
           unregisterSucceeded: function() {
--- a/b2g/components/ContentPermissionPrompt.js
+++ b/b2g/components/ContentPermissionPrompt.js
@@ -200,19 +200,19 @@ ContentPermissionPrompt.prototype = {
     let appsService = Cc["@mozilla.org/AppsService;1"]
                         .getService(Ci.nsIAppsService);
     let app = appsService.getAppByLocalId(request.principal.appId);
 
     // Check each permission if it's denied by permission manager with app's
     // URL.
     let notDenyAppPrincipal = function(type) {
       let url = Services.io.newURI(app.origin, null, null);
-      let principal = secMan.getAppCodebasePrincipal(url,
-                                                     request.principal.appId,
-                                                     /*mozbrowser*/false);
+      let principal =
+        secMan.createCodebasePrincipal(url,
+                                       {appId: request.principal.appId});
       let result = Services.perms.testExactPermissionFromPrincipal(principal,
                                                                    type.access);
 
       if (result == Ci.nsIPermissionManager.ALLOW_ACTION ||
           result == Ci.nsIPermissionManager.PROMPT_ACTION) {
         type.deny = false;
       }
       return !type.deny;
--- a/browser/base/content/aboutSocialError.xhtml
+++ b/browser/base/content/aboutSocialError.xhtml
@@ -50,17 +50,18 @@
       let mode = searchParams.get("mode");
       config.origin = searchParams.get("origin");
       let encodedURL = searchParams.get("url");
       let url = decodeURIComponent(encodedURL);
       // directory does not have origin set, in that case use the url origin for
       // the error message.
       if (!config.origin) {
         let URI = Services.io.newURI(url, null, null);
-        config.origin = Services.scriptSecurityManager.getNoAppCodebasePrincipal(URI).origin;
+        config.origin =
+          Services.scriptSecurityManager.createCodebasePrincipal(URI, {}).origin;
       }
 
       switch (mode) {
         case "compactInfo":
           document.getElementById("btnTryAgain").style.display = 'none';
           break;
         case "tryAgainOnly":
           //intentional fall-through
--- a/browser/base/content/test/general/browser_offlineQuotaNotification.js
+++ b/browser/base/content/test/general/browser_offlineQuotaNotification.js
@@ -6,17 +6,17 @@
 // Test offline quota warnings - must be run as a mochitest-browser test or
 // else the test runner gets in the way of notifications due to bug 857897.
 
 const URL = "http://mochi.test:8888/browser/browser/base/content/test/general/offlineQuotaNotification.html";
 
 registerCleanupFunction(function() {
   // Clean up after ourself
   let uri = Services.io.newURI(URL, null, null);
-  var principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
+  let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
   Services.perms.removeFromPrincipal(principal, "offline-app");
   Services.prefs.clearUserPref("offline-apps.quota.warn");
   Services.prefs.clearUserPref("offline-apps.allow_by_default");
 });
 
 // Check that the "preferences" UI is opened and showing which websites have
 // offline storage permissions - currently this is the "network" tab in the
 // "advanced" pane.
--- a/browser/base/content/test/general/browser_sanitizeDialog.js
+++ b/browser/base/content/test/general/browser_sanitizeDialog.js
@@ -559,17 +559,17 @@ var gAllTests = [
     var URL = "http://www.example.com";
 
     var ios = Cc["@mozilla.org/network/io-service;1"]
               .getService(Ci.nsIIOService);
     var URI = ios.newURI(URL, null, null);
 
     var sm = Cc["@mozilla.org/scriptsecuritymanager;1"]
              .getService(Ci.nsIScriptSecurityManager);
-    var principal = sm.getNoAppCodebasePrincipal(URI);
+    var principal = sm.createCodebasePrincipal(URI, {});
 
     // Give www.example.com privileges to store offline data
     var pm = Cc["@mozilla.org/permissionmanager;1"]
              .getService(Ci.nsIPermissionManager);
     pm.addFromPrincipal(principal, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
     pm.addFromPrincipal(principal, "offline-app", Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
 
     // Store something to the offline cache
@@ -629,17 +629,17 @@ var gAllTests = [
     var URL = "http://www.example.com";
 
     var ios = Cc["@mozilla.org/network/io-service;1"]
               .getService(Ci.nsIIOService);
     var URI = ios.newURI(URL, null, null);
 
     var sm = Cc["@mozilla.org/scriptsecuritymanager;1"]
              .getService(Ci.nsIScriptSecurityManager);
-    var principal = sm.getNoAppCodebasePrincipal(URI);
+    var principal = sm.createCodebasePrincipal(URI, {});
 
     // Open the dialog
     let wh = new WindowHelper();
     wh.onload = function () {
       this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
       // Show details
       this.toggleDetails();
       // Clear only offlineApps
--- a/browser/base/content/test/general/test_offlineNotification.html
+++ b/browser/base/content/test/general/test_offlineNotification.html
@@ -38,22 +38,20 @@ window.addEventListener("message", funct
       // Clean up after ourself
       var pm = Cc["@mozilla.org/permissionmanager;1"].
                getService(SpecialPowers.Ci.nsIPermissionManager);
       var ioService = Cc["@mozilla.org/network/io-service;1"]
                         .getService(SpecialPowers.Ci.nsIIOService);
       var uri1 = ioService.newURI(frames.testFrame.location, null, null);
       var uri2 = ioService.newURI(frames.testFrame3.location, null, null);
 
-      var principal1 = Cc["@mozilla.org/scriptsecuritymanager;1"]
-                        .getService(SpecialPowers.Ci.nsIScriptSecurityManager)
-                        .getNoAppCodebasePrincipal(uri1);
-      var principal2 = Cc["@mozilla.org/scriptsecuritymanager;1"]
-                        .getService(SpecialPowers.Ci.nsIScriptSecurityManager)
-                        .getNoAppCodebasePrincipal(uri2);
+      var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
+                  .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
+      var principal1 = ssm.createCodebasePrincipal(uri1, {});
+      var principal2 = ssm.createCodebasePrincipal(uri2, {});
 
       pm.removeFromPrincipal(principal1, "offline-app");
       pm.removeFromPrincipal(principal2, "offline-app");
 
       offlineByDefault.reset();
 
       SimpleTest.finish();
     }
--- a/browser/base/content/test/general/test_offline_gzip.html
+++ b/browser/base/content/test/general/test_offline_gzip.html
@@ -34,19 +34,19 @@ SimpleTest.waitForExplicitFinish();
 function finishTest() {
   // Clean up after ourselves.
   var Cc = SpecialPowers.Cc;
   var pm = Cc["@mozilla.org/permissionmanager;1"].
            getService(SpecialPowers.Ci.nsIPermissionManager);
 
   var uri = Cc["@mozilla.org/network/io-service;1"].getService(SpecialPowers.Ci.nsIIOService)
               .newURI(window.frames[0].location, null, null);
-  var principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
-                   .getService(SpecialPowers.Ci.nsIScriptSecurityManager)
-                   .getNoAppCodebasePrincipal(uri);
+  var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
+              .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
+  var principal = ssm.createCodebasePrincipal(uri, {});
 
   pm.removeFromPrincipal(principal, "offline-app");
 
   window.removeEventListener("message", handleMessageEvents, false);
 
   offlineByDefault.reset();
   SimpleTest.finish();  
 }
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -13,17 +13,17 @@ Cu.import("resource:///modules/Directory
 Cu.import("resource://testing-common/PlacesTestUtils.jsm", tmp);
 Cc["@mozilla.org/moz/jssubscript-loader;1"]
   .getService(Ci.mozIJSSubScriptLoader)
   .loadSubScript("chrome://browser/content/sanitize.js", tmp);
 Cu.import("resource://gre/modules/Timer.jsm", tmp);
 let {Promise, NewTabUtils, Sanitizer, clearTimeout, setTimeout, DirectoryLinksProvider, PlacesTestUtils} = tmp;
 
 let uri = Services.io.newURI("about:newtab", null, null);
-let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
+let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
 
 let isMac = ("nsILocalFileMac" in Ci);
 let isLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc);
 let isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
 let gWindow = window;
 
 // Default to dummy/empty directory links
 let gDirectorySource = 'data:application/json,{"test":1}';
--- a/browser/components/feeds/FeedConverter.js
+++ b/browser/components/feeds/FeedConverter.js
@@ -249,17 +249,17 @@ FeedConverter.prototype = {
         // page can access it.
         feedService.addFeedResult(result);
 
         // Now load the actual XUL document.
         var aboutFeedsURI = ios.newURI("about:feeds", null, null);
         chromeChannel = ios.newChannelFromURIWithLoadInfo(aboutFeedsURI, loadInfo);
         chromeChannel.originalURI = result.uri;
         chromeChannel.owner =
-          Services.scriptSecurityManager.getNoAppCodebasePrincipal(aboutFeedsURI);
+          Services.scriptSecurityManager.createCodebasePrincipal(aboutFeedsURI, {});
       } else {
         chromeChannel = ios.newChannelFromURIWithLoadInfo(result.uri, loadInfo);
       }
 
       chromeChannel.loadGroup = this._request.loadGroup;
       chromeChannel.asyncOpen(this._listener, null);
     }
     finally {
--- a/browser/components/preferences/aboutPermissions.js
+++ b/browser/components/preferences/aboutPermissions.js
@@ -492,17 +492,17 @@ let AboutPermissions = {
     gSitesStmt.params.limit = this.PLACES_SITES_LIMIT;
     gSitesStmt.executeAsync({
       handleResult: function(aResults) {
         AboutPermissions.startSitesListBatch();
         let row;
         while (row = aResults.getNextRow()) {
           let spec = row.getResultByName("url");
           let uri = NetUtil.newURI(spec);
-          let principal = gSecMan.getNoAppCodebasePrincipal(uri);
+          let principal = gSecMan.createCodebasePrincipal(uri, {});
 
           AboutPermissions.addPrincipal(principal);
         }
         AboutPermissions.endSitesListBatch();
       },
       handleError: function(aError) {
         Cu.reportError("AboutPermissions: " + aError);
       },
@@ -543,33 +543,33 @@ let AboutPermissions = {
     let logins = Services.logins.getAllLogins();
     logins.forEach(function(aLogin) {
       if (itemCnt % this.LIST_BUILD_CHUNK == 0) {
         yield true;
       }
       try {
         // aLogin.hostname is a string in origin URL format (e.g. "http://foo.com")
         let uri = NetUtil.newURI(aLogin.hostname);
-        let principal = gSecMan.getNoAppCodebasePrincipal(uri);
+        let principal = gSecMan.createCodebasePrincipal(uri, {});
         this.addPrincipal(principal);
       } catch (e) {
         // newURI will throw for add-ons logins stored in chrome:// URIs
       }
       itemCnt++;
     }, this);
 
     let disabledHosts = Services.logins.getAllDisabledHosts();
     disabledHosts.forEach(function(aHostname) {
       if (itemCnt % this.LIST_BUILD_CHUNK == 0) {
         yield true;
       }
       try {
         // aHostname is a string in origin URL format (e.g. "http://foo.com")
         let uri = NetUtil.newURI(aHostname);
-        let principal = gSecMan.getNoAppCodebasePrincipal(uri);
+        let principal = gSecMan.createCodebasePrincipal(uri, {});
         this.addPrincipal(principal);
       } catch (e) {
         // newURI will throw for add-ons logins stored in chrome:// URIs
       }
       itemCnt++;
     }, this);
 
     let enumerator = Services.perms.enumerator;
--- a/browser/components/preferences/permissions.js
+++ b/browser/components/preferences/permissions.js
@@ -90,22 +90,22 @@ var gPermissionManager = {
       // help catch cases where the URI parser parsed something like
       // `localhost:8080` as having the scheme `localhost`, rather than being
       // an invalid URI. A canonical origin representation is required by the
       // permission manager for storage, so this won't prevent any valid
       // permissions from being entered by the user.
       let uri;
       try {
         uri = Services.io.newURI(input_url, null, null);
-        principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
+        principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
         // If we have ended up with an unknown scheme, the following will throw.
         principal.origin;
       } catch(ex) {
         uri = Services.io.newURI("http://" + input_url, null, null);
-        principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
+        principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
         // If we have ended up with an unknown scheme, the following will throw.
         principal.origin;
       }
     } catch(ex) {
       var message = this._bundle.getString("invalidURI");
       var title = this._bundle.getString("invalidURITitle");
       Services.prompt.alert(window, title, message);
       return;
--- a/browser/components/preferences/tests/browser_permissions.js
+++ b/browser/components/preferences/tests/browser_permissions.js
@@ -3,18 +3,20 @@
 
 Components.utils.import("resource://gre/modules/NetUtil.jsm");
 
 const ABOUT_PERMISSIONS_SPEC = "about:permissions";
 
 const TEST_URI_1 = NetUtil.newURI("http://mozilla.com/");
 const TEST_URI_2 = NetUtil.newURI("http://mozilla.org/");
 
-const TEST_PRINCIPAL_1 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_URI_1);
-const TEST_PRINCIPAL_2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_URI_2);
+const TEST_PRINCIPAL_1 =
+  Services.scriptSecurityManager.createCodebasePrincipal(TEST_URI_1, {});
+const TEST_PRINCIPAL_2 =
+  Services.scriptSecurityManager.createCodebasePrincipal(TEST_URI_2, {});
 
 // values from DefaultPermissions object
 const PERM_UNKNOWN = 0;
 const PERM_ALLOW = 1;
 const PERM_DENY = 2;
 // cookie specific permissions
 const PERM_FIRST_PARTY_ONLY = 9;
 
--- a/browser/devtools/styleinspector/test/browser_styleinspector_csslogic-content-stylesheets.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_csslogic-content-stylesheets.js
@@ -10,19 +10,19 @@
 // toolkit/devtools/server/tests/mochitest/test_css-logic-...something...html
 // test
 
 const TEST_URI_HTML = TEST_URL_ROOT + "doc_content_stylesheet.html";
 const TEST_URI_XUL = TEST_URL_ROOT + "doc_content_stylesheet.xul";
 const XUL_URI = Cc["@mozilla.org/network/io-service;1"]
                 .getService(Ci.nsIIOService)
                 .newURI(TEST_URI_XUL, null, null);
-const XUL_PRINCIPAL = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
-                        .getService(Ci.nsIScriptSecurityManager)
-                        .getNoAppCodebasePrincipal(XUL_URI);
+let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
+                            .getService(Ci.nsIScriptSecurityManager);
+const XUL_PRINCIPAL = ssm.createCodebasePrincipal(XUL_URI, {});
 
 add_task(function*() {
   info("Checking stylesheets on HTML document");
   yield addTab(TEST_URI_HTML);
   let target = getNode("#target");
 
   let {inspector} = yield openRuleView();
   yield selectNode("#target", inspector);
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
@@ -990,24 +990,31 @@ PdfStreamConverter.prototype = {
     };
 
     // Keep the URL the same so the browser sees it as the same.
     channel.originalURI = aRequest.URI;
     channel.loadGroup = aRequest.loadGroup;
 
     // We can use resource principal when data is fetched by the chrome
     // e.g. useful for NoScript
-    var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1']
-                          .getService(Ci.nsIScriptSecurityManager);
+    var ssm = Cc['@mozilla.org/scriptsecuritymanager;1']
+                .getService(Ci.nsIScriptSecurityManager);
     var uri = NetUtil.newURI(PDF_VIEWER_WEB_PAGE, null, null);
     // FF16 and below had getCodebasePrincipal, it was replaced by
     // getNoAppCodebasePrincipal (bug 758258).
-    var resourcePrincipal = 'getNoAppCodebasePrincipal' in securityManager ?
-                            securityManager.getNoAppCodebasePrincipal(uri) :
-                            securityManager.getCodebasePrincipal(uri);
+    // FF 43 added createCodebasePrincipal to replace getNoAppCodebasePrincipal
+    // (bug 1165272).
+    var resourcePrincipal
+    if ('createCodebasePrincipal' in ssm) {
+      resourcePrincipal = ssm.createCodebasePrincipal(uri, {});
+    } else if ('getNoAppCodebasePrincipal' in ssm) {
+      resourcePrincipal = ssm.getNoAppCodebasePrincipal(uri)
+    } else {
+      resourcePrincipal = ssm.getCodebasePrincipal(uri);
+    }
     aRequest.owner = resourcePrincipal;
     channel.asyncOpen(proxy, aContext);
   },
 
   // nsIRequestObserver::onStopRequest
   onStopRequest: function(aRequest, aContext, aStatusCode) {
     if (!this.dataListener) {
       // Do nothing
--- a/browser/extensions/shumway/chrome/SpecialStorage.jsm
+++ b/browser/extensions/shumway/chrome/SpecialStorage.jsm
@@ -17,19 +17,19 @@
 var EXPORTED_SYMBOLS = ['SpecialStorageUtils'];
 
 Components.utils.import('resource://gre/modules/Services.jsm');
 
 var SpecialStorageUtils = {
   createWrappedSpecialStorage: function (sandbox, swfUrl, privateBrowsing) {
     // Creating internal localStorage object based on url and privateBrowsing setting.
     var uri = Services.io.newURI(swfUrl, null, null);
-    var principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
-                              .getService(Components.interfaces.nsIScriptSecurityManager)
-                              .getNoAppCodebasePrincipal(uri);
+    var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
+                                .getService(Components.interfaces.nsIScriptSecurityManager);
+    var principal = ssm.createCodebasePrincipal(uri, {});
     var dsm = Components.classes["@mozilla.org/dom/localStorage-manager;1"]
                                 .getService(Components.interfaces.nsIDOMStorageManager);
     var storage = dsm.createStorage(null, principal, privateBrowsing);
 
     // We will return object created in the sandbox/content, with some exposed
     // properties/methods, so we can send data between wrapped object and
     // and sandbox/content.
     var wrapper = Components.utils.cloneInto({
--- a/browser/modules/Feeds.jsm
+++ b/browser/modules/Feeds.jsm
@@ -61,17 +61,18 @@ this.Feeds = {
     if (!aIsFeed) {
       aIsFeed = (type == "application/rss+xml" ||
                  type == "application/atom+xml");
     }
 
     if (aIsFeed) {
       // re-create the principal as it may be a CPOW.
       let principalURI = BrowserUtils.makeURIFromCPOW(aPrincipal.URI);
-      let principalToCheck = Services.scriptSecurityManager.getNoAppCodebasePrincipal(principalURI);
+      let principalToCheck =
+        Services.scriptSecurityManager.createCodebasePrincipal(principalURI, {});
       try {
         BrowserUtils.urlSecurityCheck(aLink.href, principalToCheck,
                                       Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
         return type || "application/rss+xml";
       }
       catch(ex) {
       }
     }
--- a/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -14,32 +14,16 @@ MOZ_NATIVE_ICU=
 MOZ_ARG_WITH_BOOL(system-icu,
 [  --with-system-icu
                           Use system ICU (located with pkgconfig)],
     MOZ_NATIVE_ICU=1)
 
 if test -n "$MOZ_NATIVE_ICU"; then
     PKG_CHECK_MODULES(MOZ_ICU, icu-i18n >= 50.1)
     MOZ_SHARED_ICU=1
-elif test -n "$gonkdir" && test "$ANDROID_VERSION" -ge 18; then
-    dnl Use system's ICU since version is 50.1+.
-    if test -d "$gonkdir/external/icu/icu4c/source"; then
-        dnl gonk-L (API version is 21)
-        MOZ_ICU_GONK_PATH="$gonkdir/external/icu/icu4c/source"
-    elif test -d "$gonkdir/external/icu4c"; then
-        MOZ_ICU_GONK_PATH="$gonkdir/external/icu4c"
-    else
-        AC_MSG_ERROR([Cannot find ICU source code under gonk])
-    fi
-    MOZ_ICU_CFLAGS="-I$MOZ_ICU_GONK_PATH/common -I$MOZ_ICU_GONK_PATH/i18n"
-    dnl icudata is a datafile under /usr/icu/icudt<version number>l.dat,
-    dnl not shared library.  So we don't link to icudata on B2G.
-    MOZ_ICU_LIBS='-licui18n -licuuc'
-    MOZ_NATIVE_ICU=1
-    MOZ_SHARED_ICU=1
 else
     MOZ_ICU_CFLAGS='-I$(topsrcdir)/intl/icu/source/common -I$(topsrcdir)/intl/icu/source/i18n'
     AC_SUBST_LIST(MOZ_ICU_CFLAGS)
 fi
 
 AC_SUBST(MOZ_NATIVE_ICU)
 
 MOZ_ARG_WITH_STRING(intl-api,
--- a/build/gen_mach_buildprops.py
+++ b/build/gen_mach_buildprops.py
@@ -51,24 +51,25 @@ def getPartialInfo(props):
 if __name__ == '__main__':
     parser = ArgumentParser(description='Generate mach_build_properties.json for automation builds.')
     parser.add_argument("--complete-mar-file", required=True,
                         action="store", dest="complete_mar_file",
                         help="Path to the complete MAR file, relative to the objdir.")
     parser.add_argument("--partial-mar-file", required=False,
                         action="store", dest="partial_mar_file",
                         help="Path to the partial MAR file, relative to the objdir.")
-    parser.add_argument("--upload-properties", required=True,
+    parser.add_argument("--upload-properties", required=False,
                         action="store", dest="upload_properties",
                         help="Path to the properties written by 'make upload'")
     args = parser.parse_args()
 
     json_data = getMarProperties(args.complete_mar_file)
-    with open(args.upload_properties) as f:
-        json_data.update(json.load(f))
+    if args.upload_properties:
+        with open(args.upload_properties) as f:
+            json_data.update(json.load(f))
     if args.partial_mar_file:
         json_data.update(getMarProperties(args.partial_mar_file, partial=True))
 
         # Pull the previous buildid from the partial mar filename.
         res = re.match(r'.*\.([0-9]+)-[0-9]+.mar', args.partial_mar_file)
         if res:
             json_data['previous_buildid'] = res.group(1)
 
--- a/build/moz-automation.mk
+++ b/build/moz-automation.mk
@@ -12,18 +12,20 @@ endif
 endif
 
 include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
 include $(topsrcdir)/toolkit/mozapps/installer/upload-files.mk
 
 # Clear out DIST_FILES if it was set by upload-files.mk (for Android builds)
 DIST_FILES =
 
+ifeq (1,$(MOZ_AUTOMATION_UPLOAD))
 # Properties from 'make upload' that are file URLs.
-AUTOMATION_UPLOAD_PROPERTIES = $(DIST)/upload-properties.json
+AUTOMATION_UPLOAD_PROPERTIES = --upload-properties $(DIST)/upload-properties.json
+endif
 
 # Helper variables to convert from MOZ_AUTOMATION_* variables to the
 # corresponding the make target
 tier_MOZ_AUTOMATION_BUILD_SYMBOLS = buildsymbols
 tier_MOZ_AUTOMATION_L10N_CHECK = l10n-check
 tier_MOZ_AUTOMATION_PRETTY_L10N_CHECK = pretty-l10n-check
 tier_MOZ_AUTOMATION_INSTALLER = installer
 tier_MOZ_AUTOMATION_PRETTY_INSTALLER = pretty-installer
@@ -93,17 +95,17 @@ automation/sdk: automation/installer aut
 # conflicts in writing to the same files.
 automation/installer: automation/pretty-installer
 automation/package: automation/pretty-package
 automation/package-tests: automation/pretty-package-tests
 automation/l10n-check: automation/pretty-l10n-check
 automation/update-packaging: automation/pretty-update-packaging
 
 automation/build: $(addprefix automation/,$(MOZ_AUTOMATION_TIERS))
-	$(PYTHON) $(topsrcdir)/build/gen_mach_buildprops.py --complete-mar-file $(DIST)/$(COMPLETE_MAR) $(addprefix --partial-mar-file ,$(wildcard $(DIST)/$(PARTIAL_MAR))) --upload-properties $(AUTOMATION_UPLOAD_PROPERTIES)
+	$(PYTHON) $(topsrcdir)/build/gen_mach_buildprops.py --complete-mar-file $(DIST)/$(COMPLETE_MAR) $(addprefix --partial-mar-file ,$(wildcard $(DIST)/$(PARTIAL_MAR))) $(AUTOMATION_UPLOAD_PROPERTIES)
 
 # Note: We have to force -j1 here, at least until bug 1036563 is fixed.
 AUTOMATION_EXTRA_CMDLINE-l10n-check = -j1
 AUTOMATION_EXTRA_CMDLINE-pretty-l10n-check = -j1
 
 # And force -j1 here until bug 1077670 is fixed.
 AUTOMATION_EXTRA_CMDLINE-package-tests = -j1
 AUTOMATION_EXTRA_CMDLINE-pretty-package-tests = -j1
--- a/caps/nsIScriptSecurityManager.idl
+++ b/caps/nsIScriptSecurityManager.idl
@@ -21,17 +21,17 @@ class DomainPolicyClone;
 }
 }
 %}
 
 [ptr] native JSContextPtr(JSContext);
 [ptr] native JSObjectPtr(JSObject);
 [ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone);
 
-[scriptable, uuid(9a8f0b70-6b9f-4e19-8885-7cfe24f4a42d)]
+[scriptable, uuid(6e8a4d1e-d9c6-4d86-bf53-d73f58f36148)]
 interface nsIScriptSecurityManager : nsISupports
 {
     /**
      * For each of these hooks returning NS_OK means 'let the action continue'.
      * Returning an error code means 'veto the action'. XPConnect will return
      * false to the js engine if the action is vetoed. The implementor of this
      * interface is responsible for setting a JS exception into the JSContext
      * if that is appropriate.
@@ -145,20 +145,22 @@ interface nsIScriptSecurityManager : nsI
      */
     nsIPrincipal getSimpleCodebasePrincipal(in nsIURI aURI);
 
     /**
      * Returns a principal that has the given information.
      * @param appId is the app id of the principal. It can't be UNKNOWN_APP_ID.
      * @param inMozBrowser is true if the principal has to be considered as
      * inside a mozbrowser frame.
+     *
+     * @deprecated use createCodebasePrincipal instead.
      */
-    nsIPrincipal getAppCodebasePrincipal(in nsIURI uri,
-                                         in unsigned long appId,
-                                         in boolean inMozBrowser);
+    [deprecated] nsIPrincipal getAppCodebasePrincipal(in nsIURI uri,
+                                                      in unsigned long appId,
+                                                      in boolean inMozBrowser);
 
     /**
      * Returns a principal that has the appId and inMozBrowser of the load
      * context.
      * @param loadContext to get appId/inMozBrowser from.
      */
     nsIPrincipal getLoadContextCodebasePrincipal(in nsIURI uri,
                                                  in nsILoadContext loadContext);
@@ -170,18 +172,20 @@ interface nsIScriptSecurityManager : nsI
      */
     nsIPrincipal getDocShellCodebasePrincipal(in nsIURI uri,
                                               in nsIDocShell docShell);
 
     /**
      * Returns a principal with that has the same origin as uri and is not part
      * of an appliction.
      * The returned principal will have appId = NO_APP_ID.
+     *
+     * @deprecated use createCodebasePrincipal instead.
      */
-    nsIPrincipal getNoAppCodebasePrincipal(in nsIURI uri);
+    [deprecated] nsIPrincipal getNoAppCodebasePrincipal(in nsIURI uri);
 
     /**
      * Legacy method for getting a principal with no origin attributes.
      *
      * @deprecated use createCodebasePrincipal instead.
      */
     [deprecated] nsIPrincipal getCodebasePrincipal(in nsIURI uri);
 
--- a/config/check_spidermonkey_style.py
+++ b/config/check_spidermonkey_style.py
@@ -73,16 +73,17 @@ included_inclnames_to_ignore = set([
     'prlink.h',                 # NSPR
     'prlock.h',                 # NSPR
     'prprf.h',                  # NSPR
     'prthread.h',               # NSPR
     'prtypes.h',                # NSPR
     'selfhosted.out.h',         # generated in $OBJDIR
     'unicode/locid.h',          # ICU
     'unicode/numsys.h',         # ICU
+    'unicode/timezone.h',       # ICU
     'unicode/ucal.h',           # ICU
     'unicode/uclean.h',         # ICU
     'unicode/ucol.h',           # ICU
     'unicode/udat.h',           # ICU
     'unicode/udatpg.h',         # ICU
     'unicode/uenum.h',          # ICU
     'unicode/unorm.h',          # ICU
     'unicode/unum.h',           # ICU
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -6,16 +6,17 @@
 
 #include "nsDocShell.h"
 
 #include <algorithm>
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
+#include "mozilla/BasePrincipal.h"
 #include "mozilla/Casting.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 #include "mozilla/dom/ScreenOrientation.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
@@ -9355,32 +9356,31 @@ nsDocShell::JustStartedNetworkLoad()
   return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
 }
 
 nsresult
 nsDocShell::CreatePrincipalFromReferrer(nsIURI* aReferrer,
                                         nsIPrincipal** aResult)
 {
   nsresult rv;
-  nsCOMPtr<nsIScriptSecurityManager> secMan =
-    do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
 
   uint32_t appId;
   rv = GetAppId(&appId);
   NS_ENSURE_SUCCESS(rv, rv);
   bool isInBrowserElement;
   rv = GetIsInBrowserElement(&isInBrowserElement);
   NS_ENSURE_SUCCESS(rv, rv);
-  rv = secMan->GetAppCodebasePrincipal(aReferrer,
-                                       appId,
-                                       isInBrowserElement,
-                                       aResult);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return NS_OK;
+
+  // TODO: Bug 1165466 - Pass mOriginAttributes directly.
+  OriginAttributes attrs(appId, isInBrowserElement);
+  nsCOMPtr<nsIPrincipal> prin =
+    BasePrincipal::CreateCodebasePrincipal(aReferrer, attrs);
+  prin.forget(aResult);
+
+  return *aResult ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsDocShell::InternalLoad(nsIURI* aURI,
                          nsIURI* aReferrer,
                          uint32_t aReferrerPolicy,
                          nsISupports* aOwner,
                          uint32_t aFlags,
--- a/dom/apps/AppsUtils.jsm
+++ b/dom/apps/AppsUtils.jsm
@@ -68,21 +68,19 @@ mozIApplication.prototype = {
   get principal() {
     if (this._principal) {
       return this._principal;
     }
 
     this._principal = null;
 
     try {
-      this._principal = Services.scriptSecurityManager.getAppCodebasePrincipal(
+      this._principal = Services.scriptSecurityManager.createCodebasePrincipal(
         Services.io.newURI(this.origin, null, null),
-        this.localId,
-        false /* mozbrowser */
-      );
+        {appId: this.localId});
     } catch(e) {
       dump("Could not create app principal " + e + "\n");
     }
 
     return this._principal;
   },
 
   QueryInterface: function(aIID) {
--- a/dom/apps/OfflineCacheInstaller.jsm
+++ b/dom/apps/OfflineCacheInstaller.jsm
@@ -223,18 +223,18 @@ function installCache(app) {
   if (!cacheDir.exists())
     return;
 
   let cacheManifest = cacheDir.clone();
   cacheManifest.append('manifest.appcache');
   if (!cacheManifest.exists())
     return;
 
-  let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(
-      app.origin, app.localId, false);
+  let principal =
+    Services.scriptSecurityManager.createCodebasePrincipal(app.origin, {appId: aApp.localId});
 
   // If the build has been correctly configured, this should not happen!
   // If we install the cache anyway, it won't be updateable. If we don't install
   // it, the application won't be useable offline.
   let metadataLoaded;
   if (!resourcesMetadata.exists()) {
     // Not debug, since this is something that should be logged always!
     dump("OfflineCacheInstaller: App " + app.appId + " does have an app cache" +
--- a/dom/apps/ScriptPreloader.jsm
+++ b/dom/apps/ScriptPreloader.jsm
@@ -35,17 +35,17 @@ this.ScriptPreloader = {
 
     if (aManifest.precompile &&
         Array.isArray(aManifest.precompile) &&
         aManifest.precompile.length > 0) {
       let origin = Services.io.newURI(aApp.origin, null, null);
       let toLoad = aManifest.precompile.length;
       let principal =
         Services.scriptSecurityManager
-                .getAppCodebasePrincipal(origin, aApp.localId, false);
+                .createCodebasePrincipal(origin, {appId: aApp.localId});
 
       aManifest.precompile.forEach((aPath) => {
         let uri = Services.io.newURI(aPath, null, origin);
         debug("Script to compile: " + uri.spec);
         try {
           Services.scriptloader.precompileScript(uri, principal,
             (aSubject, aTopic, aData) => {
               let uri = aSubject.QueryInterface(Ci.nsIURI);
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -815,18 +815,17 @@ this.DOMApplicationRegistry = {
     if (!aManifest) {
       debug("updateDataStore: no manifest for " + aOrigin);
       return;
     }
 
     let uri = Services.io.newURI(aOrigin, null, null);
     let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
                    .getService(Ci.nsIScriptSecurityManager);
-    let principal = secMan.getAppCodebasePrincipal(uri, aId,
-                                                   /*mozbrowser*/ false);
+    let principal = secMan.createCodebasePrincipal(uri, {appId: aId});
     if (!dataStoreService.checkPermission(principal)) {
       return;
     }
 
     if ('datastores-owned' in aManifest) {
       for (let name in aManifest['datastores-owned']) {
         let readonly = "access" in aManifest['datastores-owned'][name]
                          ? aManifest['datastores-owned'][name].access == 'readonly'
@@ -3356,18 +3355,19 @@ this.DOMApplicationRegistry = {
     return true;
   },
 
   _getRequestChannel: function(aFullPackagePath, aIsLocalFileInstall, aOldApp,
                                aNewApp) {
     let requestChannel;
 
     let appURI = NetUtil.newURI(aNewApp.origin, null, null);
-    let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(
-                      appURI, aNewApp.localId, false);
+    let principal =
+      Services.scriptSecurityManager.createCodebasePrincipal(appURI,
+                                                             {appId: aNewApp.localId});
 
     if (aIsLocalFileInstall) {
       requestChannel = NetUtil.newChannel({
         uri: aFullPackagePath,
         loadingPrincipal: principal,
         contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER}
       ).QueryInterface(Ci.nsIFileChannel);
     } else {
--- a/dom/base/TextInputProcessor.cpp
+++ b/dom/base/TextInputProcessor.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "gfxPrefs.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/TextEventDispatcher.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TextInputProcessor.h"
 #include "nsIDocShell.h"
 #include "nsIWidget.h"
 #include "nsPIDOMWindow.h"
 #include "nsPresContext.h"
@@ -279,18 +280,21 @@ TextInputProcessor::MaybeDispatchKeydown
   // Modifier keys are not allowed because managing modifier state in this
   // method makes this messy.
   if (NS_WARN_IF(aKeyboardEvent->IsModifierKeyEvent())) {
     result.mResult = NS_ERROR_INVALID_ARG;
     result.mCanContinue = false;
     return result;
   }
 
+  uint32_t consumedFlags = 0;
+
   result.mResult = KeydownInternal(*aKeyboardEvent, aKeyFlags, false,
-                                   result.mDoDefault);
+                                   consumedFlags);
+  result.mDoDefault = !consumedFlags;
   if (NS_WARN_IF(NS_FAILED(result.mResult))) {
     result.mCanContinue = false;
     return result;
   }
 
   result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition());
   return result;
 }
@@ -749,50 +753,68 @@ TextInputProcessor::PrepareKeyboardEvent
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TextInputProcessor::Keydown(nsIDOMKeyEvent* aDOMKeyEvent,
                             uint32_t aKeyFlags,
                             uint8_t aOptionalArgc,
-                            bool* aDoDefault)
+                            uint32_t* aConsumedFlags)
 {
-  MOZ_RELEASE_ASSERT(aDoDefault, "aDoDefault must not be nullptr");
+  MOZ_RELEASE_ASSERT(aConsumedFlags, "aConsumedFlags must not be nullptr");
   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
   if (!aOptionalArgc) {
     aKeyFlags = 0;
   }
   if (NS_WARN_IF(!aDOMKeyEvent)) {
     return NS_ERROR_INVALID_ARG;
   }
   WidgetKeyboardEvent* originalKeyEvent =
     aDOMKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
   if (NS_WARN_IF(!originalKeyEvent)) {
     return NS_ERROR_INVALID_ARG;
   }
-  return KeydownInternal(*originalKeyEvent, aKeyFlags, true, *aDoDefault);
+  return KeydownInternal(*originalKeyEvent, aKeyFlags, true, *aConsumedFlags);
+}
+
+TextEventDispatcher::DispatchTo
+TextInputProcessor::GetDispatchTo() const
+{
+  // Support asynchronous tests.
+  if (mForTests) {
+    return gfxPrefs::TestEventsAsyncEnabled() ?
+             TextEventDispatcher::eDispatchToParentProcess :
+             TextEventDispatcher::eDispatchToCurrentProcess;
+  }
+
+  // Otherwise, TextInputProcessor supports only keyboard apps on B2G.
+  // Keyboard apps on B2G doesn't want to dispatch keyboard events to
+  // chrome process. Therefore, this should dispatch key events only in
+  // the current process.
+  return TextEventDispatcher::eDispatchToCurrentProcess;
 }
 
 nsresult
 TextInputProcessor::KeydownInternal(const WidgetKeyboardEvent& aKeyboardEvent,
                                     uint32_t aKeyFlags,
                                     bool aAllowToDispatchKeypress,
-                                    bool& aDoDefault)
+                                    uint32_t& aConsumedFlags)
 {
-  aDoDefault = false;
+  aConsumedFlags = KEYEVENT_NOT_CONSUMED;
 
   // We shouldn't modify the internal WidgetKeyboardEvent.
   WidgetKeyboardEvent keyEvent(aKeyboardEvent);
   nsresult rv = PrepareKeyboardEventToDispatch(keyEvent, aKeyFlags);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  aDoDefault = !(aKeyFlags & KEY_DEFAULT_PREVENTED);
+  aConsumedFlags = (aKeyFlags & KEY_DEFAULT_PREVENTED) ? KEYDOWN_IS_CONSUMED :
+                                                         KEYEVENT_NOT_CONSUMED;
 
   if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent.mKeyNameIndex)) {
     ModifierKeyData modifierKeyData(keyEvent);
     if (WidgetKeyboardEvent::IsLockableModifier(keyEvent.mKeyNameIndex)) {
       // If the modifier key is lockable modifier key such as CapsLock,
       // let's toggle modifier key state at keydown.
       ToggleModifierKey(modifierKeyData);
     } else {
@@ -809,29 +831,37 @@ TextInputProcessor::KeydownInternal(cons
   keyEvent.modifiers = GetActiveModifiers();
 
   nsRefPtr<TextEventDispatcher> kungfuDeathGrip(mDispatcher);
   rv = IsValidStateForComposition();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  nsEventStatus status = aDoDefault ? nsEventStatus_eIgnore :
-                                      nsEventStatus_eConsumeNoDefault;
-  if (!mDispatcher->DispatchKeyboardEvent(NS_KEY_DOWN, keyEvent, status)) {
+  nsEventStatus status = aConsumedFlags ? nsEventStatus_eConsumeNoDefault :
+                                          nsEventStatus_eIgnore;
+  if (!mDispatcher->DispatchKeyboardEvent(NS_KEY_DOWN, keyEvent, status,
+                                          GetDispatchTo())) {
     // If keydown event isn't dispatched, we don't need to dispatch keypress
     // events.
     return NS_OK;
   }
 
-  if (aAllowToDispatchKeypress) {
-    mDispatcher->MaybeDispatchKeypressEvents(keyEvent, status);
+  aConsumedFlags |=
+    (status == nsEventStatus_eConsumeNoDefault) ? KEYDOWN_IS_CONSUMED :
+                                                  KEYEVENT_NOT_CONSUMED;
+
+  if (aAllowToDispatchKeypress &&
+      mDispatcher->MaybeDispatchKeypressEvents(keyEvent, status, 
+                                               GetDispatchTo())) {
+    aConsumedFlags |=
+      (status == nsEventStatus_eConsumeNoDefault) ? KEYPRESS_IS_CONSUMED :
+                                                    KEYEVENT_NOT_CONSUMED;
   }
 
-  aDoDefault = (status != nsEventStatus_eConsumeNoDefault);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TextInputProcessor::Keyup(nsIDOMKeyEvent* aDOMKeyEvent,
                           uint32_t aKeyFlags,
                           uint8_t aOptionalArgc,
                           bool* aDoDefault)
@@ -885,17 +915,18 @@ TextInputProcessor::KeyupInternal(const 
   nsRefPtr<TextEventDispatcher> kungfuDeathGrip(mDispatcher);
   rv = IsValidStateForComposition();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsEventStatus status = aDoDefault ? nsEventStatus_eIgnore :
                                       nsEventStatus_eConsumeNoDefault;
-  mDispatcher->DispatchKeyboardEvent(NS_KEY_UP, keyEvent, status);
+  mDispatcher->DispatchKeyboardEvent(NS_KEY_UP, keyEvent, status,
+                                     GetDispatchTo());
   aDoDefault = (status != nsEventStatus_eConsumeNoDefault);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TextInputProcessor::GetModifierState(const nsAString& aModifierKeyName,
                                      bool* aActive)
 {
--- a/dom/base/TextInputProcessor.h
+++ b/dom/base/TextInputProcessor.h
@@ -4,28 +4,25 @@
  * 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_textinputprocessor_h_
 #define mozilla_dom_textinputprocessor_h_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
+#include "mozilla/TextEventDispatcher.h"
 #include "mozilla/TextEventDispatcherListener.h"
 #include "nsAutoPtr.h"
 #include "nsITextInputProcessor.h"
 #include "nsITextInputProcessorCallback.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 
-namespace widget{
-class TextEventDispatcher;
-} // namespace widget
-
 class TextInputProcessor final : public nsITextInputProcessor
                                , public widget::TextEventDispatcherListener
 {
   typedef mozilla::widget::IMENotification IMENotification;
   typedef mozilla::widget::TextEventDispatcher TextEventDispatcher;
 
 public:
   TextInputProcessor();
@@ -55,20 +52,21 @@ private:
              const nsAString* aCommitString = nullptr,
              bool* aSucceeded = nullptr);
   nsresult CancelCompositionInternal(
              const WidgetKeyboardEvent* aKeyboardEvent = nullptr,
              uint32_t aKeyFlags = 0);
   nsresult KeydownInternal(const WidgetKeyboardEvent& aKeyboardEvent,
                            uint32_t aKeyFlags,
                            bool aAllowToDispatchKeypress,
-                           bool& aDoDefault);
+                           uint32_t& aConsumedFlags);
   nsresult KeyupInternal(const WidgetKeyboardEvent& aKeyboardEvent,
                          uint32_t aKeyFlags,
                          bool& aDoDefault);
+  TextEventDispatcher::DispatchTo GetDispatchTo() const;
   nsresult IsValidStateForComposition();
   void UnlinkFromTextEventDispatcher();
   nsresult PrepareKeyboardEventToDispatch(WidgetKeyboardEvent& aKeyboardEvent,
                                           uint32_t aKeyFlags);
   bool IsValidEventTypeForComposition(
          const WidgetKeyboardEvent& aKeyboardEvent) const;
   nsresult PrepareKeyboardEventForComposition(
              nsIDOMKeyEvent* aDOMKeyEvent,
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -7899,18 +7899,22 @@ nsDocument::GetViewportInfo(const Screen
   CSSToLayoutDeviceScale layoutDeviceScale(widgetScale * fullZoom);
 
   CSSToScreenScale defaultScale = layoutDeviceScale
                                 * LayoutDeviceToScreenScale(1.0);
   // Get requested Desktopmode
   nsPIDOMWindow* win = GetWindow();
   if (win && win->IsDesktopModeViewport())
   {
-    return nsViewportInfo(aDisplaySize,
-                          defaultScale,
+    float viewportWidth = gfxPrefs::DesktopViewportWidth() / fullZoom;
+    float scaleToFit = aDisplaySize.width / viewportWidth;
+    float aspectRatio = (float)aDisplaySize.height / aDisplaySize.width;
+    ScreenSize viewportSize(viewportWidth, viewportWidth * aspectRatio);
+    return nsViewportInfo(RoundedToInt(viewportSize),
+                          CSSToScreenScale(scaleToFit),
                           /*allowZoom*/false,
                           /*allowDoubleTapZoom*/ false);
   }
 
   if (!gfxPrefs::MetaViewportEnabled()) {
     return nsViewportInfo(aDisplaySize,
                           defaultScale,
                           /*allowZoom*/ false,
@@ -8055,18 +8059,17 @@ nsDocument::GetViewportInfo(const Screen
 
     if (!mValidWidth) {
       if (mValidHeight && !aDisplaySize.IsEmpty()) {
         size.width = size.height * aDisplaySize.width / aDisplaySize.height;
       } else {
         // Stretch CSS pixel size of viewport to keep device pixel size
         // unchanged after full zoom applied.
         // See bug 974242.
-        size.width = Preferences::GetInt("browser.viewport.desktopWidth",
-                                         kViewportDefaultScreenWidth) / fullZoom;
+        size.width = gfxPrefs::DesktopViewportWidth() / fullZoom;
       }
     }
 
     if (!mValidHeight) {
       if (!aDisplaySize.IsEmpty()) {
         size.height = size.width * aDisplaySize.height / aDisplaySize.width;
       } else {
         size.height = size.width;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -90,17 +90,16 @@
 #include "nsIDOMElement.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsDOMString.h"
 #include "nsIEmbeddingSiteWindow.h"
 #include "nsThreadUtils.h"
 #include "nsILoadContext.h"
 #include "nsIPresShell.h"
-#include "nsIScriptSecurityManager.h"
 #include "nsIScrollableFrame.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsISelectionController.h"
 #include "nsISelection.h"
 #include "nsIPrompt.h"
 #include "nsIPromptService.h"
 #include "nsIPromptFactory.h"
@@ -187,16 +186,17 @@
 #include "mozilla/dom/GamepadService.h"
 #endif
 
 #include "mozilla/dom/VRDevice.h"
 
 #include "nsRefreshDriver.h"
 
 #include "mozilla/AddonPathService.h"
+#include "mozilla/BasePrincipal.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "nsLocation.h"
 #include "nsHTMLDocument.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "prrng.h"
 #include "nsSandboxFlags.h"
@@ -251,16 +251,18 @@ static PRLogModuleInfo* gDOMLeakPRLog;
 #include <unistd.h> // for getpid()
 #endif
 
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
+using mozilla::BasePrincipal;
+using mozilla::OriginAttributes;
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
 using mozilla::dom::cache::CacheStorage;
 using mozilla::dom::indexedDB::IDBFactory;
 
 nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nullptr;
 bool nsGlobalWindow::sWarnedAboutWindowInternal = false;
 bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false;
@@ -6519,17 +6521,25 @@ nsGlobalWindow::SetFullscreenInternal(Fu
   // If we are already in full screen mode, just return.
   if (mFullScreen == aFullScreen)
     return NS_OK;
 
   // Note that although entering DOM fullscreen could also cause
   // consequential calls to this method, those calls will be skipped
   // at the condition above.
   if (aReason == eForFullscreenMode) {
-    mFullscreenMode = aFullScreen;
+    if (!aFullScreen && !mFullscreenMode) {
+      // If we are exiting fullscreen mode, but we actually didn't
+      // entered fullscreen mode, the fullscreen state was only for
+      // the Fullscreen API. Change the reason here so that we can
+      // perform transition for it.
+      aReason = eForFullscreenAPI;
+    } else {
+      mFullscreenMode = aFullScreen;
+    }
   } else {
     // If we are exiting from DOM fullscreen while we initially make
     // the window fullscreen because of fullscreen mode, don't restore
     // the window. But we still need to exit the DOM fullscreen state.
     if (!aFullScreen && mFullscreenMode) {
       FinishDOMFullscreenChange(mDoc, false);
       return NS_OK;
     }
@@ -8574,31 +8584,24 @@ nsGlobalWindow::PostMessageMozOuter(JSCo
       return;
     }
 
     if (NS_FAILED(originURI->SetUserPass(EmptyCString())) ||
         NS_FAILED(originURI->SetPath(EmptyCString()))) {
       return;
     }
 
-    nsCOMPtr<nsIScriptSecurityManager> ssm =
-      nsContentUtils::GetSecurityManager();
-    MOZ_ASSERT(ssm);
-
     nsCOMPtr<nsIPrincipal> principal = nsContentUtils::SubjectPrincipal();
     MOZ_ASSERT(principal);
 
-    uint32_t appId = principal->GetAppId();
-    bool isInBrowser = principal->GetIsInBrowserElement();
-
+    OriginAttributes attrs = BasePrincipal::Cast(principal)->OriginAttributesRef();
     // Create a nsIPrincipal inheriting the app/browser attributes from the
     // caller.
-    nsresult rv = ssm->GetAppCodebasePrincipal(originURI, appId, isInBrowser,
-                                             getter_AddRefs(providedPrincipal));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
+    providedPrincipal = BasePrincipal::CreateCodebasePrincipal(originURI, attrs);
+    if (NS_WARN_IF(!providedPrincipal)) {
       return;
     }
   }
 
   // Create and asynchronously dispatch a runnable which will handle actual DOM
   // event creation and dispatch.
   nsRefPtr<PostMessageEvent> event =
     new PostMessageEvent(nsContentUtils::IsCallerChrome() || !callerInnerWin
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -302,16 +302,22 @@ nsJSUtils::GetScopeChainForElement(JSCon
     if (!aScopeChain.append(&val.toObject())) {
       return false;
     }
   }
 
   return true;
 }
 
+/* static */
+void
+nsJSUtils::ResetTimeZone()
+{
+  JS::ResetTimeZone();
+}
 
 //
 // nsDOMJSUtils.h
 //
 
 JSObject* GetDefaultScopeFromJSContext(JSContext *cx)
 {
   // DOM JSContexts don't store their default compartment object on
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -108,16 +108,19 @@ public:
                                  JS::CompileOptions &aCompileOptions,
                                  void **aOffThreadToken);
 
   // Returns false if an exception got thrown on aCx.  Passing a null
   // aElement is allowed; that wil produce an empty aScopeChain.
   static bool GetScopeChainForElement(JSContext* aCx,
                                       mozilla::dom::Element* aElement,
                                       JS::AutoObjectVector& aScopeChain);
+
+  static void ResetTimeZone();
+
 private:
   // Implementation for our EvaluateString bits
   static nsresult EvaluateString(JSContext* aCx,
                                  JS::SourceBufferHolder& aSrcBuf,
                                  JS::Handle<JSObject*> aEvaluationGlobal,
                                  JS::CompileOptions& aCompileOptions,
                                  const EvaluateOptions& aEvaluateOptions,
                                  JS::MutableHandle<JS::Value> aRetValue,
--- a/dom/base/nsViewportInfo.h
+++ b/dom/base/nsViewportInfo.h
@@ -11,17 +11,16 @@
 
 /**
  * Default values for the nsViewportInfo class.
  */
 static const mozilla::LayoutDeviceToScreenScale kViewportMinScale(0.0f);
 static const mozilla::LayoutDeviceToScreenScale kViewportMaxScale(10.0f);
 static const mozilla::CSSIntSize kViewportMinSize(200, 40);
 static const mozilla::CSSIntSize kViewportMaxSize(10000, 10000);
-static const int32_t  kViewportDefaultScreenWidth = 980;
 
 /**
  * Information retrieved from the <meta name="viewport"> tag. See
  * nsContentUtils::GetViewportInfo for more information on this functionality.
  */
 class MOZ_STACK_CLASS nsViewportInfo
 {
   public:
--- a/dom/base/test/chrome/window_nsITextInputProcessor.xul
+++ b/dom/base/test/chrome/window_nsITextInputProcessor.xul
@@ -10,17 +10,17 @@
         src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 <body  xmlns="http://www.w3.org/1999/xhtml">
 <p id="display">
 <input id="input" type="text"/><br/>
 <iframe id="iframe" width="300" height="150"
         src="data:text/html,&lt;textarea id='textarea' cols='20' rows='4'&gt;&lt;/textarea&gt;"></iframe><br/>
 </p>
 <div id="content" style="display: none">
-  
+
 </div>
 <pre id="test">
 </pre>
 </body>
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
@@ -2335,18 +2335,18 @@ function runKeyTests()
 
   // Printable key test:
   // Emulates pressing 'a' key.
   var keyA = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A });
 
   reset();
   var doDefaultKeydown = TIP.keydown(keyA);
 
-  ok(!doDefaultKeydown,
-     description + "TIP.keydown(keyA) should return false because the keypress event should be consumed by the input element");
+  is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED,
+     description + "TIP.keydown(keyA) should return 0x02 because the keypress event should be consumed by the input element");
   is(events.length, 2,
      description + "TIP.keydown(keyA) should cause keydown and keypress event");
   checkKeyAttrs("TIP.keydown(keyA)", events[0],
                 { type: "keydown",  key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0 });
   checkKeyAttrs("TIP.keydown(keyA)", events[1],
                 { type: "keypress", key: "a", code: "KeyA", keyCode: 0,                      charCode: "a".charCodeAt(0), defaultPrevented: true });
   is(input.value, "a",
      description + "input.value should be \"a\" which is inputted by TIP.keydown(keyA)");
@@ -2366,18 +2366,18 @@ function runKeyTests()
 
   // Non-printable key test:
   // Emulates pressing Enter key.
   var keyEnter = new KeyboardEvent("", { key: "Enter", code: "Enter" });
 
   reset();
   doDefaultKeydown = TIP.keydown(keyEnter);
 
-  ok(doDefaultKeydown,
-     description + "TIP.keydown(keyEnter) should return true");
+  is(doDefaultKeydown, 0,
+     description + "TIP.keydown(keyEnter) should return 0");
   is(events.length, 2,
      description + "TIP.keydown(keyEnter) should cause keydown and keypress event");
   checkKeyAttrs("TIP.keydown(keyEnter)", events[0],
                 { type: "keydown",  key: "Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN });
   checkKeyAttrs("TIP.keydown(keyEnter)", events[1],
                 { type: "keypress", key: "Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN });
   is(input.value, "a",
      description + "input.value should stay \"a\" which was inputted by TIP.keydown(keyA)");
@@ -2397,18 +2397,18 @@ function runKeyTests()
 
   // KEY_DEFAULT_PREVENTED should cause defaultPrevented = true and not cause keypress event
   var keyB = new KeyboardEvent("", { key: "b", code: "KeyB", keyCode: KeyboardEvent.DOM_VK_B });
 
   reset();
   doDefaultKeydown = TIP.keydown(keyB, TIP.KEY_DEFAULT_PREVENTED);
   doDefaultKeyup   = TIP.keyup(keyB, TIP.KEY_DEFAULT_PREVENTED);
 
-  ok(!doDefaultKeydown,
-     description + "TIP.keydown(keyB, TIP.KEY_DEFAULT_PREVENTED) should return false because it's marked as consumed at dispatching the event");
+  is(doDefaultKeydown, TIP.KEYDOWN_IS_CONSUMED,
+     description + "TIP.keydown(keyB, TIP.KEY_DEFAULT_PREVENTED) should return 0x01 because it's marked as consumed at dispatching the event");
   ok(!doDefaultKeyup,
      description + "TIP.keyup(keyB, TIP.KEY_DEFAULT_PREVENTED) should return false because it's marked as consumed at dispatching the event");
   is(events.length, 2,
      description + "TIP.keydown(keyB, TIP.KEY_DEFAULT_PREVENTED) and TIP.keyup(keyB, TIP.KEY_DEFAULT_PREVENTED) should cause keydown and keyup event");
   checkKeyAttrs("TIP.keydown(keyB, TIP.KEY_DEFAULT_PREVENTED) and TIP.keyup(keyB, TIP.KEY_DEFAULT_PREVENTED)", events[0],
                 { type: "keydown", key: "b", code: "KeyB", keyCode: KeyboardEvent.DOM_VK_B, defaultPrevented: true });
   checkKeyAttrs("TIP.keydown(keyB, TIP.KEY_DEFAULT_PREVENTED) and TIP.keyup(keyB, TIP.KEY_DEFAULT_PREVENTED)", events[1],
                 { type: "keyup",   key: "b", code: "KeyB", keyCode: KeyboardEvent.DOM_VK_B, defaultPrevented: true });
@@ -2418,17 +2418,17 @@ function runKeyTests()
   // Assume that KeyX causes inputting text "abc"
   input.value = "";
   var keyABC = new KeyboardEvent("", { key: "abc", code: "KeyX", keyCode: KeyboardEvent.DOM_VK_A });
 
   reset();
   doDefaultKeydown = TIP.keydown(keyABC);
   doDefaultKeyup   = TIP.keyup(keyABC);
 
-  ok(!doDefaultKeydown,
+  is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED,
      description + "TIP.keydown(keyABC) should return false because the keypress events should be consumed by the input element");
   ok(doDefaultKeyup,
      description + "TIP.keyup(keyABC) should return true");
   is(events.length, 5,
      description + "TIP.keydown(keyABC) and TIP.keyup(keyABC) should cause keydown, keypress, keypress, keypress and keyup event");
   checkKeyAttrs("TIP.keydown(keyABC) and TIP.keyup(keyABC)", events[0],
                 { type: "keydown",  key: "abc",           code: "KeyX", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0,                   defaultPrevented: false });
   checkKeyAttrs("TIP.keydown(keyABC) and TIP.keyup(keyABC)", events[1],
@@ -2445,18 +2445,18 @@ function runKeyTests()
   // If KEY_FORCE_PRINTABLE_KEY is specified, registered key names can be a printable key which inputs the specified value.
   input.value = "";
   var keyEnterPrintable = new KeyboardEvent("", { key: "Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN });
 
   reset();
   doDefaultKeydown = TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY);
   doDefaultKeyup   = TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY);
 
-  ok(!doDefaultKeydown,
-     description + "TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) should return false because the keypress events should be consumed by the input element");
+  is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED,
+     description + "TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) should return 0x02 because the keypress events should be consumed by the input element");
   ok(doDefaultKeyup,
      description + "TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) should return true");
   is(events.length, 7,
      description + "TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) and TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) should cause keydown, keypress, keypress, keypress, keypress, keypress and keyup event");
   checkKeyAttrs("TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) and TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY)", events[0],
                 { type: "keydown",  key: "Enter",           code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN, charCode: 0,                    defaultPrevented: false });
   checkKeyAttrs("TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) and TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY)", events[1],
                 { type: "keypress", key: "Enter".charAt(0), code: "Enter", keyCode: 0,                           charCode: "Enter".charCodeAt(0), defaultPrevented: true });
@@ -2475,18 +2475,18 @@ function runKeyTests()
 
   // modifiers should be ignored.
   var keyWithModifiers = new KeyboardEvent("", { key: "Escape", code: "Escape", shiftKey: true, ctrlKey: true, altKey: true, metaKey: true });
 
   reset();
   doDefaultKeydown = TIP.keydown(keyWithModifiers);
   doDefaultKeyup   = TIP.keyup(keyWithModifiers);
 
-  ok(doDefaultKeydown,
-     description + "TIP.keydown(keyWithModifiers) should return true");
+  is(doDefaultKeydown, 0,
+     description + "TIP.keydown(keyWithModifiers) should return 0");
   ok(doDefaultKeyup,
      description + "TIP.keyup(keyWithModifiers) should return true");
   is(events.length, 3,
      description + "TIP.keydown(keyWithModifiers) and TIP.keyup(keyWithModifiers) should cause keydown, keypress and keyup event");
   checkKeyAttrs("TIP.keydown(keyWithModifiers) and TIP.keyup(keyWithModifiers)", events[0],
                 { type: "keydown",  key: "Escape", code: "Escape", keyCode: KeyboardEvent.DOM_VK_ESCAPE });
   checkKeyAttrs("TIP.keydown(keyWithModifiers) and TIP.keyup(keyWithModifiers)", events[1],
                 { type: "keypress", key: "Escape", code: "Escape", keyCode: KeyboardEvent.DOM_VK_ESCAPE });
@@ -2497,18 +2497,18 @@ function runKeyTests()
 
   // Call preventDefault() at keydown
   input.value = "";
   reset();
   doPreventDefaults = [ "keydown" ];
   doDefaultKeydown = TIP.keydown(keyA);
   doDefaultKeyup   = TIP.keyup(keyA);
 
-  ok(!doDefaultKeydown,
-     description + "TIP.keydown(keyA) should return false because keydown event's preventDefault should be called");
+  is(doDefaultKeydown, TIP.KEYDOWN_IS_CONSUMED,
+     description + "TIP.keydown(keyA) should return 0x01 because keydown event's preventDefault should be called");
   ok(doDefaultKeyup,
      description + "TIP.keyup(keyA) should return true");
   is(events.length, 2,
      description + "TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keydown should cause keydown and keyup event");
   checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keydown", events[0],
                 { type: "keydown",  key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, defaultPrevented: true });
   checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keydown", events[1],
                 { type: "keyup",    key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, defaultPrevented: false });
@@ -2516,18 +2516,18 @@ function runKeyTests()
      description + "input.value shouldn't be modified by TIP.keyup(keyA) if the keydown event is consumed");
 
   // Call preventDefault() at keypress
   reset();
   doPreventDefaults = [ "keypress" ];
   doDefaultKeydown = TIP.keydown(keyA);
   doDefaultKeyup   = TIP.keyup(keyA);
 
-  ok(!doDefaultKeydown,
-     description + "TIP.keydown(keyA) should return false because keypress event's preventDefault should be called");
+  is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED,
+     description + "TIP.keydown(keyA) should return 0x02 because keypress event's preventDefault should be called");
   ok(doDefaultKeyup,
      description + "TIP.keyup(keyA) should return true");
   is(events.length, 3,
      description + "TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keypress should cause keydown, keypress and keyup event");
   checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keypress", events[0],
                 { type: "keydown",  key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0,                 defaultPrevented: false });
   checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keypress", events[1],
                 { type: "keypress", key: "a", code: "KeyA", keyCode: 0,                      charCode: "a".charCodeAt(0), defaultPrevented: true });
@@ -2538,18 +2538,18 @@ function runKeyTests()
 
   // Call preventDefault() at keyup
   input.value = "";
   reset();
   doPreventDefaults = [ "keyup" ];
   doDefaultKeydown = TIP.keydown(keyA);
   doDefaultKeyup   = TIP.keyup(keyA);
 
-  ok(!doDefaultKeydown,
-     description + "TIP.keydown(keyA) should return false because the key event should be consumed by the input element");
+  is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED,
+     description + "TIP.keydown(keyA) should return 0x02 because the key event should be consumed by the input element");
   ok(!doDefaultKeyup,
      description + "TIP.keyup(keyA) should return false because keyup event's preventDefault should be called");
   is(events.length, 3,
      description + "TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keyup should cause keydown, keypress and keyup event");
   checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keyup", events[0],
                 { type: "keydown",  key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0,                 defaultPrevented: false });
   checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keyup", events[1],
                 { type: "keypress", key: "a", code: "KeyA", keyCode: 0,                      charCode: "a".charCodeAt(0), defaultPrevented: true });
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -828,24 +828,26 @@ BrowserElementParent.prototype = {
     if (_options.referrer) {
       // newURI can throw on malformed URIs.
       try {
         referrer = Services.io.newURI(_options.referrer, null, null);
       }
       catch(e) {
         debug('Malformed referrer -- ' + e);
       }
+
+      // TODO Bug 1165466: use originAttributes from nsILoadContext.
+      let attrs = {appId: this._frameLoader.loadContext.appId,
+                   inBrowser: this._frameLoader.loadContext.isInBrowserElement};
       // This simply returns null if there is no principal available
       // for the requested uri. This is an acceptable fallback when
       // calling newChannelFromURI2.
-      principal = 
-        Services.scriptSecurityManager.getAppCodebasePrincipal(
-          referrer, 
-          this._frameLoader.loadContext.appId, 
-          this._frameLoader.loadContext.isInBrowserElement);
+      principal =
+        Services.scriptSecurityManager.createCodebasePrincipal(
+          referrer, attrs);
     }
 
     debug('Using principal? ' + !!principal);
 
     let channel = 
       Services.io.newChannelFromURI2(url,
                                      null,       // No document. 
                                      principal,  // Loading principal
--- a/dom/browser-element/mochitest/browserElement_Auth.js
+++ b/dom/browser-element/mochitest/browserElement_Auth.js
@@ -153,25 +153,27 @@ function testAuthJarNoInterfere(e) {
   var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
                .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
   var ioService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
                   .getService(SpecialPowers.Ci.nsIIOService);
   var uri = ioService.newURI("http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs", null, null);
 
   // Set a bunch of auth data that should not conflict with the correct auth data already
   // stored in the cache.
-  var principal = secMan.getAppCodebasePrincipal(uri, 1, false);
+  var attrs = {appId: 1};
+  var principal = secMan.createCodebasePrincipal(uri, attrs);
   authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm',
                           'tests/dom/browser-element/mochitest/file_http_401_response.sjs',
                           '', 'httpuser', 'wrongpass', false, principal);
-  principal = secMan.getAppCodebasePrincipal(uri, 1, true);
+  attrs = {appId: 1, inBrowser: true};
+  principal = secMan.createCodebasePrincipal(uri, attrs);
   authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm',
                           'tests/dom/browser-element/mochitest/file_http_401_response.sjs',
                           '', 'httpuser', 'wrongpass', false, principal);
-  principal = secMan.getAppCodebasePrincipal(uri, secMan.NO_APP_ID, false);
+  principal = secMan.createCodebasePrincipal(uri, {});
   authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm',
                           'tests/dom/browser-element/mochitest/file_http_401_response.sjs',
                           '', 'httpuser', 'wrongpass', false, principal);
 
   // Will authenticate with correct password, prompt should not be
   // called again.
   iframe.addEventListener("mozbrowserusernameandpasswordrequired", testFail);
   iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) {
@@ -191,17 +193,17 @@ function testAuthJarInterfere(e) {
     .getService(SpecialPowers.Ci.nsIHttpAuthManager);
   var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
                .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
   var ioService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
                   .getService(SpecialPowers.Ci.nsIIOService);
   var uri = ioService.newURI("http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs", null, null);
 
   // Set some auth data that should overwrite the successful stored details.
-  var principal = secMan.getAppCodebasePrincipal(uri, secMan.NO_APP_ID, true);
+  var principal = secMan.createCodebasePrincipal(uri, {inBrowser: true});
   authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm',
                           'tests/dom/browser-element/mochitest/file_http_401_response.sjs',
                           '', 'httpuser', 'wrongpass', false, principal);
 
   // Will authenticate with correct password, prompt should not be
   // called again.
   var gotusernamepasswordrequired = false;
   function onUserNameAndPasswordRequired() {
--- a/dom/cache/test/mochitest/test_chrome_constructor.html
+++ b/dom/cache/test/mochitest/test_chrome_constructor.html
@@ -15,17 +15,17 @@
 
   SpecialPowers.pushPrefEnv({
     "set": [["dom.caches.enabled", true],
             ["dom.caches.testing.enabled", true]],
   }, function() {
     // attach to a different origin's CacheStorage
     var url = 'http://example.com/';
     var uri = Services.io.newURI(url, null, null);
-    var principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
+    var principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
     var storage = new CacheStorage('content', principal);
 
     // verify we can use the other origin's CacheStorage as normal
     var req = new Request('http://example.com/index.html');
     var res = new Response('hello world');
     var cache;
     storage.open('foo').then(function(c) {
       cache = c;
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1723,72 +1723,91 @@ CanvasRenderingContext2D::Scale(double x
 {
   TransformWillUpdate();
   if (!IsTargetValid()) {
     error.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   Matrix newMatrix = mTarget->GetTransform();
-  mTarget->SetTransform(newMatrix.PreScale(x, y));
+  newMatrix.PreScale(x, y);
+  if (!newMatrix.IsFinite()) {
+    return;
+  }
+  mTarget->SetTransform(newMatrix);
 }
 
 void
 CanvasRenderingContext2D::Rotate(double angle, ErrorResult& error)
 {
   TransformWillUpdate();
   if (!IsTargetValid()) {
     error.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  Matrix rotation = Matrix::Rotation(angle);
-  mTarget->SetTransform(rotation * mTarget->GetTransform());
+  Matrix newMatrix = Matrix::Rotation(angle) * mTarget->GetTransform();
+  if (!newMatrix.IsFinite()) {
+    return;
+  }
+  mTarget->SetTransform(newMatrix);
 }
 
 void
 CanvasRenderingContext2D::Translate(double x, double y, ErrorResult& error)
 {
   TransformWillUpdate();
   if (!IsTargetValid()) {
     error.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  mTarget->SetTransform(Matrix(mTarget->GetTransform()).PreTranslate(x, y));
+  Matrix newMatrix = mTarget->GetTransform();
+  newMatrix.PreTranslate(x, y);
+  if (!newMatrix.IsFinite()) {
+    return;
+  }
+  mTarget->SetTransform(newMatrix);
 }
 
 void
 CanvasRenderingContext2D::Transform(double m11, double m12, double m21,
                                     double m22, double dx, double dy,
                                     ErrorResult& error)
 {
   TransformWillUpdate();
   if (!IsTargetValid()) {
     error.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  Matrix matrix(m11, m12, m21, m22, dx, dy);
-  mTarget->SetTransform(matrix * mTarget->GetTransform());
+  Matrix newMatrix(m11, m12, m21, m22, dx, dy);
+  newMatrix *= mTarget->GetTransform();
+  if (!newMatrix.IsFinite()) {
+    return;
+  }
+  mTarget->SetTransform(newMatrix);
 }
 
 void
 CanvasRenderingContext2D::SetTransform(double m11, double m12,
                                        double m21, double m22,
                                        double dx, double dy,
                                        ErrorResult& error)
 {
   TransformWillUpdate();
   if (!IsTargetValid()) {
     error.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   Matrix matrix(m11, m12, m21, m22, dx, dy);
+  if (!matrix.IsFinite()) {
+    return;
+  }
   mTarget->SetTransform(matrix);
 }
 
 void
 CanvasRenderingContext2D::ResetTransform(ErrorResult& error)
 {
   SetTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, error);
 }
--- a/dom/canvas/WebGLShaderValidator.cpp
+++ b/dom/canvas/WebGLShaderValidator.cpp
@@ -204,59 +204,75 @@ static bool
 StartsWith(const std::string& haystack, const char (&needle)[N])
 {
     return haystack.compare(0, N - 1, needle) == 0;
 }
 
 bool
 ShaderValidator::CanLinkTo(const ShaderValidator* prev, nsCString* const out_log) const
 {
+    if (!prev) {
+        nsPrintfCString error("Passed in NULL prev ShaderValidator.");
+        *out_log = error;
+        return false;
+    }
+
     {
-        const std::vector<sh::Uniform>& vertList = *ShGetUniforms(prev->mHandle);
-        const std::vector<sh::Uniform>& fragList = *ShGetUniforms(mHandle);
+        const std::vector<sh::Uniform>* vertPtr = ShGetUniforms(prev->mHandle);
+        const std::vector<sh::Uniform>* fragPtr = ShGetUniforms(mHandle);
+        if (!vertPtr || !fragPtr) {
+            nsPrintfCString error("Could not create uniform list.");
+            *out_log = error;
+            return false;
+        }
 
-        for (auto itrFrag = fragList.begin(); itrFrag != fragList.end(); ++itrFrag) {
-            for (auto itrVert = vertList.begin(); itrVert != vertList.end(); ++itrVert) {
+        for (auto itrFrag = fragPtr->begin(); itrFrag != fragPtr->end(); ++itrFrag) {
+            for (auto itrVert = vertPtr->begin(); itrVert != vertPtr->end(); ++itrVert) {
                 if (itrVert->name != itrFrag->name)
                     continue;
 
                 if (!itrVert->isSameUniformAtLinkTime(*itrFrag)) {
                     nsPrintfCString error("Uniform `%s`is not linkable between"
                                           " attached shaders.",
                                           itrFrag->name.c_str());
                     *out_log = error;
                     return false;
                 }
 
                 break;
             }
         }
     }
     {
-        const std::vector<sh::Varying>& vertList = *ShGetVaryings(prev->mHandle);
-        const std::vector<sh::Varying>& fragList = *ShGetVaryings(mHandle);
+        const std::vector<sh::Varying>* vertPtr = ShGetVaryings(prev->mHandle);
+        const std::vector<sh::Varying>* fragPtr = ShGetVaryings(mHandle);
+        if (!vertPtr || !fragPtr) {
+            nsPrintfCString error("Could not create varying list.");
+            *out_log = error;
+            return false;
+        }
 
         nsTArray<ShVariableInfo> staticUseVaryingList;
 
-        for (auto itrFrag = fragList.begin(); itrFrag != fragList.end(); ++itrFrag) {
+        for (auto itrFrag = fragPtr->begin(); itrFrag != fragPtr->end(); ++itrFrag) {
             const ShVariableInfo varInfo = { itrFrag->type,
                                              (int)itrFrag->elementCount() };
 
             static const char prefix[] = "gl_";
             if (StartsWith(itrFrag->name, prefix)) {
                 if (itrFrag->staticUse)
                     staticUseVaryingList.AppendElement(varInfo);
 
                 continue;
             }
 
             bool definedInVertShader = false;
             bool staticVertUse = false;
 
-            for (auto itrVert = vertList.begin(); itrVert != vertList.end(); ++itrVert) {
+            for (auto itrVert = vertPtr->begin(); itrVert != vertPtr->end(); ++itrVert) {
                 if (itrVert->name != itrFrag->name)
                     continue;
 
                 if (!itrVert->isSameVaryingAtLinkTime(*itrFrag)) {
                     nsPrintfCString error("Varying `%s`is not linkable between"
                                           " attached shaders.",
                                           itrFrag->name.c_str());
                     *out_log = error;
new file mode 100644
--- /dev/null
+++ b/dom/canvas/crashtests/1190705.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+    var canvas = document.createElement('canvas');
+    var ctx = canvas.getContext('2d');
+    ctx.rotate(1e308);
+    ctx.fillText("A", 1, 1);
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
--- a/dom/canvas/crashtests/crashtests.list
+++ b/dom/canvas/crashtests/crashtests.list
@@ -18,8 +18,10 @@ skip-if(Android||B2G) skip-if(gtkWidget&
 load 794463-1.html
 load 802926-1.html
 load 896047-1.html
 load 896047-2.html
 load 916128-1.html
 load 934939-1.html
 load 1099143-1.html
 load 1183363.html
+load 1190705.html
+
--- a/dom/datastore/DataStoreService.cpp
+++ b/dom/datastore/DataStoreService.cpp
@@ -9,16 +9,17 @@
 #include "DataStoreCallbacks.h"
 #include "DataStoreDB.h"
 #include "DataStoreRevision.h"
 #include "mozilla/dom/DataStore.h"
 #include "mozilla/dom/DataStoreBinding.h"
 #include "mozilla/dom/DataStoreImplBinding.h"
 #include "nsIDataStore.h"
 
+#include "mozilla/BasePrincipal.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/indexedDB/IDBCursor.h"
 #include "mozilla/dom/indexedDB/IDBObjectStore.h"
@@ -51,16 +52,19 @@
 #include "nsXULAppAPI.h"
 
 #define ASSERT_PARENT_PROCESS()                                             \
   MOZ_ASSERT(XRE_IsParentProcess());                                        \
   if (NS_WARN_IF(!XRE_IsParentProcess())) {                                 \
     return NS_ERROR_FAILURE;                                                \
   }
 
+using mozilla::BasePrincipal;
+using mozilla::OriginAttributes;
+
 namespace mozilla {
 namespace dom {
 
 using namespace indexedDB;
 
 // This class contains all the information about a DataStore.
 class DataStoreInfo
 {
@@ -208,27 +212,20 @@ ResetPermission(uint32_t aAppId, const n
 
   nsCOMPtr<nsIURI> uri;
   rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aOriginURL), nullptr, nullptr,
                          getter_AddRefs(uri));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
-  if (!ssm) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsIPrincipal> principal;
-  rv = ssm->GetAppCodebasePrincipal(uri, aAppId, false,
-                                    getter_AddRefs(principal));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
+  OriginAttributes attrs(aAppId, false);
+  nsCOMPtr<nsIPrincipal> principal =
+    BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+  NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIPermissionManager> pm =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   if (!pm) {
     return NS_ERROR_FAILURE;
   }
 
   nsCString basePermission;
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -18514,22 +18514,16 @@ FactoryOp::CheckAtLeastOneAppHasPermissi
       return false;
     }
 
     nsCOMPtr<nsIIOService> ioService = do_GetIOService();
     if (NS_WARN_IF(!ioService)) {
       return false;
     }
 
-    nsCOMPtr<nsIScriptSecurityManager> secMan =
-      do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
-    if (NS_WARN_IF(!secMan)) {
-      return false;
-    }
-
     nsCOMPtr<nsIPermissionManager> permMan =
       mozilla::services::GetPermissionManager();
     if (NS_WARN_IF(!permMan)) {
       return false;
     }
 
     const nsPromiseFlatCString permissionString =
       PromiseFlatCString(aPermissionString);
@@ -18543,34 +18537,19 @@ FactoryOp::CheckAtLeastOneAppHasPermissi
                  appId != nsIScriptSecurityManager::NO_APP_ID);
 
       nsCOMPtr<mozIApplication> app;
       nsresult rv = appsService->GetAppByLocalId(appId, getter_AddRefs(app));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return false;
       }
 
-      nsString origin;
-      rv = app->GetOrigin(origin);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return false;
-      }
-
-      nsCOMPtr<nsIURI> uri;
-      rv = NS_NewURI(getter_AddRefs(uri), origin, nullptr, nullptr, ioService);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return false;
-      }
-
       nsCOMPtr<nsIPrincipal> principal;
-      rv = secMan->GetAppCodebasePrincipal(uri, appId, false,
-                                           getter_AddRefs(principal));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return false;
-      }
+      app->GetPrincipal(getter_AddRefs(principal));
+      NS_ENSURE_TRUE(principal, false);
 
       uint32_t permission;
       rv = permMan->TestExactPermissionFromPrincipal(principal,
                                                      permissionString.get(),
                                                      &permission);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return false;
       }
--- a/dom/indexedDB/test/head.js
+++ b/dom/indexedDB/test/head.js
@@ -111,45 +111,45 @@ function dispatchEvent(eventName)
 
 function setPermission(url, permission)
 {
   const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
 
   let uri = Components.classes["@mozilla.org/network/io-service;1"]
                       .getService(Components.interfaces.nsIIOService)
                       .newURI(url, null, null);
-  let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
-                    .getService(Ci.nsIScriptSecurityManager)
-                    .getNoAppCodebasePrincipal(uri);
+  let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
+                      .getService(Ci.nsIScriptSecurityManager);
+  let principal = ssm.createCodebasePrincipal(uri, {});
 
   Components.classes["@mozilla.org/permissionmanager;1"]
             .getService(nsIPermissionManager)
             .addFromPrincipal(principal, permission,
                               nsIPermissionManager.ALLOW_ACTION);
 }
 
 function removePermission(url, permission)
 {
   let uri = Components.classes["@mozilla.org/network/io-service;1"]
                       .getService(Components.interfaces.nsIIOService)
                       .newURI(url, null, null);
-  let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
-                    .getService(Ci.nsIScriptSecurityManager)
-                    .getNoAppCodebasePrincipal(uri);
+  let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
+                      .getService(Ci.nsIScriptSecurityManager);
+  let principal = ssm.createCodebasePrincipal(uri, {});
 
   Components.classes["@mozilla.org/permissionmanager;1"]
             .getService(Components.interfaces.nsIPermissionManager)
             .removeFromPrincipal(principal, permission);
 }
 
 function getPermission(url, permission)
 {
   let uri = Components.classes["@mozilla.org/network/io-service;1"]
                       .getService(Components.interfaces.nsIIOService)
                       .newURI(url, null, null);
-  let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
-                    .getService(Ci.nsIScriptSecurityManager)
-                    .getNoAppCodebasePrincipal(uri);
+  let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
+                      .getService(Ci.nsIScriptSecurityManager);
+  let principal = ssm.createCodebasePrincipal(uri, {});
 
   return Components.classes["@mozilla.org/permissionmanager;1"]
                    .getService(Components.interfaces.nsIPermissionManager)
                    .testPermissionFromPrincipal(principal, permission);
 }
--- a/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js
+++ b/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js
@@ -85,23 +85,20 @@ function testSteps()
 
   let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
                          .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
 
   function openDatabase(params) {
     let request;
     if ("url" in params) {
       let uri = ios.newURI(params.url, null, null);
-      let principal;
-      if ("appId" in params) {
-        principal = ssm.getAppCodebasePrincipal(uri, params.appId,
-                                                params.inMozBrowser);
-      } else {
-        principal = ssm.getNoAppCodebasePrincipal(uri);
-      }
+      let principal =
+        ssm.createCodebasePrincipal(uri,
+                                    {appId: params.appId || ssm.NO_APPID,
+                                     inBrowser: params.inMozBrowser});
       if ("dbVersion" in params) {
         request = indexedDB.openForPrincipal(principal, params.dbName,
                                              params.dbVersion);
       } else {
         request = indexedDB.openForPrincipal(principal, params.dbName,
                                              params.dbOptions);
       }
     } else {
--- a/dom/indexedDB/test/unit/test_idle_maintenance.js
+++ b/dom/indexedDB/test/unit/test_idle_maintenance.js
@@ -5,19 +5,19 @@
 
 var testGenerator = testSteps();
 
 function testSteps()
 {
   let uri = Cc["@mozilla.org/network/io-service;1"].
             getService(Ci.nsIIOService).
             newURI("https://www.example.com", null, null);
-  let principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
-                  getService(Ci.nsIScriptSecurityManager).
-                  getNoAppCodebasePrincipal(uri);
+  let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
+              .getService(Ci.nsIScriptSecurityManager);
+  let principal = ssm.createCodebasePrincipal(uri, {});
 
   info("Setting permissions");
 
   let permMgr =
     Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
   permMgr.add(uri, "indexedDB", Ci.nsIPermissionManager.ALLOW_ACTION);
 
   info("Setting idle preferences to prevent real 'idle-daily' notification");
--- a/dom/indexedDB/test/unit/test_metadataRestore.js
+++ b/dom/indexedDB/test/unit/test_metadataRestore.js
@@ -62,17 +62,17 @@ function testSteps()
 
   let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
                          .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
 
   function openDatabase(params) {
     let request;
     if ("url" in params) {
       let uri = ios.newURI(params.url, null, null);
-      let principal = ssm.getNoAppCodebasePrincipal(uri);
+      let principal = ssm.createCodebasePrincipal(uri, {});
       request = indexedDB.openForPrincipal(principal, params.dbName,
                                            params.dbOptions);
     } else {
       request = indexedDB.open(params.dbName, params.dbOptions);
     }
     return request;
   }
 
--- a/dom/indexedDB/test/unit/test_open_for_principal.js
+++ b/dom/indexedDB/test/unit/test_open_for_principal.js
@@ -43,19 +43,19 @@ function testSteps()
   request.onsuccess = grabEventAndContinueHandler;
   event = yield undefined;
 
   is(event.target.result, data.key, "Got correct key");
 
   let uri = Components.classes["@mozilla.org/network/io-service;1"]
                       .getService(Components.interfaces.nsIIOService)
                       .newURI("http://appdata.example.com", null, null);
-  let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
-                     .getService(Components.interfaces.nsIScriptSecurityManager)
-                     .getNoAppCodebasePrincipal(uri);
+  let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
+                      .getService(Components.interfaces.nsIScriptSecurityManager);
+  let principal = ssm.createCodebasePrincipal(uri, {});
 
   request = indexedDB.openForPrincipal(principal, name, 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   request.onsuccess = grabEventAndContinueHandler;
   event = yield undefined;
 
   is(event.type, "upgradeneeded", "Got correct event type");
--- a/dom/indexedDB/test/unit/test_temporary_storage.js
+++ b/dom/indexedDB/test/unit/test_temporary_storage.js
@@ -29,19 +29,19 @@ function testSteps()
   function getSpec(index) {
     return "http://foo" + index + ".com";
   }
 
   function getPrincipal(url) {
     let uri = Cc["@mozilla.org/network/io-service;1"]
                 .getService(Ci.nsIIOService)
                 .newURI(url, null, null);
-    return Cc["@mozilla.org/scriptsecuritymanager;1"]
-             .getService(Ci.nsIScriptSecurityManager)
-             .getNoAppCodebasePrincipal(uri);
+    let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
+                .getService(Ci.nsIScriptSecurityManager);
+    return ssm.createCodebasePrincipal(uri, {});
   }
 
   for (let temporary of [true, false]) {
     info("Testing '" + (temporary ? "temporary" : "default") + "' storage");
 
     setLimit(tempStorageLimitKB);
 
     clearAllDatabases(continueToNextStepSync);
--- a/dom/inputmethod/MozKeyboard.js
+++ b/dom/inputmethod/MozKeyboard.js
@@ -376,17 +376,17 @@ MozInputMethod.prototype = {
       cpmmSendAsyncMessageWithKbID(this, 'Keyboard:Unregister', {});
       if (this._inputcontext) {
         this.setInputContext(null);
       }
     }
   },
 
   addInput: function(inputId, inputManifest) {
-    return this._sendPromise(function(resolverId) {
+    return this.createPromiseWithId(function(resolverId) {
       let appId = this._window.document.nodePrincipal.appId;
 
       cpmm.sendAsyncMessage('InputRegistry:Add', {
         requestId: resolverId,
         inputId: inputId,
         inputManifest: inputManifest,
         appId: appId
       });
@@ -568,17 +568,17 @@ MozInputContext.prototype = {
 
     // Update context first before resolving promise to avoid race condition
     if (json.selectioninfo) {
       this.updateSelectionContext(json.selectioninfo, true);
     }
 
     switch (msg.name) {
       case "Keyboard:SendKey:Result:OK":
-        resolver.resolve();
+        resolver.resolve(true);
         break;
       case "Keyboard:SendKey:Result:Error":
         resolver.reject(json.error);
         break;
       case "Keyboard:GetText:Result:OK":
         resolver.resolve(json.text);
         break;
       case "Keyboard:GetText:Result:Error":
@@ -591,17 +591,17 @@ MozInputContext.prototype = {
         break;
       case "Keyboard:SequenceError":
         // Occurs when a new element got focus, but the inputContext was
         // not invalidated yet...
         resolver.reject("InputContext has expired");
         break;
       case "Keyboard:SetComposition:Result:OK": // Fall through.
       case "Keyboard:EndComposition:Result:OK":
-        resolver.resolve();
+        resolver.resolve(true);
         break;
       default:
         dump("Could not find a handler for " + msg.name);
         resolver.reject();
         break;
     }
   },
 
@@ -733,60 +733,136 @@ MozInputContext.prototype = {
       });
     });
   },
 
   deleteSurroundingText: function ic_deleteSurrText(offset, length) {
     return this.replaceSurroundingText(null, offset, length);
   },
 
-  sendKey: function ic_sendKey(keyCode, charCode, modifiers, repeat) {
-    let self = this;
-    return this._sendPromise(function(resolverId) {
-      cpmmSendAsyncMessageWithKbID(self, 'Keyboard:SendKey', {
-        contextId: self._contextId,
+  sendKey: function ic_sendKey(dictOrKeyCode, charCode, modifiers, repeat) {
+    if (typeof dictOrKeyCode === 'number') {
+      // XXX: modifiers are ignored in this API method.
+
+      return this._sendPromise((resolverId) => {
+        cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', {
+          contextId: this._contextId,
+          requestId: resolverId,
+          method: 'sendKey',
+          keyCode: dictOrKeyCode,
+          charCode: charCode,
+          repeat: repeat
+        });
+      });
+    } else if (typeof dictOrKeyCode === 'object') {
+      return this._sendPromise((resolverId) => {
+        cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', {
+          contextId: this._contextId,
+          requestId: resolverId,
+          method: 'sendKey',
+          keyboardEventDict: this._getkeyboardEventDict(dictOrKeyCode)
+        });
+      });
+    } else {
+      // XXX: Should not reach here; implies WebIDL binding error.
+      throw new TypeError('Unknown argument passed.');
+    }
+  },
+
+  keydown: function ic_keydown(dict) {
+    return this._sendPromise((resolverId) => {
+      cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', {
+        contextId: this._contextId,
+         requestId: resolverId,
+        method: 'keydown',
+        keyboardEventDict: this._getkeyboardEventDict(dict)
+       });
+     });
+   },
+
+  keyup: function ic_keyup(dict) {
+    return this._sendPromise((resolverId) => {
+      cpmmSendAsyncMessageWithKbID(this, 'Keyboard:SendKey', {
+        contextId: this._contextId,
         requestId: resolverId,
-        keyCode: keyCode,
-        charCode: charCode,
-        modifiers: modifiers,
-        repeat: repeat
+        method: 'keyup',
+        keyboardEventDict: this._getkeyboardEventDict(dict)
       });
     });
   },
 
-  setComposition: function ic_setComposition(text, cursor, clauses) {
+  setComposition: function ic_setComposition(text, cursor, clauses, dict) {
     let self = this;
-    return this._sendPromise(function(resolverId) {
+    return this._sendPromise((resolverId) => {
       cpmmSendAsyncMessageWithKbID(self, 'Keyboard:SetComposition', {
         contextId: self._contextId,
         requestId: resolverId,
         text: text,
         cursor: (typeof cursor !== 'undefined') ? cursor : text.length,
-        clauses: clauses || null
+        clauses: clauses || null,
+        keyboardEventDict: this._getkeyboardEventDict(dict)
       });
     });
   },
 
-  endComposition: function ic_endComposition(text) {
+  endComposition: function ic_endComposition(text, dict) {
     let self = this;
-    return this._sendPromise(function(resolverId) {
+    return this._sendPromise((resolverId) => {
       cpmmSendAsyncMessageWithKbID(self, 'Keyboard:EndComposition', {
         contextId: self._contextId,
         requestId: resolverId,
-        text: text || ''
+        text: text || '',
+        keyboardEventDict: this._getkeyboardEventDict(dict)
       });
     });
   },
 
   _sendPromise: function(callback) {
     let self = this;
     return this._ipcHelper.createPromiseWithId(function(aResolverId) {
       if (!WindowMap.isActive(self._window)) {
         self._ipcHelper.removePromiseResolver(aResolverId);
         reject('Input method is not active.');
         return;
       }
       callback(aResolverId);
     });
+  },
+
+  // Take a MozInputMethodKeyboardEventDict dict, creates a keyboardEventDict
+  // object that can be sent to forms.js
+  _getkeyboardEventDict: function(dict) {
+    if (typeof dict !== 'object' || !dict.key) {
+      return;
+    }
+
+    var keyboardEventDict = {
+      key: dict.key,
+      code: dict.code,
+      repeat: dict.repeat,
+      flags: 0
+    };
+
+    if (dict.printable) {
+      keyboardEventDict.flags |=
+        Ci.nsITextInputProcessor.KEY_FORCE_PRINTABLE_KEY;
+    }
+
+    if (/^[a-zA-Z0-9]$/.test(dict.key)) {
+      // keyCode must follow the key value in this range;
+      // disregard the keyCode from content.
+      keyboardEventDict.keyCode = dict.key.toUpperCase().charCodeAt(0);
+    } else if (typeof dict.keyCode === 'number') {
+      // Allow keyCode to be specified for other key values.
+      keyboardEventDict.keyCode = dict.keyCode;
+
+      // Allow keyCode to be explicitly set to zero.
+      if (dict.keyCode === 0) {
+        keyboardEventDict.flags |=
+          Ci.nsITextInputProcessor.KEY_KEEP_KEYCODE_ZERO;
+      }
+    }
+
+    return keyboardEventDict;
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MozInputMethod]);
--- a/dom/inputmethod/forms.js
+++ b/dom/inputmethod/forms.js
@@ -6,27 +6,68 @@
 
 "use strict";
 
 dump("###################################### forms.js loaded\n");
 
 let Ci = Components.interfaces;
 let Cc = Components.classes;
 let Cu = Components.utils;
+let Cr = Components.results;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 XPCOMUtils.defineLazyServiceGetter(Services, "fm",
                                    "@mozilla.org/focus-manager;1",
                                    "nsIFocusManager");
 
-XPCOMUtils.defineLazyGetter(this, "domWindowUtils", function () {
-  return content.QueryInterface(Ci.nsIInterfaceRequestor)
-                .getInterface(Ci.nsIDOMWindowUtils);
-});
+/*
+ * A WeakMap to map window to objects keeping it's TextInputProcessor instance.
+ */
+let WindowMap = {
+  // WeakMap of <window, object> pairs.
+  _map: null,
+
+  /*
+   * Set the object associated to the window and return it.
+   */
+  _getObjForWin: function(win) {
+    if (!this._map) {
+      this._map = new WeakMap();
+    }
+    if (this._map.has(win)) {
+      return this._map.get(win);
+    } else {
+      let obj = {
+        tip: null
+      };
+      this._map.set(win, obj);
+
+      return obj;
+    }
+  },
+
+  getTextInputProcessor: function(win) {
+    if (!win) {
+      return;
+    }
+    let obj = this._getObjForWin(win);
+    let tip = obj.tip
+
+    if (!tip) {
+      tip = obj.tip = Cc["@mozilla.org/text-input-processor;1"]
+        .createInstance(Ci.nsITextInputProcessor);
+    }
+
+    if (!tip.beginInputTransaction(win, textInputProcessorCallback)) {
+      tip = obj.tip = null;
+    }
+    return tip;
+  }
+};
 
 const RESIZE_SCROLL_DELAY = 20;
 // In content editable node, when there are hidden elements such as <br>, it
 // may need more than one (usually less than 3 times) move/extend operations
 // to change the selection range. If we cannot change the selection range
 // with more than 20 opertations, we are likely being blocked and cannot change
 // the selection range any more.
 const MAX_BLOCKED_COUNT = 20;
@@ -36,16 +77,161 @@ let HTMLHtmlElement = Ci.nsIDOMHTMLHtmlE
 let HTMLBodyElement = Ci.nsIDOMHTMLBodyElement;
 let HTMLIFrameElement = Ci.nsIDOMHTMLIFrameElement;
 let HTMLInputElement = Ci.nsIDOMHTMLInputElement;
 let HTMLTextAreaElement = Ci.nsIDOMHTMLTextAreaElement;
 let HTMLSelectElement = Ci.nsIDOMHTMLSelectElement;
 let HTMLOptGroupElement = Ci.nsIDOMHTMLOptGroupElement;
 let HTMLOptionElement = Ci.nsIDOMHTMLOptionElement;
 
+function guessKeyNameFromKeyCode(KeyboardEvent, aKeyCode) {
+  switch (aKeyCode) {
+    case KeyboardEvent.DOM_VK_CANCEL:
+      return "Cancel";
+    case KeyboardEvent.DOM_VK_HELP:
+      return "Help";
+    case KeyboardEvent.DOM_VK_BACK_SPACE:
+      return "Backspace";
+    case KeyboardEvent.DOM_VK_TAB:
+      return "Tab";
+    case KeyboardEvent.DOM_VK_CLEAR:
+      return "Clear";
+    case KeyboardEvent.DOM_VK_RETURN:
+      return "Enter";
+    case KeyboardEvent.DOM_VK_SHIFT:
+      return "Shift";
+    case KeyboardEvent.DOM_VK_CONTROL:
+      return "Control";
+    case KeyboardEvent.DOM_VK_ALT:
+      return "Alt";
+    case KeyboardEvent.DOM_VK_PAUSE:
+      return "Pause";
+    case KeyboardEvent.DOM_VK_EISU:
+      return "Eisu";
+    case KeyboardEvent.DOM_VK_ESCAPE:
+      return "Escape";
+    case KeyboardEvent.DOM_VK_CONVERT:
+      return "Convert";
+    case KeyboardEvent.DOM_VK_NONCONVERT:
+      return "NonConvert";
+    case KeyboardEvent.DOM_VK_ACCEPT:
+      return "Accept";
+    case KeyboardEvent.DOM_VK_MODECHANGE:
+      return "ModeChange";
+    case KeyboardEvent.DOM_VK_PAGE_UP:
+      return "PageUp";
+    case KeyboardEvent.DOM_VK_PAGE_DOWN:
+      return "PageDown";
+    case KeyboardEvent.DOM_VK_END:
+      return "End";
+    case KeyboardEvent.DOM_VK_HOME:
+      return "Home";
+    case KeyboardEvent.DOM_VK_LEFT:
+      return "ArrowLeft";
+    case KeyboardEvent.DOM_VK_UP:
+      return "ArrowUp";
+    case KeyboardEvent.DOM_VK_RIGHT:
+      return "ArrowRight";
+    case KeyboardEvent.DOM_VK_DOWN:
+      return "ArrowDown";
+    case KeyboardEvent.DOM_VK_SELECT:
+      return "Select";
+    case KeyboardEvent.DOM_VK_PRINT:
+      return "Print";
+    case KeyboardEvent.DOM_VK_EXECUTE:
+      return "Execute";
+    case KeyboardEvent.DOM_VK_PRINTSCREEN:
+      return "PrintScreen";
+    case KeyboardEvent.DOM_VK_INSERT:
+      return "Insert";
+    case KeyboardEvent.DOM_VK_DELETE:
+      return "Delete";
+    case KeyboardEvent.DOM_VK_WIN:
+      return "OS";
+    case KeyboardEvent.DOM_VK_CONTEXT_MENU:
+      return "ContextMenu";
+    case KeyboardEvent.DOM_VK_SLEEP:
+      return "Standby";
+    case KeyboardEvent.DOM_VK_F1:
+      return "F1";
+    case KeyboardEvent.DOM_VK_F2:
+      return "F2";
+    case KeyboardEvent.DOM_VK_F3:
+      return "F3";
+    case KeyboardEvent.DOM_VK_F4:
+      return "F4";
+    case KeyboardEvent.DOM_VK_F5:
+      return "F5";
+    case KeyboardEvent.DOM_VK_F6:
+      return "F6";
+    case KeyboardEvent.DOM_VK_F7:
+      return "F7";
+    case KeyboardEvent.DOM_VK_F8:
+      return "F8";
+    case KeyboardEvent.DOM_VK_F9:
+      return "F9";
+    case KeyboardEvent.DOM_VK_F10:
+      return "F10";
+    case KeyboardEvent.DOM_VK_F11:
+      return "F11";
+    case KeyboardEvent.DOM_VK_F12:
+      return "F12";
+    case KeyboardEvent.DOM_VK_F13:
+      return "F13";
+    case KeyboardEvent.DOM_VK_F14:
+      return "F14";
+    case KeyboardEvent.DOM_VK_F15:
+      return "F15";
+    case KeyboardEvent.DOM_VK_F16:
+      return "F16";
+    case KeyboardEvent.DOM_VK_F17:
+      return "F17";
+    case KeyboardEvent.DOM_VK_F18:
+      return "F18";
+    case KeyboardEvent.DOM_VK_F19:
+      return "F19";
+    case KeyboardEvent.DOM_VK_F20:
+      return "F20";
+    case KeyboardEvent.DOM_VK_F21:
+      return "F21";
+    case KeyboardEvent.DOM_VK_F22:
+      return "F22";
+    case KeyboardEvent.DOM_VK_F23:
+      return "F23";
+    case KeyboardEvent.DOM_VK_F24:
+      return "F24";
+    case KeyboardEvent.DOM_VK_NUM_LOCK:
+      return "NumLock";
+    case KeyboardEvent.DOM_VK_SCROLL_LOCK:
+      return "ScrollLock";
+    case KeyboardEvent.DOM_VK_VOLUME_MUTE:
+      return "VolumeMute";
+    case KeyboardEvent.DOM_VK_VOLUME_DOWN:
+      return "VolumeDown";
+    case KeyboardEvent.DOM_VK_VOLUME_UP:
+      return "VolumeUp";
+    case KeyboardEvent.DOM_VK_META:
+      return "Meta";
+    case KeyboardEvent.DOM_VK_ALTGR:
+      return "AltGraph";
+    case KeyboardEvent.DOM_VK_ATTN:
+      return "Attn";
+    case KeyboardEvent.DOM_VK_CRSEL:
+      return "CrSel";
+    case KeyboardEvent.DOM_VK_EXSEL:
+      return "ExSel";
+    case KeyboardEvent.DOM_VK_EREOF:
+      return "EraseEof";
+    case KeyboardEvent.DOM_VK_PLAY:
+      return "Play";
+    default:
+      return "Unidentified";
+  }
+}
+
 let FormVisibility = {
   /**
    * Searches upwards in the DOM for an element that has been scrolled.
    *
    * @param {HTMLElement} node element to start search at.
    * @return {Window|HTMLElement|Null} null when none are found window/element otherwise.
    */
   findScrolled: function fv_findScrolled(node) {
@@ -179,16 +365,51 @@ let FormVisibility = {
       (parent !== parent.parent) &&
       (parent = parent.parent)
     );
 
     return visible;
   }
 };
 
+// This object implements nsITextInputProcessorCallback
+let textInputProcessorCallback = {
+  onNotify: function(aTextInputProcessor, aNotification) {
+    try {
+      switch (aNotification.type) {
+        case "request-to-commit":
+          // TODO: Send a notification through asyncMessage to the keyboard here.
+          aTextInputProcessor.commitComposition();
+
+          break;
+        case "request-to-cancel":
+          // TODO: Send a notification through asyncMessage to the keyboard here.
+          aTextInputProcessor.cancelComposition();
+
+          break;
+
+        case "notify-detached":
+          // TODO: Send a notification through asyncMessage to the keyboard here.
+          break;
+
+        // TODO: Manage _focusedElement for text input from here instead.
+        //       (except for <select> which will be need to handled elsewhere)
+        case "notify-focus":
+          break;
+
+        case "notify-blur":
+          break;
+      }
+    } catch (e) {
+      return false;
+    }
+    return true;
+  }
+};
+
 let FormAssistant = {
   init: function fa_init() {
     addEventListener("focus", this, true, false);
     addEventListener("blur", this, true, false);
     addEventListener("resize", this, true, false);
     // We should not blur the fucus if the submit event is cancelled,
     // therefore we are binding our event listener in the bubbling phase here.
     addEventListener("submit", this, false, false);
@@ -427,25 +648,25 @@ let FormAssistant = {
             if (this.focusedElement && !FormVisibility.isVisible(this.focusedElement)) {
               scrollSelectionOrElementIntoView(this.focusedElement);
             }
           }.bind(this), RESIZE_SCROLL_DELAY);
         }
         break;
 
       case "keydown":
-        if (!this.focusedElement) {
+        if (!this.focusedElement || this._editing) {
           break;
         }
 
         CompositionManager.endComposition('');
         break;
 
       case "keyup":
-        if (!this.focusedElement) {
+        if (!this.focusedElement || this._editing) {
           break;
         }
 
         CompositionManager.endComposition('');
         break;
 
       case "compositionend":
         if (!this.focusedElement) {
@@ -497,44 +718,142 @@ let FormAssistant = {
         event.initEvent('input', true, false);
         target.dispatchEvent(event);
         break;
       }
 
       case "Forms:Input:SendKey":
         CompositionManager.endComposition('');
 
-        let flags = domWindowUtils.KEY_FLAG_NOT_SYNTHESIZED_FOR_TESTS;
-        this._editing = true;
-        let doKeypress = domWindowUtils.sendKeyEvent('keydown', json.keyCode,
-                                                     json.charCode, json.modifiers, flags);
-        if (doKeypress) {
-          domWindowUtils.sendKeyEvent('keypress', json.keyCode,
-                                      json.charCode, json.modifiers, flags);
-        }
+        let win = target.ownerDocument.defaultView;
+        let tip = WindowMap.getTextInputProcessor(win);
+        if (!tip) {
+          if (json.requestId) {
+            sendAsyncMessage("Forms:SendKey:Result:Error", {
+              requestId: json.requestId,
+              error: "Unable to start input transaction."
+            });
+          }
 
-        if(!json.repeat) {
-          domWindowUtils.sendKeyEvent('keyup', json.keyCode,
-                                      json.charCode, json.modifiers, flags);
+          break;
         }
 
-        this._editing = false;
+        // If we receive a keyboardEventDict from json, that means the user
+        // is calling the method with the new arguments.
+        // Otherwise, we would have to construct our own keyboardEventDict
+        // based on legacy values we have received.
+        let keyboardEventDict = json.keyboardEventDict;
+        let flags = 0;
+
+        if (keyboardEventDict) {
+          if ('flags' in keyboardEventDict) {
+            flags = keyboardEventDict.flags;
+          }
+        } else {
+          // The naive way to figure out if the key to dispatch is printable.
+          let printable = !!json.charCode;
+
+          // For printable keys, the value should be the actual character.
+          // For non-printable keys, it should be a value in the D3E spec.
+          // Here we make some educated guess for it.
+          let key = printable ?
+              String.fromCharCode(json.charCode) :
+              guessKeyNameFromKeyCode(win.KeyboardEvent, json.keyCode);
+
+          // keyCode from content is only respected when the key is not an
+          // an alphanumeric character. We also ask TextInputProcessor not to
+          // infer this value for non-printable keys to keep the original
+          // behavior.
+          let keyCode = (printable && /^[a-zA-Z0-9]$/.test(key)) ?
+              key.toUpperCase().charCodeAt(0) :
+              json.keyCode;
+
+          keyboardEventDict = {
+            key: key,
+            keyCode: keyCode,
+            // We don't have any information to tell the virtual key the
+            // user have interacted with.
+            code: "",
+            // We do not have the information to infer location of the virtual key
+            // either (and we would need TextInputProcessor not to compute it).
+            location: 0,
+            // This indicates the key is triggered for repeats.
+            repeat: json.repeat
+          };
+
+          flags = tip.KEY_KEEP_KEY_LOCATION_STANDARD;
+          if (!printable) {
+            flags |= tip.KEY_NON_PRINTABLE_KEY;
+          }
+          if (!keyboardEventDict.keyCode) {
+            flags |= tip.KEY_KEEP_KEYCODE_ZERO;
+          }
+        }
+
+        let keyboardEvent = new win.KeyboardEvent("", keyboardEventDict);
 
-        if (json.requestId && doKeypress) {
-          sendAsyncMessage("Forms:SendKey:Result:OK", {
-            requestId: json.requestId,
-            selectioninfo: this.getSelectionInfo()
-          });
+        let keydownDefaultPrevented = false;
+        try {
+          switch (json.method) {
+            case 'sendKey': {
+              let consumedFlags = tip.keydown(keyboardEvent, flags);
+              keydownDefaultPrevented =
+                !!(tip.KEYDOWN_IS_CONSUMED & consumedFlags);
+              if (!keyboardEventDict.repeat) {
+                tip.keyup(keyboardEvent, flags);
+              }
+              break;
+            }
+            case 'keydown': {
+              let consumedFlags = tip.keydown(keyboardEvent, flags);
+              keydownDefaultPrevented =
+                !!(tip.KEYDOWN_IS_CONSUMED & consumedFlags);
+              break;
+            }
+            case 'keyup': {
+              tip.keyup(keyboardEvent, flags);
+
+              break;
+            }
+          }
+        } catch (err) {
+          dump("forms.js:" + err.toString() + "\n");
+
+          if (json.requestId) {
+            if (err instanceof Ci.nsIException &&
+                err.result == Cr.NS_ERROR_ILLEGAL_VALUE) {
+              sendAsyncMessage("Forms:SendKey:Result:Error", {
+                requestId: json.requestId,
+                error: "The values specified are illegal."
+              });
+            } else {
+              sendAsyncMessage("Forms:SendKey:Result:Error", {
+                requestId: json.requestId,
+                error: "Unable to type into destroyed input."
+              });
+            }
+          }
+
+          break;
         }
-        else if (json.requestId && !doKeypress) {
-          sendAsyncMessage("Forms:SendKey:Result:Error", {
-            requestId: json.requestId,
-            error: "Keydown event got canceled"
-          });
+
+        if (json.requestId) {
+          if (keydownDefaultPrevented) {
+            sendAsyncMessage("Forms:SendKey:Result:Error", {
+              requestId: json.requestId,
+              error: "Key event(s) was cancelled."
+            });
+          } else {
+            sendAsyncMessage("Forms:SendKey:Result:OK", {
+              requestId: json.requestId,
+              selectioninfo: this.getSelectionInfo()
+            });
+          }
         }
+
         break;
 
       case "Forms:Select:Choice":
         let options = target.options;
         let valueChanged = false;
         if ("index" in json) {
           if (options.selectedIndex != json.index) {
             options.selectedIndex = json.index;
@@ -637,26 +956,26 @@ let FormAssistant = {
       case "Forms:GetContext": {
         let obj = getJSON(target, this._focusCounter);
         sendAsyncMessage("Forms:GetContext:Result:OK", obj);
         break;
       }
 
       case "Forms:SetComposition": {
         CompositionManager.setComposition(target, json.text, json.cursor,
-                                          json.clauses);
+                                          json.clauses, json.keyboardEventDict);
         sendAsyncMessage("Forms:SetComposition:Result:OK", {
           requestId: json.requestId,
           selectioninfo: this.getSelectionInfo()
         });
         break;
       }
 
       case "Forms:EndComposition": {
-        CompositionManager.endComposition(json.text);
+        CompositionManager.endComposition(json.text, json.keyboardEventDict);
         sendAsyncMessage("Forms:EndComposition:Result:OK", {
           requestId: json.requestId,
           selectioninfo: this.getSelectionInfo()
         });
         break;
       }
     }
     this._editing = false;
@@ -1165,57 +1484,30 @@ function replaceSurroundingText(element,
     // Insert the text to be replaced with.
     editor.insertText(text);
   }
   return true;
 }
 
 let CompositionManager =  {
   _isStarted: false,
-  _textInputProcessor: null,
+  _tip: null,
+  _KeyboardEventForWin: null,
   _clauseAttrMap: {
     'raw-input':
       Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE,
     'selected-raw-text':
       Ci.nsITextInputProcessor.ATTR_SELECTED_RAW_CLAUSE,
     'converted-text':
       Ci.nsITextInputProcessor.ATTR_CONVERTED_CLAUSE,
     'selected-converted-text':
       Ci.nsITextInputProcessor.ATTR_SELECTED_CLAUSE
   },
 
-  _callback: function cm_callback(aTIP, aNotification)
-  {
-    try {
-      switch (aNotification.type) {
-        case "request-to-commit":
-          aTIP.commitComposition();
-          break;
-        case "request-to-cancel":
-          aTIP.cancelComposition();
-          break;
-      }
-    } catch (e) {
-      return false;
-    }
-    return true;
-  },
-
-  _prepareTextInputProcessor: function cm_prepareTextInputProcessor(aWindow)
-  {
-    if (!this._textInputProcessor) {
-      this._textInputProcessor =
-        Cc["@mozilla.org/text-input-processor;1"].
-          createInstance(Ci.nsITextInputProcessor);
-    }
-    return this._textInputProcessor.beginInputTransaction(aWindow,
-                                                          this._callback);
-  },
-
-  setComposition: function cm_setComposition(element, text, cursor, clauses) {
+  setComposition: function cm_setComposition(element, text, cursor, clauses, dict) {
     // Check parameters.
     if (!element) {
       return;
     }
     let len = text.length;
     if (cursor > len) {
       cursor = len;
     }
@@ -1243,43 +1535,72 @@ let CompositionManager =  {
         clauseLens[clauseLens.length - 1] += remainingLength;
       }
     } else {
       clauseLens.push(len);
       clauseAttrs.push(Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE);
     }
 
     let win = element.ownerDocument.defaultView;
-    if (!this._prepareTextInputProcessor(win)) {
+    let tip = WindowMap.getTextInputProcessor(win);
+    if (!tip) {
       return;
     }
     // Update the composing text.
-    this._textInputProcessor.setPendingCompositionString(text);
+    tip.setPendingCompositionString(text);
     for (var i = 0; i < clauseLens.length; i++) {
       if (!clauseLens[i]) {
         continue;
       }
-      this._textInputProcessor.appendClauseToPendingComposition(clauseLens[i],
-                                                                clauseAttrs[i]);
+      tip.appendClauseToPendingComposition(clauseLens[i], clauseAttrs[i]);
     }
     if (cursor >= 0) {
-      this._textInputProcessor.setCaretInPendingComposition(cursor);
+      tip.setCaretInPendingComposition(cursor);
     }
-    this._isStarted = this._textInputProcessor.flushPendingComposition();
+
+    if (!dict) {
+      this._isStarted = tip.flushPendingComposition();
+    } else {
+      let keyboardEvent = new win.KeyboardEvent("", dict);
+      let flags = dict.flags;
+      this._isStarted = tip.flushPendingComposition(keyboardEvent, flags);
+    }
+
+    if (this._isStarted) {
+      this._tip = tip;
+      this._KeyboardEventForWin = win.KeyboardEvent;
+    }
   },
 
-  endComposition: function cm_endComposition(text) {
+  endComposition: function cm_endComposition(text, dict) {
     if (!this._isStarted) {
       return;
     }
-    this._textInputProcessor.commitCompositionWith(text ? text : "");
+    let tip = this._tip;
+    if (!tip) {
+      return;
+    }
+
+    text = text || "";
+    if (!dict) {
+      tip.commitCompositionWith(text);
+    } else {
+      let keyboardEvent = new this._KeyboardEventForWin("", dict);
+      let flags = dict.flags;
+      tip.commitCompositionWith(text, keyboardEvent, flags);
+    }
+
     this._isStarted = false;
+    this._tip = null;
+    this._KeyboardEventForWin = null;
   },
 
   // Composition ends due to external actions.
   onCompositionEnd: function cm_onCompositionEnd() {
     if (!this._isStarted) {
       return;
     }
 
     this._isStarted = false;
+    this._tip = null;
+    this._KeyboardEventForWin = null;
   }
 };
--- a/dom/inputmethod/mochitest/mochitest.ini
+++ b/dom/inputmethod/mochitest/mochitest.ini
@@ -21,8 +21,9 @@ support-files =
 [test_bug1059163.html]
 [test_bug1066515.html]
 [test_bug1175399.html]
 [test_sendkey_cancel.html]
 [test_sync_edit.html]
 [test_two_inputs.html]
 [test_two_selects.html]
 [test_unload.html]
+[test_bug1137557.html]
new file mode 100644
--- /dev/null
+++ b/dom/inputmethod/mochitest/test_bug1137557.html
@@ -0,0 +1,1674 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1137557
+-->
+<head>
+  <title>Test for new API arguments accepting D3E properties</title>
+  <script type="application/javascript;version=1.7" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript;version=1.7" src="inputmethod_common.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1137557">Mozilla Bug 1137557</a>
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.7">
+
+inputmethod_setup(function() {
+  runTest();
+});
+
+let gEventDetails = [];
+let gCurrentValue = '';
+let gTestDescription = '';
+
+let appFrameScript = function appFrameScript() {
+  let input = content.document.body.firstElementChild;
+
+  input.focus();
+
+  function sendEventDetail(evt) {
+    var eventDetail;
+
+    switch (evt.type) {
+      case 'compositionstart':
+      case 'compositionupdate':
+      case 'compositionend':
+        eventDetail = {
+          type: evt.type,
+          value: input.value,
+          data: evt.data
+        };
+        break;
+
+      case 'input':
+        eventDetail = {
+          type: evt.type,
+          value: input.value
+        };
+        break;
+
+      default: // keyboard events
+        eventDetail = {
+          type: evt.type,
+          charCode: evt.charCode,
+          keyCode: evt.keyCode,
+          key: evt.key,
+          code: evt.code,
+          location: evt.location,
+          repeat: evt.repeat,
+          value: input.value,
+          shift: evt.getModifierState('Shift'),
+          capsLock: evt.getModifierState('CapsLock'),
+          control: evt.getModifierState('Control'),
+          alt: evt.getModifierState('Alt')
+        };
+        break;
+    }
+
+    sendAsyncMessage('test:eventDetail', eventDetail);
+  }
+
+  input.addEventListener('compositionstart', sendEventDetail);
+  input.addEventListener('compositionupdate', sendEventDetail);
+  input.addEventListener('compositionend', sendEventDetail);
+  input.addEventListener('input', sendEventDetail);
+  input.addEventListener('keydown', sendEventDetail);
+  input.addEventListener('keypress', sendEventDetail);
+  input.addEventListener('keyup', sendEventDetail);
+};
+
+function waitForInputContextChange() {
+  return new Promise((resolve) => {
+    navigator.mozInputMethod.oninputcontextchange = resolve;
+  });
+}
+
+function assertEventDetail(expectedDetails, testName) {
+  is(gEventDetails.length, expectedDetails.length,
+    testName + ' expects ' + expectedDetails.map(d => d.type).join(', ') + ' events, got ' + gEventDetails.map(d => d.type).join(', '));
+
+  expectedDetails.forEach((expectedDetail, j) => {
+    for (let key in expectedDetail) {
+      is(gEventDetails[j][key], expectedDetail[key],
+        testName + ' expects ' + key + ' of ' + gEventDetails[j].type + ' to be equal to ' + expectedDetail[key]);
+    }
+  });
+}
+
+function sendKeyAndAssertResult(testdata) {
+  var dict = testdata.dict;
+  var testName = gTestDescription + 'sendKey(' + JSON.stringify(dict) + ')';
+  var promise = navigator.mozInputMethod.inputcontext.sendKey(dict);
+
+  if (testdata.expectedReject) {
+    promise = promise
+      .then(() => {
+        ok(false, testName + ' should not resolve.');
+      }, (e) => {
+        ok(true, testName + ' rejects.');
+        ok(e instanceof testdata.expectedReject, 'Reject with type.');
+      })
+
+    return promise;
+  }
+
+  promise = promise
+    .then((res) => {
+      is(res, true,
+        testName + ' should resolve to true.');
+
+      var expectedEventDetail = [];
+
+      var expectedValues = testdata.expectedValues;
+
+      expectedEventDetail.push({
+        type: 'keydown',
+        key: expectedValues.key,
+        charCode: 0,
+        code: expectedValues.code || '',
+        keyCode: expectedValues.keyCode || 0,
+        location: 0,
+        repeat: expectedValues.repeat || false,
+        value: gCurrentValue,
+        shift: false,
+        capsLock: false,
+        control: false,
+        alt: false
+      });
+
+      if (testdata.expectedKeypress) {
+        expectedEventDetail.push({
+          type: 'keypress',
+          key: expectedValues.key,
+          charCode: expectedValues.charCode,
+          code: expectedValues.code || '',
+          keyCode: expectedValues.charCode ? 0 : expectedValues.keyCode,
+          location: 0,
+          repeat: expectedValues.repeat || false,
+          value: gCurrentValue,
+          shift: false,
+          capsLock: false,
+          control: false,
+          alt: false
+        });
+      }
+
+      if (testdata.expectedInput) {
+        expectedEventDetail.push({
+          type: 'input',
+          value: gCurrentValue += testdata.expectedInput
+        });
+      }
+
+      if (!testdata.expectedRepeat) {
+        expectedEventDetail.push({
+          type: 'keyup',
+          key: expectedValues.key,
+          charCode: 0,
+          code: expectedValues.code || '',
+          keyCode: expectedValues.keyCode || 0,
+          location: 0,
+          repeat: expectedValues.repeat || false,
+          value: gCurrentValue,
+          shift: false,
+          capsLock: false,
+          control: false,
+          alt: false
+        });
+      }
+
+      assertEventDetail(expectedEventDetail, testName);
+      gEventDetails = [];
+    }, (e) => {
+      ok(false, testName + ' should not reject. ' + e);
+    });
+
+  return promise;
+}
+
+function runSendKeyAlphabetTests() {
+  gTestDescription = 'runSendKeyAlphabetTests(): ';
+  var promiseQueue = Promise.resolve();
+
+  // Test the plain alphabets
+  var codeA = 'A'.charCodeAt(0);
+  for (var i = 0; i < 26; i++) {
+    // callbacks in then() are deferred; must only reference these block-scoped
+    // variable instead of i.
+    let keyCode = codeA + i;
+    let code = 'Key' + String.fromCharCode(keyCode);
+
+    [String.fromCharCode(keyCode),
+      String.fromCharCode(keyCode).toLowerCase()]
+    .forEach((chr) => {
+      // Test plain alphabet
+      promiseQueue = promiseQueue.then(() => {
+        return sendKeyAndAssertResult({
+          dict: {
+            key: chr
+          },
+          expectedKeypress: true,
+          expectedInput: chr,
+          expectedValues: {
+            key: chr, code: '',
+            keyCode: keyCode,
+            charCode: chr.charCodeAt(0)
+          }
+        });
+      });
+
+      // Test plain alphabet with keyCode set
+      promiseQueue = promiseQueue.then(() => {
+        return sendKeyAndAssertResult({
+          dict: {
+            key: chr,
+            keyCode: keyCode
+          },
+          expectedKeypress: true,
+          expectedInput: chr,
+          expectedValues: {
+            key: chr, code: '',
+            keyCode: keyCode,
+            charCode: chr.charCodeAt(0)
+          }
+        });
+      });
+
+      // Test plain alphabet with keyCode set to keyCode + 1,
+      // expects keyCode to follow key value and ignore the incorrect value.
+      promiseQueue = promiseQueue.then(() => {
+        return sendKeyAndAssertResult({
+          dict: {
+            key: chr,
+            keyCode: keyCode + 1
+          },
+          expectedKeypress: true,
+          expectedInput: chr,
+          expectedValues: {
+            key: chr, code: '',
+            keyCode: keyCode,
+            charCode: chr.charCodeAt(0)
+          }
+        });
+      });
+
+      // Test plain alphabet with code set
+      promiseQueue = promiseQueue.then(() => {
+        return sendKeyAndAssertResult({
+          dict: {
+            key: chr,
+            code: code
+          },
+          expectedKeypress: true,
+          expectedInput: chr,
+          expectedValues: {
+            key: chr, code: code,
+            keyCode: keyCode,
+            charCode: chr.charCodeAt(0)
+          }
+        });
+      });
+
+      // Test plain alphabet with code set to Digit1,
+      // expects keyCode to follow key value.
+      promiseQueue = promiseQueue.then(() => {
+        return sendKeyAndAssertResult({
+          dict: {
+            key: chr,
+            code: 'Digit1'
+          },
+          expectedKeypress: true,
+          expectedInput: chr,
+          expectedValues: {
+            key: chr, code: 'Digit1',
+            keyCode: keyCode,
+            charCode: chr.charCodeAt(0)
+          }
+        });
+      });
+
+      // Test plain alphabet with keyCode set to DOM_VK_1,
+      // expects keyCode to follow key value.
+      promiseQueue = promiseQueue.then(() => {
+        return sendKeyAndAssertResult({
+          dict: {
+            key: chr,
+            keyCode: KeyboardEvent.DOM_VK_1
+          },
+          expectedKeypress: true,
+          expectedInput: chr,
+          expectedValues: {
+            key: chr, code: '',
+            keyCode: keyCode,
+            charCode: chr.charCodeAt(0)
+          }
+        });
+      });
+
+      // Test plain alphabet with code set to Digit1
+      // and keyCode set to DOM_VK_1,
+      // expects keyCode to follow key value.
+      promiseQueue = promiseQueue.then(() => {
+        return sendKeyAndAssertResult({
+          dict: {
+            key: chr,
+            code: 'Digit1',
+            keyCode: KeyboardEvent.DOM_VK_1
+          },
+          expectedKeypress: true,
+          expectedInput: chr,
+          expectedValues: {
+            key: chr, code: 'Digit1',
+            keyCode: keyCode,
+            charCode: chr.charCodeAt(0)
+          }
+        });
+      });
+    });
+  }
+
+  return promiseQueue;
+}
+
+function runSendKeyNumberTests() {
+  gTestDescription = 'runSendKeyNumberTests(): ';
+  var promiseQueue = Promise.resolve();
+
+  // Test numbers
+  var code0 = '0'.charCodeAt(0);
+  for (var i = 0; i < 10; i++) {
+    // callbacks in then() are deferred; must only reference these block-scoped
+    // variable instead of i.
+    let keyCode = code0 + i;
+    let chr = String.fromCharCode(keyCode);
+
+    // Test plain number
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: '',
+          keyCode: keyCode,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain number with keyCode set
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          keyCode: keyCode
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: '',
+          keyCode: keyCode,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain number with keyCode set to keyCode + 1,
+    // expects keyCode to follow key value and ignore the incorrect value.
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          keyCode: keyCode + 1
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: '',
+          keyCode: keyCode,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain number with code set
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          code: 'Digit' + chr
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: 'Digit' + chr,
+          keyCode: keyCode,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain upper caps alphabet with code set to KeyA,
+    // expects keyCode to follow key value.
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          code: 'KeyA'
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: 'KeyA',
+          keyCode: keyCode,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain upper caps alphabet with code set to KeyA,
+    // and keyCode set to DOM_VK_A.
+    // expects keyCode to follow key value.
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          code: 'KeyA',
+          keyCode: KeyboardEvent.DOM_VK_A
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: 'KeyA',
+          keyCode: keyCode,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+  }
+
+  return promiseQueue;
+}
+
+function runSendKeyDvorakTests() {
+  gTestDescription = 'runSendKeyDvorakTests(): ';
+  var promiseQueue = Promise.resolve();
+
+  // Test Dvorak layout emulation
+  var qwertyCodeForDvorakKeys = [
+    'KeyR', 'KeyT', 'KeyY', 'KeyU', 'KeyI', 'KeyO', 'KeyP',
+    'KeyA', 'KeyS', 'KeyD', 'KeyF', 'KeyG',
+    'KeyH', 'KeyJ', 'KeyK', 'KeyL', 'Semicolon',
+    'KeyX', 'KeyC', 'KeyV', 'KeyB', 'KeyN',
+    'KeyM', 'Comma', 'Period', 'Slash'];
+  var dvorakKeys = 'PYFGCRL' +
+    'AOEUIDHTNS' +
+    'QJKXBMWVZ';
+  for (var i = 0; i < dvorakKeys.length; i++) {
+    // callbacks in then() are deferred; must only reference these block-scoped
+    // variable instead of i.
+    let keyCode = dvorakKeys.charCodeAt(i);
+    let code = qwertyCodeForDvorakKeys[i];
+
+    [dvorakKeys.charAt(i), dvorakKeys.charAt(i).toLowerCase()]
+    .forEach((chr) => {
+      // Test alphabet with code set to Qwerty code,
+      // expects keyCode to follow key value.
+      // (This is *NOT* the expected scenario for emulating a Dvorak keyboard,
+      //  even though expected results are the same.)
+      promiseQueue = promiseQueue.then(() => {
+        return sendKeyAndAssertResult({
+          dict: {
+            key: chr,
+            code: code
+          },
+          expectedKeypress: true,
+          expectedInput: chr,
+          expectedValues: {
+            key: chr, code: code,
+            keyCode: keyCode,
+            charCode: chr.charCodeAt(0)
+          }
+        });
+      });
+
+      // Test alphabet with code set to Qwerty code and keyCode set,
+      // expects keyCode to follow key/keyCode value.
+      // (This is the expected scenario for emulating a Dvorak keyboard)
+      promiseQueue = promiseQueue.then(() => {
+        return sendKeyAndAssertResult({
+          dict: {
+            key: chr,
+            keyCode: keyCode,
+            code: code
+          },
+          expectedKeypress: true,
+          expectedInput: chr,
+          expectedValues: {
+            key: chr, code: code,
+            keyCode: keyCode,
+            charCode: chr.charCodeAt(0)
+          }
+        });
+      });
+    });
+  }
+
+  var qwertyCodeForDvorakSymbols = [
+    'Minus', 'Equal',
+    'KeyQ', 'KeyW', 'KeyE', 'BracketLeft', 'BracketRight', 'Backslash',
+    'Quote', 'KeyZ'];
+
+  var shiftDvorakSymbols = '{}\"<>?+|_:';
+  var dvorakSymbols = '[]\',./=\\-;';
+  var dvorakSymbolsKeyCodes = [
+    KeyboardEvent.DOM_VK_OPEN_BRACKET,
+    KeyboardEvent.DOM_VK_CLOSE_BRACKET,
+    KeyboardEvent.DOM_VK_QUOTE,
+    KeyboardEvent.DOM_VK_COMMA,
+    KeyboardEvent.DOM_VK_PERIOD,
+    KeyboardEvent.DOM_VK_SLASH,
+    KeyboardEvent.DOM_VK_EQUALS,
+    KeyboardEvent.DOM_VK_BACK_SLASH,
+    KeyboardEvent.DOM_VK_HYPHEN_MINUS,
+    KeyboardEvent.DOM_VK_SEMICOLON
+  ];
+
+  for (var i = 0; i < dvorakSymbols.length; i++) {
+    // callbacks in then() are deferred; must only reference these block-scoped
+    // variable instead of i.
+    let keyCode = dvorakSymbolsKeyCodes[i];
+    let code = qwertyCodeForDvorakSymbols[i];
+
+    [dvorakSymbols.charAt(i), shiftDvorakSymbols.charAt(i)]
+    .forEach((chr) => {
+      // Test symbols with code set to Qwerty code,
+      // expects keyCode to be 0.
+      // (This is *NOT* the expected scenario for emulating a Dvorak keyboard)
+      promiseQueue = promiseQueue.then(() => {
+        return sendKeyAndAssertResult({
+          dict: {
+            key: chr,
+            code: code
+          },
+          expectedKeypress: true,
+          expectedInput: chr,
+          expectedValues: {
+            key: chr, code: code,
+            keyCode: 0,
+            charCode: chr.charCodeAt(0)
+          }
+        });
+      });
+
+      // Test alphabet with code set to Qwerty code and keyCode set,
+      // expects keyCode to follow keyCode value.
+      // (This is the expected scenario for emulating a Dvorak keyboard)
+      promiseQueue = promiseQueue.then(() => {
+        return sendKeyAndAssertResult({
+          dict: {
+            key: chr,
+            keyCode: keyCode,
+            code: code
+          },
+          expectedKeypress: true,
+          expectedInput: chr,
+          expectedValues: {
+            key: chr, code: code,
+            keyCode: keyCode,
+            charCode: chr.charCodeAt(0)
+          }
+        });
+      });
+    });
+  }
+
+  return promiseQueue;
+}
+
+function runSendKeyDigitKeySymbolsTests() {
+  gTestDescription = 'runSendKeyDigitKeySymbolsTests(): ';
+  var promiseQueue = Promise.resolve();
+
+  var digitKeySymbols = ')!@#$%^&*(';
+  for (var i = 0; i < digitKeySymbols.length; i++) {
+    // callbacks in then() are deferred; must only reference these block-scoped
+    // variable instead of i.
+    let keyCode = KeyboardEvent['DOM_VK_' + i];
+    let chr = digitKeySymbols.charAt(i);
+    let code = 'Digit' + i;
+
+    // Test plain symbol
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: '', keyCode: 0,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain symbol with keyCode set
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          keyCode: keyCode
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: '',
+          keyCode: keyCode,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain symbol with code set
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          code: code
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: code,
+          keyCode: 0,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain symbol with code set to KeyA,
+    // expects keyCode to be 0.
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          code: 'KeyA'
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: 'KeyA',
+          keyCode: 0,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain symbol with keyCode set to DOM_VK_A,
+    // expects keyCode to follow the keyCode set.
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          keyCode: KeyboardEvent.DOM_VK_A
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: '',
+          keyCode: KeyboardEvent.DOM_VK_A,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain symbol with code set to KeyA
+    // expects keyCode to follow the keyCode set.
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          code: 'KeyA',
+          keyCode: KeyboardEvent.DOM_VK_A
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: 'KeyA',
+          keyCode: KeyboardEvent.DOM_VK_A,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+  }
+
+  return promiseQueue;
+}
+
+function runSendKeyUSKeyboardSymbolsTests() {
+  gTestDescription = 'runSendKeyUSKeyboardSymbolsTests(): ';
+  var promiseQueue = Promise.resolve();
+
+  // Test printable symbols on US Keyboard
+  var symbols = ' ;:=+,<-_.>/?`~[{\\|]}\'\"';
+  var symbolKeyCodes = [
+    KeyboardEvent.DOM_VK_SPACE,
+    KeyboardEvent.DOM_VK_SEMICOLON,
+    KeyboardEvent.DOM_VK_SEMICOLON,
+    KeyboardEvent.DOM_VK_EQUALS,
+    KeyboardEvent.DOM_VK_EQUALS,
+    KeyboardEvent.DOM_VK_COMMA,
+    KeyboardEvent.DOM_VK_COMMA,
+    KeyboardEvent.DOM_VK_HYPHEN_MINUS,
+    KeyboardEvent.DOM_VK_HYPHEN_MINUS,
+    KeyboardEvent.DOM_VK_PERIOD,
+    KeyboardEvent.DOM_VK_PERIOD,
+    KeyboardEvent.DOM_VK_SLASH,
+    KeyboardEvent.DOM_VK_SLASH,
+    KeyboardEvent.DOM_VK_BACK_QUOTE,
+    KeyboardEvent.DOM_VK_BACK_QUOTE,
+    KeyboardEvent.DOM_VK_OPEN_BRACKET,
+    KeyboardEvent.DOM_VK_OPEN_BRACKET,
+    KeyboardEvent.DOM_VK_BACK_SLASH,
+    KeyboardEvent.DOM_VK_BACK_SLASH,
+    KeyboardEvent.DOM_VK_CLOSE_BRACKET,
+    KeyboardEvent.DOM_VK_CLOSE_BRACKET,
+    KeyboardEvent.DOM_VK_QUOTE,
+    KeyboardEvent.DOM_VK_QUOTE
+  ];
+  var symbolCodes = [
+    'Space',
+    'Semicolon',
+    'Semicolon',
+    'Equal',
+    'Equal',
+    'Comma',
+    'Comma',
+    'Minus',
+    'Minus',
+    'Period',
+    'Period',
+    'Slash',
+    'Slash',
+    'Backquote',
+    'Backquote',
+    'BracketLeft',
+    'BracketLeft',
+    'Backslash',
+    'Backslash',
+    'BracketRight',
+    'BracketRight',
+    'Quote',
+    'Quote'
+  ];
+  for (var i = 0; i < symbols.length; i++) {
+    // callbacks in then() are deferred; must only reference these block-scoped
+    // variable instead of i.
+    let keyCode = symbolKeyCodes[i];
+    let chr = symbols.charAt(i);
+    let code = symbolCodes[i];
+
+    // Test plain symbol
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: '',
+          keyCode: 0,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain symbol with keyCode set
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          keyCode: keyCode
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: '',
+          keyCode: keyCode,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain symbol with code set
+    // expects keyCode to be 0.
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          code: code
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: code,
+          keyCode: 0,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain symbol with code set to KeyA,
+    // expects keyCode to be 0.
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          code: 'KeyA'
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: 'KeyA',
+          keyCode: 0,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain symbol with keyCode set to DOM_VK_A,
+    // expects keyCode to follow the keyCode set.
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          keyCode: KeyboardEvent.DOM_VK_A
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: '',
+          keyCode: KeyboardEvent.DOM_VK_A,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain symbol with code set to KeyA
+    // expects keyCode to follow the keyCode set.
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          code: 'KeyA',
+          keyCode: KeyboardEvent.DOM_VK_A
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: 'KeyA',
+          keyCode: KeyboardEvent.DOM_VK_A,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+  }
+
+  return promiseQueue;
+}
+
+function runSendKeyGreekLettersTests() {
+  gTestDescription = 'runSendKeyGreekLettersTests(): ';
+  var promiseQueue = Promise.resolve();
+
+  // Test Greek letters
+  var greekLetters =
+    '\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c' +
+    '\u039d\u039e\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9' +
+    '\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc' +
+    '\u03bd\u03be\u03bf\u03c0\u03c1\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9' +
+    '\u03c2';
+  var greekLettersLayoutMap =
+    'ABGDEZHUIKLMNJOPRSTYFXCVABGDEZHUIKLMNJOPRSTYFXCVQ';
+  for (var i = 0; i < greekLetters.length; i++) {
+    // callbacks in then() are deferred; must only reference these block-scoped
+    // variable instead of i.
+    let keyCode = greekLettersLayoutMap.charCodeAt(i);
+    let chr = greekLetters.charAt(i);
+    let code = 'Key' + greekLettersLayoutMap.charAt(i);
+
+    // Test plain alphabet
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: '',
+          keyCode: 0,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain alphabet with keyCode set
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          keyCode: keyCode
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: '',
+          keyCode: keyCode,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain alphabet with code set,
+    // expects keyCode to be 0.
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          code: code
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: code,
+          keyCode: 0,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain alphabet with code set to Digit1,
+    // expects keyCode to be 0.
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          code: 'Digit1'
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: 'Digit1',
+          keyCode: 0,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+    // Test plain alphabet with code set to Digit1,
+    // and keyCode set to DOM_VK_A.
+    // expects keyCode to follow the keyCode set.
+    promiseQueue = promiseQueue.then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: chr,
+          code: 'Digit1',
+          keyCode: KeyboardEvent.DOM_VK_A
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: 'Digit1',
+          keyCode: KeyboardEvent.DOM_VK_A,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+  }
+
+  return promiseQueue;
+}
+
+function runSendKeyEnterTests() {
+  gTestDescription = 'runSendKeyEnterTests(): ';
+  var promiseQueue = Promise.resolve();
+
+  // Test Enter with code unset
+  promiseQueue = promiseQueue.then(() => {
+    return sendKeyAndAssertResult({
+      dict: {
+        key: 'Enter'
+      },
+      expectedKeypress: true,
+      expectedInput: '\n',
+      expectedValues: {
+        key: 'Enter', code: '',
+        keyCode: KeyboardEvent.DOM_VK_RETURN,
+        charCode: 0
+      }
+    });
+  });
+
+  // Test Enter with code set
+  promiseQueue = promiseQueue.then(() => {
+    return sendKeyAndAssertResult({
+      dict: {
+        key: 'Enter',
+        code: 'Enter'
+      },
+      expectedKeypress: true,
+      expectedInput: '\n',
+      expectedValues: {
+        key: 'Enter', code: 'Enter',
+        keyCode: KeyboardEvent.DOM_VK_RETURN,
+        charCode: 0
+      }
+    });
+  });
+
+  // Test Enter with keyCode explict set to zero
+  promiseQueue = promiseQueue.then(() => {
+    return sendKeyAndAssertResult({
+      dict: {
+        key: 'Enter',
+        keyCode: 0
+      },
+      expectedKeypress: true,
+      expectedValues: {
+        key: 'Enter', code: '',
+        keyCode: 0,
+        charCode: 0
+      }
+    });
+  });
+
+  return promiseQueue;
+}
+
+function runSendKeyRejectionTests() {
+  gTestDescription = 'runSendKeyRejectionTests(): ';
+  var promiseQueue = Promise.resolve();
+
+  promiseQueue = promiseQueue.then(() => {
+    return sendKeyAndAssertResult({
+      dict: undefined,
+      expectedReject: TypeError
+    });
+  });
+
+  return promiseQueue;
+}
+
+function setCompositionAndAssertResult(testdata) {
+  var dict = testdata.dict;
+  var testName;
+  var promise;
+
+  if (dict) {
+    testName = gTestDescription +
+      'setComposition(' + testdata.text +
+      ', undefined, undefined, '
+      + JSON.stringify(dict) + ')';
+    promise = navigator.mozInputMethod.inputcontext
+      .setComposition(testdata.text, undefined, undefined, dict);
+  } else {
+    testName = gTestDescription +
+      'setComposition(' + testdata.text + ')';
+    promise = navigator.mozInputMethod.inputcontext
+      .setComposition(testdata.text);
+  }
+
+  if (testdata.expectedReject) {
+    promise = promise
+      .then(() => {
+        ok(false, testName + ' should not resolve.');
+      }, (e) => {
+        ok(true, testName + ' rejects.');
+        ok(e instanceof testdata.expectedReject, 'Reject with type.');
+      })
+
+    return promise;
+  }
+
+  promise = promise
+    .then((res) => {
+      is(res, true,
+        testName + ' should resolve to true.');
+
+      var expectedEventDetail = [];
+
+      var expectedValues = testdata.expectedValues;
+
+      if (testdata.expectsKeyEvents &&
+          (testdata.startsComposition ||
+           testdata.dispatchKeyboardEventDuringComposition)) {
+        expectedEventDetail.push({
+          type: 'keydown',
+          key: expectedValues.key,
+          charCode: 0,
+          code: expectedValues.code || '',
+          keyCode: expectedValues.keyCode || 0,
+          location: 0,
+          repeat: expectedValues.repeat || false,
+          value: gCurrentValue,
+          shift: false,
+          capsLock: false,
+          control: false,
+          alt: false
+        });
+      }
+
+      if (testdata.startsComposition) {
+        expectedEventDetail.push({
+          type: 'compositionstart',
+          data: '',
+          value: gCurrentValue
+        });
+      }
+
+      expectedEventDetail.push({
+        type: 'compositionupdate',
+        data: testdata.text,
+        value: gCurrentValue
+      });
+
+      expectedEventDetail.push({
+        type: 'input',
+        value: gCurrentValue += testdata.expectedInput
+      });
+
+      if (testdata.expectsKeyEvents &&
+          testdata.dispatchKeyboardEventDuringComposition) {
+        expectedEventDetail.push({
+          type: 'keyup',
+          key: expectedValues.key,
+          charCode: 0,
+          code: expectedValues.code || '',
+          keyCode: expectedValues.keyCode || 0,
+          location: 0,
+          repeat: expectedValues.repeat || false,
+          value: gCurrentValue,
+          shift: false,
+          capsLock: false,
+          control: false,
+          alt: false
+        });
+      }
+
+      assertEventDetail(expectedEventDetail, testName);
+      gEventDetails = [];
+    }, (e) => {
+      ok(false, testName + ' should not reject. ' + e);
+    });
+
+  return promise;
+}
+
+function endCompositionAndAssertResult(testdata) {
+  var dict = testdata.dict;
+  var testName;
+  var promise;
+  if (dict) {
+    testName = gTestDescription +
+      'endComposition(' + testdata.text + ', ' + JSON.stringify(dict) + ')';
+    promise = navigator.mozInputMethod.inputcontext
+      .endComposition(testdata.text, dict);
+  } else {
+    testName = gTestDescription +
+      'endComposition(' + testdata.text + ')';
+    promise = navigator.mozInputMethod.inputcontext
+      .endComposition(testdata.text);
+  }
+
+  if (testdata.expectedReject) {
+    promise = promise
+      .then(() => {
+        ok(false, testName + ' should not resolve.');
+      }, (e) => {
+        ok(true, testName + ' rejects.');
+        ok(e instanceof testdata.expectedReject, 'Reject with type.');
+      })
+
+    return promise;
+  }
+
+  promise = promise
+    .then((res) => {
+      is(res, true,
+        testName + ' should resolve to true.');
+
+      var expectedEventDetail = [];
+
+      var expectedValues = testdata.expectedValues;
+
+      if (testdata.expectsKeyEvents &&
+          testdata.dispatchKeyboardEventDuringComposition) {
+        expectedEventDetail.push({
+          type: 'keydown',
+          key: expectedValues.key,
+          charCode: 0,
+          code: expectedValues.code || '',
+          keyCode: expectedValues.keyCode || 0,
+          location: 0,
+          repeat: expectedValues.repeat || false,
+          value: gCurrentValue,
+          shift: false,
+          capsLock: false,
+          control: false,
+          alt: false
+        });
+      }
+
+      expectedEventDetail.push({
+        type: 'compositionend',
+        data: testdata.text,
+        value: gCurrentValue
+      });
+
+      expectedEventDetail.push({
+        type: 'input',
+        value: gCurrentValue
+      });
+
+      if (testdata.expectsKeyEvents) {
+        expectedEventDetail.push({
+          type: 'keyup',
+          key: expectedValues.key,
+          charCode: 0,
+          code: expectedValues.code || '',
+          keyCode: expectedValues.keyCode || 0,
+          location: 0,
+          repeat: expectedValues.repeat || false,
+          value: gCurrentValue,
+          shift: false,
+          capsLock: false,
+          control: false,
+          alt: false
+        });
+      }
+
+      assertEventDetail(expectedEventDetail, testName);
+      gEventDetails = [];
+    }, (e) => {
+      ok(false, testName + ' should not reject. ' + e);
+    });
+
+  return promise;
+}
+
+function runCompositionWithKeyEventTests() {
+  var promiseQueue = Promise.resolve();
+
+  [true, false].forEach((dispatchKeyboardEventDuringComposition) => {
+    gTestDescription = 'runCompositionWithKeyEventTests() (dispatchKeyboardEvent =' + dispatchKeyboardEventDuringComposition + '): ';
+
+    promiseQueue = promiseQueue
+      .then(() => {
+        SpecialPowers.setBoolPref(
+          'dom.keyboardevent.dispatch_during_composition',
+          dispatchKeyboardEventDuringComposition);
+      })
+      .then(() => {
+        return setCompositionAndAssertResult({
+          text: 'foo',
+          expectsKeyEvents: true,
+          startsComposition: true,
+          dispatchKeyboardEventDuringComposition: dispatchKeyboardEventDuringComposition,
+          expectedInput: 'foo',
+          dict: {
+            key: 'a',
+            code: 'KeyA',
+            keyCode: KeyboardEvent.DOM_VK_A
+          },
+          expectedValues: {
+            key: 'a',
+            code: 'KeyA',
+            keyCode: KeyboardEvent.DOM_VK_A
+          }
+        });
+      })
+      .then(() => {
+        return setCompositionAndAssertResult({
+          text: 'foobar',
+          expectsKeyEvents: true,
+          startsComposition: false,
+          dispatchKeyboardEventDuringComposition: dispatchKeyboardEventDuringComposition,
+          expectedInput: 'bar',
+          dict: {
+            key: 'a',
+            code: 'KeyA',
+            keyCode: KeyboardEvent.DOM_VK_A
+          },
+          expectedValues: {
+            key: 'a',
+            code: 'KeyA',
+            keyCode: KeyboardEvent.DOM_VK_A
+          }
+        });
+      })
+      .then(() => {
+        return endCompositionAndAssertResult({
+          text: 'foobar',
+          expectsKeyEvents: true,
+          dispatchKeyboardEventDuringComposition: dispatchKeyboardEventDuringComposition,
+          expectedInput: '',
+          dict: {
+            key: 'a',
+            code: 'KeyA',
+            keyCode: KeyboardEvent.DOM_VK_A
+          },
+          expectedValues: {
+            key: 'a',
+            code: 'KeyA',
+            keyCode: KeyboardEvent.DOM_VK_A
+          }
+        });
+      })
+      .then(() => {
+        SpecialPowers.clearUserPref(
+          'dom.keyboardevent.dispatch_during_composition');
+      });
+  });
+
+  return promiseQueue;
+}
+
+function runCompositionWithoutKeyEventTests() {
+  var promiseQueue = Promise.resolve();
+
+  gTestDescription = 'runCompositionWithoutKeyEventTests(): ';
+
+  promiseQueue = promiseQueue
+    .then(() => {
+      return setCompositionAndAssertResult({
+        text: 'foo',
+        expectsKeyEvents: false,
+        startsComposition: true,
+        expectedInput: 'foo'
+      });
+    })
+    .then(() => {
+      return setCompositionAndAssertResult({
+        text: 'foobar',
+        expectsKeyEvents: false,
+        startsComposition: false,
+        expectedInput: 'bar'
+      });
+    })
+    .then(() => {
+      return endCompositionAndAssertResult({
+        text: 'foobar',
+        expectsKeyEvents: false,
+        expectedInput: ''
+      });
+    });
+
+  return promiseQueue;
+}
+
+function keydownAndAssertResult(testdata) {
+  var dict = testdata.dict;
+  var testName = gTestDescription + 'keydown(' + JSON.stringify(dict) + ')';
+  var promise = navigator.mozInputMethod.inputcontext.keydown(dict);
+
+  if (testdata.expectedReject) {
+    promise = promise
+      .then(() => {
+        ok(false, testName + ' should not resolve.');
+      }, (e) => {
+        ok(true, testName + ' rejects.');
+        ok(e instanceof testdata.expectedReject, 'Reject with type.');
+      })
+
+    return promise;
+  }
+
+  promise = promise
+    .then((res) => {
+      is(res, true,
+        testName + ' should resolve to true.');
+
+      var expectedEventDetail = [];
+
+      var expectedValues = testdata.expectedValues;
+
+      expectedEventDetail.push({
+        type: 'keydown',
+        key: expectedValues.key,
+        charCode: 0,
+        code: expectedValues.code || '',
+        keyCode: expectedValues.keyCode || 0,
+        location: 0,
+        repeat: expectedValues.repeat || false,
+        value: gCurrentValue,
+        shift: false,
+        capsLock: false,
+        control: false,
+        alt: false
+      });
+
+      if (testdata.expectedKeypress) {
+        expectedEventDetail.push({
+          type: 'keypress',
+          key: expectedValues.key,
+          charCode: expectedValues.charCode,
+          code: expectedValues.code || '',
+          keyCode: expectedValues.charCode ? 0 : expectedValues.keyCode,
+          location: 0,
+          repeat: expectedValues.repeat || false,
+          value: gCurrentValue,
+          shift: false,
+          capsLock: false,
+          control: false,
+          alt: false
+        });
+      }
+
+      if (testdata.expectedInput) {
+        expectedEventDetail.push({
+          type: 'input',
+          value: gCurrentValue += testdata.expectedInput
+        });
+      }
+
+      assertEventDetail(expectedEventDetail, testName);
+      gEventDetails = [];
+    }, (e) => {
+      ok(false, testName + ' should not reject. ' + e);
+    });
+
+  return promise;
+}
+
+function keyupAndAssertResult(testdata) {
+  var dict = testdata.dict;
+  var testName = gTestDescription + 'keyup(' + JSON.stringify(dict) + ')';
+  var promise = navigator.mozInputMethod.inputcontext.keyup(dict);
+
+  if (testdata.expectedReject) {
+    promise = promise
+      .then(() => {
+        ok(false, testName + ' should not resolve.');
+      }, (e) => {
+        ok(true, testName + ' rejects.');
+        ok(e instanceof testdata.expectedReject, 'Reject with type.');
+      })
+
+    return promise;
+  }
+
+  promise = promise
+    .then((res) => {
+      is(res, true,
+        testName + ' should resolve to true.');
+
+      var expectedEventDetail = [];
+
+      var expectedValues = testdata.expectedValues;
+
+      expectedEventDetail.push({
+        type: 'keyup',
+        key: expectedValues.key,
+        charCode: 0,
+        code: expectedValues.code || '',
+        keyCode: expectedValues.keyCode || 0,
+        location: 0,
+        repeat: expectedValues.repeat || false,
+        value: gCurrentValue,
+        shift: false,
+        capsLock: false,
+        control: false,
+        alt: false
+      });
+
+      assertEventDetail(expectedEventDetail, testName);
+      gEventDetails = [];
+    }, (e) => {
+      ok(false, testName + ' should not reject. ' + e);
+    });
+
+  return promise;
+}
+
+function runKeyDownUpTests() {
+  gTestDescription = 'runKeyDownUpTests(): ';
+  var promiseQueue = Promise.resolve();
+
+  let chr = 'a';
+  let code = 'KeyA';
+  let keyCode = KeyboardEvent.DOM_VK_A;
+
+  promiseQueue = promiseQueue
+    .then(() => {
+      return keydownAndAssertResult({
+        dict: {
+          key: chr,
+          code: code,
+          keyCode: keyCode
+        },
+        expectedKeypress: true,
+        expectedInput: chr,
+        expectedValues: {
+          key: chr, code: code,
+          keyCode: keyCode,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    })
+    .then(() => {
+      return keyupAndAssertResult({
+        dict: {
+          key: chr,
+          code: code,
+          keyCode: keyCode
+        },
+        expectedValues: {
+          key: chr, code: code,
+          keyCode: keyCode,
+          charCode: chr.charCodeAt(0)
+        }
+      });
+    });
+
+  return promiseQueue;
+}
+
+function runKeyDownUpRejectionTests() {
+  gTestDescription = 'runKeyDownUpRejectionTests(): ';
+  var promiseQueue = Promise.resolve();
+
+  promiseQueue = promiseQueue.then(() => {
+    return keydownAndAssertResult({
+      dict: undefined,
+      expectedReject: TypeError
+    });
+  });
+
+  promiseQueue = promiseQueue.then(() => {
+    return keyupAndAssertResult({
+      dict: undefined,
+      expectedReject: TypeError
+    });
+  });
+
+  return promiseQueue;
+}
+
+function runRepeatTests() {
+  gTestDescription = 'runRepeatTests(): ';
+  var promiseQueue = Promise.resolve();
+
+  // Test repeat
+  promiseQueue = promiseQueue
+    .then(() => {
+      return sendKeyAndAssertResult({
+        dict: {
+          key: 'A',
+          repeat: true
+        },
+        expectedKeypress: true,
+        expectedRepeat: true,
+        expectedInput: 'A',
+        expectedValues: {
+          repeat: true,
+          key: 'A', code: '',
+          keyCode: KeyboardEvent.DOM_VK_A,
+          charCode: 'A'.charCodeAt(0)
+        }
+      });
+    })
+    .then(() => {
+      return keyupAndAssertResult({
+        dict: {
+          key: 'A'
+        },
+        expectedKeypress: true,
+        expectedRepeat: true,
+        expectedInput: 'A',
+        expectedValues: {
+          key: 'A', code: '',
+          keyCode: KeyboardEvent.DOM_VK_A,
+          charCode: 'A'.charCodeAt(0)
+        }
+      });
+    });
+
+  return promiseQueue;
+}
+
+function runTest() {
+  let im = navigator.mozInputMethod;
+
+  // Set current page as an input method.
+  SpecialPowers.wrap(im).setActive(true);
+
+  let iframe = document.createElement('iframe');
+  iframe.src = 'data:text/html,<html><body><textarea rows=30 cols=30></textarea></body></html>';
+  iframe.setAttribute('mozbrowser', true);
+  document.body.appendChild(iframe);
+
+  let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+
+  iframe.addEventListener('mozbrowserloadend', function() {
+    mm.addMessageListener('test:eventDetail', function(msg) {
+      gEventDetails.push(msg.data);
+    });
+    mm.loadFrameScript('data:,(' + encodeURIComponent(appFrameScript.toString()) + ')();', false);
+  });
+
+  waitForInputContextChange()
+    .then(() => {
+      var inputcontext = navigator.mozInputMethod.inputcontext;
+
+      ok(!!inputcontext, 'Receving the first input context');
+    })
+    .then(() => runSendKeyAlphabetTests())
+    .then(() => runSendKeyNumberTests())
+    .then(() => runSendKeyDvorakTests())
+    .then(() => runSendKeyDigitKeySymbolsTests())
+    .then(() => runSendKeyUSKeyboardSymbolsTests())
+    .then(() => runSendKeyGreekLettersTests())
+    .then(() => runSendKeyEnterTests())
+    .then(() => runSendKeyRejectionTests())
+    .then(() => runCompositionWithKeyEventTests())
+    .then(() => runCompositionWithoutKeyEventTests())
+    .then(() => runKeyDownUpTests())
+    .then(() => runKeyDownUpRejectionTests())
+    .then(() => runRepeatTests())
+    .catch((err) => {
+      console.error(err);
+      is(false, err.message);
+    })
+    .then(() => {
+      var p = waitForInputContextChange();
+
+      // Revoke our right from using the IM API.
+      SpecialPowers.wrap(im).setActive(false);
+
+      return p;
+    })
+    .then(() => {
+      var inputcontext = navigator.mozInputMethod.inputcontext;
+
+      is(inputcontext, null, 'Receving null input context');
+
+      inputmethod_cleanup();
+    })
+    .catch((err) => {
+      console.error(err);
+      is(false, err.message);
+    });
+}
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/interfaces/base/nsITextInputProcessor.idl
+++ b/dom/interfaces/base/nsITextInputProcessor.idl
@@ -249,17 +249,17 @@ interface nsITextInputProcessorCallback;
  *
  *   // keyup of one of shift key doesn't cause inactivating "Shift" state.
  *   TIP.keyup(rightShift);
  *
  *   // This causes inactivating "Shift" state completely.
  *   TIP.keyup(leftShift);
  */
 
-[scriptable, builtinclass, uuid(6617a9f6-3e16-4086-9e1e-c8a6c5d505c7)]
+[scriptable, builtinclass, uuid(581f6619-76ed-478c-905d-b8e6553a714a)]
 interface nsITextInputProcessor : nsISupports
 {
   /**
    * Returns true if this instance was dispatched compositionstart but hasn't
    * dispatched compositionend yet.
    */
   readonly attribute boolean hasComposition;
 
@@ -484,16 +484,22 @@ interface nsITextInputProcessor : nsISup
   // non-zero value, this flag causes throwing an exception.
   const unsigned long KEY_KEEP_KEYCODE_ZERO                        = 0x00000010;
   // If KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT is specified when the key event is
   // a modifier key's, keydown() and keyup() only modifies its modifier state
   // without dispatching key events.  This is useful for testing odd behavior
   // or emulating legacy API behavior.
   const unsigned long KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT         = 0x00000020;
 
+  // These values can be used to do bitwise operation with the return value of
+  // the keydown() method.
+  const unsigned long KEYEVENT_NOT_CONSUMED                        = 0x00000000;
+  const unsigned long KEYDOWN_IS_CONSUMED                          = 0x00000001;
+  const unsigned long KEYPRESS_IS_CONSUMED                         = 0x00000002;
+
   /**
    * keydown() may dispatch a keydown event and some keypress events if
    * preceding keydown event isn't consumed and they are necessary.
    * Note that even if this is called during composition, key events may not
    * be dispatched.  In this case, this returns false.
    *
    * You should initialize at least .key value and .code value of the event.
    * Additionally, if you try to emulate a printable key, .keyCode value should
@@ -524,23 +530,30 @@ interface nsITextInputProcessor : nsISup
    *                        defined by any standards, use "" (empty string).
    *                        #4 .keyCode is guessed from .key value if the key
    *                        name is registered and .keyCode isn't initialized.
    *                        #5 modifier key states, e.g., .shiftKey, are
    *                        ignored.  Instead, modifier states are managed by
    *                        each instance and set automatically.
    * @param aKeyFlags       Special flags.  The values can be some of KEY_*
    *                        constants.
-   * @return                true if neither the keydown event or following
-   *                        keypress events is *not* consumed.
-   *                        Otherwise, i.e., preventDefault() is called, false.
+   * @return                KEYEVENT_NOT_CONSUMED, if the keydown event nor
+   *                        the following keypress event(s) are consumed.
+   *                        KEYDOWN_IS_CONSUMED, if the keydown event is
+   *                        consumed. No keypress event will be dispatched in
+   *                        this case.
+   *                        KEYPRESS_IS_CONSUMED, if the keypress event(s) is
+   *                        consumed when dispatched.
+   *                        Note that keypress event is always consumed by
+   *                        native code for the printable keys (indicating the
+   *                        default action has been taken).
    */
   [optional_argc]
-    boolean keydown(in nsIDOMKeyEvent aKeyboardEvent,
-                    [optional] in unsigned long aKeyFlags);
+    unsigned long keydown(in nsIDOMKeyEvent aKeyboardEvent,
+                          [optional] in unsigned long aKeyFlags);
 
   /**
    * Similar to keydown(), but this dispatches only a keyup event.
    */
   [optional_argc]
     boolean keyup(in nsIDOMKeyEvent aKeyboardEvent,
                   [optional] in unsigned long aKeyFlags);
 
--- a/dom/ipc/AppProcessChecker.cpp
+++ b/dom/ipc/AppProcessChecker.cpp
@@ -7,17 +7,16 @@
 #include "AppProcessChecker.h"
 #include "nsIPermissionManager.h"
 #ifdef MOZ_CHILD_PERMISSIONS
 #include "ContentParent.h"
 #include "mozIApplication.h"
 #include "mozilla/hal_sandbox/PHalParent.h"
 #include "nsIAppsService.h"
 #include "nsIPrincipal.h"
-#include "nsIScriptSecurityManager.h"
 #include "nsPrintfCString.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "TabParent.h"
 
 #include <algorithm>
 
@@ -227,31 +226,20 @@ already_AddRefed<nsIPrincipal>
 GetAppPrincipal(uint32_t aAppId)
 {
   nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
 
   nsCOMPtr<mozIApplication> app;
   nsresult rv = appsService->GetAppByLocalId(aAppId, getter_AddRefs(app));
   NS_ENSURE_SUCCESS(rv, nullptr);
 
-  nsString origin;
-  rv = app->GetOrigin(origin);
-  NS_ENSURE_SUCCESS(rv, nullptr);
-
-  nsCOMPtr<nsIURI> uri;
-  NS_NewURI(getter_AddRefs(uri), origin);
+  nsCOMPtr<nsIPrincipal> principal;
+  app->GetPrincipal(getter_AddRefs(principal));
 
-  nsCOMPtr<nsIScriptSecurityManager> secMan =
-    do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
-
-  nsCOMPtr<nsIPrincipal> appPrincipal;
-  rv = secMan->GetAppCodebasePrincipal(uri, aAppId, false,
-                                       getter_AddRefs(appPrincipal));
-  NS_ENSURE_SUCCESS(rv, nullptr);
-  return appPrincipal.forget();
+  return principal.forget();
 }
 
 uint32_t
 CheckPermission(PContentParent* aActor,
                 nsIPrincipal* aPrincipal,
                 const char* aPermission)
 {
   if (!AssertAppPrincipal(aActor, aPrincipal)) {
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1557,33 +1557,25 @@ void
 TabChild::MaybeRequestPreinitCamera()
 {
     // Check if this tab will use the `camera` permission.
     nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1");
     if (NS_WARN_IF(!appsService)) {
       return;
     }
 
-    nsString manifestUrl = EmptyString();
-    appsService->GetManifestURLByLocalId(OwnAppId(), manifestUrl);
-    nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-    if (NS_WARN_IF(!secMan)) {
-      return;
-    }
-
-    nsCOMPtr<nsIURI> uri;
-    nsresult rv = NS_NewURI(getter_AddRefs(uri), manifestUrl);
+    nsCOMPtr<mozIApplication> app;
+    nsresult rv = appsService->GetAppByLocalId(OwnAppId(), getter_AddRefs(app));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
 
     nsCOMPtr<nsIPrincipal> principal;
-    rv = secMan->GetAppCodebasePrincipal(uri, OwnAppId(), false,
-                                         getter_AddRefs(principal));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
+    app->GetPrincipal(getter_AddRefs(principal));
+    if (NS_WARN_IF(!principal)) {
       return;
     }
 
     uint16_t status = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
     principal->GetAppStatus(&status);
     bool isCertified = status == nsIPrincipal::APP_STATUS_CERTIFIED;
     if (!isCertified) {
       return;
--- a/dom/media/DecodedStream.cpp
+++ b/dom/media/DecodedStream.cpp
@@ -349,39 +349,43 @@ OutputStreamManager::Disconnect()
   for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
     if (!mStreams[i].Disconnect()) {
       // Probably the DOMMediaStream was GCed. Clean up.
       mStreams.RemoveElementAt(i);
     }
   }
 }
 
-DecodedStream::DecodedStream(MediaQueue<MediaData>& aAudioQueue,
+DecodedStream::DecodedStream(AbstractThread* aOwnerThread,
+                             MediaQueue<MediaData>& aAudioQueue,
                              MediaQueue<MediaData>& aVideoQueue)
-  : mMonitor("DecodedStream::mMonitor")
+  : mOwnerThread(aOwnerThread)
+  , mMonitor("DecodedStream::mMonitor")
   , mPlaying(false)
   , mVolume(1.0)
   , mAudioQueue(aAudioQueue)
   , mVideoQueue(aVideoQueue)
 {
 }
 
 DecodedStream::~DecodedStream()
 {
   MOZ_ASSERT(mStartTime.isNothing(), "playback should've ended.");
 }
 
 nsRefPtr<GenericPromise>
 DecodedStream::StartPlayback(int64_t aStartTime, const MediaInfo& aInfo)
 {
+  AssertOwnerThread();
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   MOZ_ASSERT(mStartTime.isNothing(), "playback already started.");
 
   mStartTime.emplace(aStartTime);
   mInfo = aInfo;
+  ConnectListener();
 
   class R : public nsRunnable {
     typedef MozPromiseHolder<GenericPromise> Promise;
     typedef void(DecodedStream::*Method)(Promise&&);
   public:
     R(DecodedStream* aThis, Method aMethod, Promise&& aPromise)
       : mThis(aThis), mMethod(aMethod)
     {
@@ -403,22 +407,25 @@ DecodedStream::StartPlayback(int64_t aSt
   nsCOMPtr<nsIRunnable> r = new R(this, &DecodedStream::CreateData, Move(promise));
   AbstractThread::MainThread()->Dispatch(r.forget());
 
   return rv.forget();
 }
 
 void DecodedStream::StopPlayback()
 {
+  AssertOwnerThread();
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   // Playback didn't even start at all.
   if (mStartTime.isNothing()) {
     return;
   }
+
   mStartTime.reset();
+  DisconnectListener();
 
   // Clear mData immediately when this playback session ends so we won't
   // send data to the wrong stream in SendData() in next playback session.
   DecodedStreamData* data = mData.release();
   // mData is not yet created on the main thread.
   if (!data) {
     return;
   }
@@ -446,16 +453,29 @@ DecodedStream::CreateData(MozPromiseHold
     // Resolve the promise to indicate the end of playback.
     aPromise.Resolve(true, __func__);
     return;
   }
 
   auto source = mOutputStreamManager.Graph()->CreateSourceStream(nullptr);
   mData.reset(new DecodedStreamData(source, mPlaying, Move(aPromise)));
   mOutputStreamManager.Connect(mData->mStream);
+
+  // Start to send data to the stream immediately
+  nsRefPtr<DecodedStream> self = this;
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
+    ReentrantMonitorAutoEnter mon(self->GetReentrantMonitor());
+    // Don't send data if playback has ended.
+    if (self->mStartTime.isSome()) {
+      self->SendData();
+    }
+  });
+  // Don't assert success because the owner thread might have begun shutdown
+  // while we are still dealing with jobs on the main thread.
+  mOwnerThread->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
 }
 
 bool
 DecodedStream::HasConsumers() const
 {
   return !mOutputStreamManager.IsEmpty();
 }
 
@@ -475,40 +495,44 @@ void
 DecodedStream::RemoveOutput(MediaStream* aStream)
 {
   mOutputStreamManager.Remove(aStream);
 }
 
 void
 DecodedStream::SetPlaying(bool aPlaying)
 {
+  AssertOwnerThread();
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mPlaying = aPlaying;
   if (mData) {
     mData->SetPlaying(aPlaying);
   }
 }
 
 void
 DecodedStream::SetVolume(double aVolume)
 {
+  AssertOwnerThread();
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mVolume = aVolume;
 }
 
 void
 DecodedStream::SetSameOrigin(bool aSameOrigin)
 {
+  AssertOwnerThread();
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mSameOrigin = aSameOrigin;
 }
 
 void
 DecodedStream::InitTracks()
 {
+  AssertOwnerThread();
   GetReentrantMonitor().AssertCurrentThreadIn();
 
   if (mData->mStreamInitialized) {
     return;
   }
 
   SourceMediaStream* sourceStream = mData->mStream;
 
@@ -579,16 +603,17 @@ SendStreamAudio(DecodedStreamData* aStre
   aOutput->ApplyVolume(aVolume);
 
   aStream->mNextAudioTime = audio->GetEndTime();
 }
 
 void
 DecodedStream::SendAudio(double aVolume, bool aIsSameOrigin)
 {
+  AssertOwnerThread();
   GetReentrantMonitor().AssertCurrentThreadIn();
 
   if (!mInfo.HasAudio()) {
     return;
   }
 
   AudioSegment output;
   uint32_t rate = mInfo.mAudio.mRate;
@@ -644,16 +669,17 @@ ZeroDurationAtLastChunk(VideoSegment& aI
   StreamTime lastVideoStratTime;
   aInput.GetLastFrame(&lastVideoStratTime);
   return lastVideoStratTime == aInput.GetDuration();
 }
 
 void
 DecodedStream::SendVideo(bool aIsSameOrigin)
 {
+  AssertOwnerThread();
   GetReentrantMonitor().AssertCurrentThreadIn();
 
   if (!mInfo.HasVideo()) {
     return;
   }
 
   VideoSegment output;
   TrackID videoTrackId = mInfo.mVideo.mTrackId;
@@ -722,16 +748,17 @@ DecodedStream::SendVideo(bool aIsSameOri
     sourceStream->EndTrack(videoTrackId);
     mData->mHaveSentFinishVideo = true;
   }
 }
 
 void
 DecodedStream::AdvanceTracks()
 {
+  AssertOwnerThread();
   GetReentrantMonitor().AssertCurrentThreadIn();
 
   StreamTime endPosition = 0;
 
   if (mInfo.HasAudio()) {
     StreamTime audioEnd = mData->mStream->TicksToTimeRoundDown(
         mInfo.mAudio.mRate, mData->mAudioFramesWritten);
     endPosition = std::max(endPosition, audioEnd);
@@ -746,16 +773,17 @@ DecodedStream::AdvanceTracks()
   if (!mData->mHaveSentFinish) {
     mData->mStream->AdvanceKnownTracksTime(endPosition);
   }
 }
 
 void
 DecodedStream::SendData()
 {
+  AssertOwnerThread();
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   MOZ_ASSERT(mStartTime.isSome(), "Must be called after StartPlayback()");
 
   // Not yet created on the main thread. MDSM will try again later.
   if (!mData) {
     return;
   }
 
@@ -776,37 +804,68 @@ DecodedStream::SendData()
     mData->mHaveSentFinish = true;
     mData->mStream->Finish();
   }
 }
 
 int64_t
 DecodedStream::AudioEndTime() const
 {
+  AssertOwnerThread();
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   if (mStartTime.isSome() && mInfo.HasAudio() && mData) {
     CheckedInt64 t = mStartTime.ref() +
       FramesToUsecs(mData->mAudioFramesWritten, mInfo.mAudio.mRate);
     if (t.isValid()) {
       return t.value();
     }
   }
   return -1;
 }
 
 int64_t
 DecodedStream::GetPosition() const
 {
+  AssertOwnerThread();
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   // This is only called after MDSM starts playback. So mStartTime is
   // guaranteed to be something.
   MOZ_ASSERT(mStartTime.isSome());
   return mStartTime.ref() + (mData ? mData->GetPosition() : 0);
 }
 
 bool
 DecodedStream::IsFinished() const
 {
+  AssertOwnerThread();
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   return mData && mData->IsFinished();
 }
 
+void
+DecodedStream::ConnectListener()
+{
+  AssertOwnerThread();
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+
+  mAudioPushListener = mAudioQueue.PushEvent().Connect(
+    mOwnerThread, this, &DecodedStream::SendData);
+  mAudioFinishListener = mAudioQueue.FinishEvent().Connect(
+    mOwnerThread, this, &DecodedStream::SendData);
+  mVideoPushListener = mVideoQueue.PushEvent().Connect(
+    mOwnerThread, this, &DecodedStream::SendData);
+  mVideoFinishListener = mVideoQueue.FinishEvent().Connect(
+    mOwnerThread, this, &DecodedStream::SendData);
+}
+
+void
+DecodedStream::DisconnectListener()
+{
+  AssertOwnerThread();
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+
+  mAudioPushListener.Disconnect();
+  mVideoPushListener.Disconnect();
+  mAudioFinishListener.Disconnect();
+  mVideoFinishListener.Disconnect();
+}
+
 } // namespace mozilla
--- a/dom/media/DecodedStream.h
+++ b/dom/media/DecodedStream.h
@@ -3,18 +3,20 @@
 /* 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 DecodedStream_h_
 #define DecodedStream_h_
 
 #include "nsTArray.h"
+#include "MediaEventSource.h"
 #include "MediaInfo.h"
 
+#include "mozilla/AbstractThread.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/nsRefPtr.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/gfx/Point.h"
 
@@ -95,17 +97,18 @@ private:
   // are added after Connect().
   nsRefPtr<MediaStream> mInputStream;
   nsTArray<OutputStreamData> mStreams;
 };
 
 class DecodedStream {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodedStream);
 public:
-  DecodedStream(MediaQueue<MediaData>& aAudioQueue,
+  DecodedStream(AbstractThread* aOwnerThread,
+                MediaQueue<MediaData>& aAudioQueue,
                 MediaQueue<MediaData>& aVideoQueue);
 
   // Mimic MDSM::StartAudioThread.
   // Must be called before any calls to SendData().
   //
   // Return a promise which will be resolved when the stream is finished
   // or rejected if any error.
   nsRefPtr<GenericPromise> StartPlayback(int64_t aStartTime,
@@ -120,28 +123,36 @@ public:
   void SetVolume(double aVolume);
   void SetSameOrigin(bool aSameOrigin);
 
   int64_t AudioEndTime() const;
   int64_t GetPosition() const;
   bool IsFinished() const;
   bool HasConsumers() const;
 
-  void SendData();
-
 protected:
   virtual ~DecodedStream();
 
 private:
   ReentrantMonitor& GetReentrantMonitor() const;
   void CreateData(MozPromiseHolder<GenericPromise>&& aPromise);
   void InitTracks();
   void AdvanceTracks();
   void SendAudio(double aVolume, bool aIsSameOrigin);
   void SendVideo(bool aIsSameOrigin);
+  void SendData();
+
+  void AssertOwnerThread() const {
+    MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+  }
+
+  void ConnectListener();
+  void DisconnectListener();
+
+  const nsRefPtr<AbstractThread> mOwnerThread;
 
   UniquePtr<DecodedStreamData> mData;
   // Data about MediaStreams that are being fed by the decoder.
   OutputStreamManager mOutputStreamManager;
 
   // TODO: This is a temp solution to get rid of decoder monitor on the main
   // thread in MDSM::AddOutputStream and MDSM::RecreateDecodedStream as
   // required by bug 1146482. DecodedStream needs to release monitor before
@@ -155,13 +166,18 @@ private:
   double mVolume;
   bool mSameOrigin;
 
   Maybe<int64_t> mStartTime;
   MediaInfo mInfo;
 
   MediaQueue<MediaData>& mAudioQueue;
   MediaQueue<MediaData>& mVideoQueue;
+
+  MediaEventListener mAudioPushListener;
+  MediaEventListener mVideoPushListener;
+  MediaEventListener mAudioFinishListener;
+  MediaEventListener mVideoFinishListener;
 };
 
 } // namespace mozilla
 
 #endif // DecodedStream_h_
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -216,17 +216,17 @@ MediaDecoderStateMachine::MediaDecoderSt
   mDropVideoUntilNextDiscontinuity(false),
   mDecodeToSeekTarget(false),
   mCurrentTimeBeforeSeek(0),
   mCorruptFrames(30),
   mDecodingFirstFrame(true),
   mSentLoadedMetadataEvent(false),
   mSentFirstFrameLoadedEvent(false),
   mSentPlaybackEndedEvent(false),
-  mDecodedStream(new DecodedStream(mAudioQueue, mVideoQueue)),
+  mDecodedStream(new DecodedStream(mTaskQueue, mAudioQueue, mVideoQueue)),
   mResource(aDecoder->GetResource()),
   mBuffered(mTaskQueue, TimeIntervals(),
             "MediaDecoderStateMachine::mBuffered (Mirror)"),
   mEstimatedDuration(mTaskQueue, NullableTimeUnit(),
                     "MediaDecoderStateMachine::mEstimatedDuration (Mirror)"),
   mExplicitDuration(mTaskQueue, Maybe<double>(),
                     "MediaDecoderStateMachine::mExplicitDuration (Mirror)"),
   mPlayState(mTaskQueue, MediaDecoder::PLAY_STATE_LOADING,
@@ -364,24 +364,22 @@ int64_t MediaDecoderStateMachine::GetDec
   AssertCurrentThreadInMonitor();
   int64_t audioDecoded = AudioQueue().Duration();
   if (AudioEndTime() != -1) {
     audioDecoded += AudioEndTime() - GetMediaTime();
   }
   return audioDecoded;
 }
 
-void MediaDecoderStateMachine::SendStreamData()
+void MediaDecoderStateMachine::DiscardStreamData()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
 
-  mDecodedStream->SendData();
-
   const auto clockTime = GetClock();
   while (true) {
     const MediaData* a = AudioQueue().PeekFront();
 
     // If we discard audio samples fed to the stream immediately, we will
     // keep decoding audio samples till the end and consume a lot of memory.
     // Therefore we only discard those behind the stream clock to throttle
     // the decoding speed.
@@ -563,20 +561,16 @@ MediaDecoderStateMachine::OnAudioDecoded
     case DECODER_STATE_DECODING: {
       Push(audio);
       if (MaybeFinishDecodeFirstFrame()) {
         return;
       }
       if (mIsAudioPrerolling && DonePrerollingAudio()) {
         StopPrerollingAudio();
       }
-      // Schedule the state machine to send stream data as soon as possible.
-      if (mAudioCaptured) {
-        ScheduleStateMachine();
-      }
       return;
     }
 
     case DECODER_STATE_SEEKING: {
       if (!mCurrentSeek.Exists()) {
         // We've received a sample from a previous decode. Discard it.
         return;
       }
@@ -762,20 +756,16 @@ MediaDecoderStateMachine::OnNotDecoded(M
   }
   switch (mState) {
     case DECODER_STATE_BUFFERING:
     case DECODER_STATE_DECODING: {
       if (MaybeFinishDecodeFirstFrame()) {
         return;
       }
       CheckIfDecodeComplete();
-      // Schedule the state machine to notify track ended as soon as possible.
-      if (mAudioCaptured) {
-        ScheduleStateMachine();
-      }
       return;
     }
     case DECODER_STATE_SEEKING: {
       if (!mCurrentSeek.Exists()) {
         // We've received a sample from a previous decode. Discard it.
         return;
       }
 
@@ -852,17 +842,17 @@ MediaDecoderStateMachine::OnVideoDecoded
 
       // Schedule the state machine to send stream data as soon as possible or
       // the VideoQueue() is empty before the Push().
       // VideoQueue() is empty implies the state machine thread doesn't have
       // precise time information about video frames. Once the first video
       // frame pushed in the queue, schedule the state machine as soon as
       // possible to render the video frame or delay the state machine thread
       // accurately.
-      if (mAudioCaptured || VideoQueue().GetSize() == 1) {
+      if (VideoQueue().GetSize() == 1) {
         ScheduleStateMachine();
       }
 
       // For non async readers, if the requested video sample was slow to
       // arrive, increase the amount of audio we buffer to ensure that we
       // don't run out of audio. This is unnecessary for async readers,
       // since they decode audio and video on different threads so they
       // are unlikely to run out of decoded audio.
@@ -2646,17 +2636,17 @@ void MediaDecoderStateMachine::UpdateRen
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   if (!IsPlaying() || mLogicallySeeking) {
     return;
   }
 
   if (mAudioCaptured) {
-    SendStreamData();
+    DiscardStreamData();
   }
 
   TimeStamp nowTime;
   const int64_t clockTime = GetClock(&nowTime);
   // Skip frames up to the frame at the playback position, and figure out
   // the time remaining until it's time to display the next frame and drop
   // the current frame.
   NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -318,20 +318,18 @@ public:
     MOZ_ASSERT(NS_IsMainThread());
     if (mReader) {
       mReader->BreakCycles();
     }
     mResource = nullptr;
     mDecoder = nullptr;
   }
 
-  // Copy queued audio/video data in the reader to any output MediaStreams that
-  // need it.
-  void SendStreamData();
-  void FinishStreamData();
+  // Discard audio/video data that are already played by MSG.
+  void DiscardStreamData();
   bool HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs);
   bool HaveEnoughDecodedVideo();
 
   // Returns true if the state machine has shutdown or is in the process of
   // shutting down. The decoder monitor must be held while calling this.
   bool IsShutdown();
 
   void QueueMetadata(const media::TimeUnit& aPublishTime,
--- a/dom/media/MediaEventSource.h
+++ b/dom/media/MediaEventSource.h
@@ -71,27 +71,30 @@ template <>
 struct EventTypeTraits<void> {
   typedef bool ArgType;
 };
 
 /**
  * Test if a method function or lambda accepts one or more arguments.
  */
 template <typename T>
-class TakeArgs {
+class TakeArgsHelper {
   template <typename C> static FalseType test(void(C::*)(), int);
   template <typename C> static FalseType test(void(C::*)() const, int);
   template <typename C> static FalseType test(void(C::*)() volatile, int);
   template <typename C> static FalseType test(void(C::*)() const volatile, int);
   template <typename F> static FalseType test(F&&, decltype(DeclVal<F>()(), 0));
   static TrueType test(...);
 public:
   typedef decltype(test(DeclVal<T>(), 0)) Type;
 };
 
+template <typename T>
+struct TakeArgs : public TakeArgsHelper<T>::Type {};
+
 template <typename T> struct EventTarget;
 
 template <>
 struct EventTarget<nsIEventTarget> {
   static void
   Dispatch(nsIEventTarget* aTarget, already_AddRefed<nsIRunnable>&& aTask) {
     aTarget->Dispatch(Move(aTask), NS_DISPATCH_NORMAL);
   }
@@ -113,16 +116,203 @@ template <typename T>
 class RawPtr {
 public:
   explicit RawPtr(T* aPtr) : mPtr(aPtr) {}
   T* get() const { return mPtr; }
 private:
   T* const mPtr;
 };
 
+/**
+ * A helper class to pass event data to the listeners. Optimized to save
+ * copy when Move is possible or |Function| takes no arguments.
+ */
+template<typename Target, typename Function>
+class ListenerHelper {
+  // Define our custom runnable to minimize copy of the event data.
+  // NS_NewRunnableFunction will result in 2 copies of the event data.
+  // One is captured by the lambda and the other is the copy of the lambda.
+  template <typename T>
+  class R : public nsRunnable {
+    typedef typename RemoveCV<typename RemoveReference<T>::Type>::Type ArgType;
+  public:
+    template <typename U>
+    R(RevocableToken* aToken, const Function& aFunction, U&& aEvent)
+      : mToken(aToken), mFunction(aFunction), mEvent(Forward<U>(aEvent)) {}
+
+    NS_IMETHOD Run() override {
+      // Don't call the listener if it is disconnected.
+      if (!mToken->IsRevoked()) {
+        // Enable move whenever possible since mEvent won't be used anymore.
+        mFunction(Move(mEvent));
+      }
+      return NS_OK;
+    }
+
+  private:
+    nsRefPtr<RevocableToken> mToken;
+    Function mFunction;
+    ArgType mEvent;
+  };
+
+public:
+  ListenerHelper(RevocableToken* aToken, Target* aTarget, const Function& aFunc)
+    : mToken(aToken), mTarget(aTarget), mFunction(aFunc) {}
+
+  // |F| takes one argument.
+  template <typename F, typename T>
+  typename EnableIf<TakeArgs<F>::value, void>::Type
+  Dispatch(const F& aFunc, T&& aEvent) {
+    nsCOMPtr<nsIRunnable> r = new R<T>(mToken, aFunc, Forward<T>(aEvent));
+    EventTarget<Target>::Dispatch(mTarget.get(), r.forget());
+  }
+
+  // |F| takes no arguments. Don't bother passing aEvent.
+  template <typename F, typename T>
+  typename EnableIf<!TakeArgs<F>::value, void>::Type
+  Dispatch(const F& aFunc, T&&) {
+    const nsRefPtr<RevocableToken>& token = mToken;
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
+      // Don't call the listener if it is disconnected.
+      if (!token->IsRevoked()) {
+        aFunc();
+      }
+    });
+    EventTarget<Target>::Dispatch(mTarget.get(), r.forget());
+  }
+
+  template <typename T>
+  void Dispatch(T&& aEvent) {
+    Dispatch(mFunction, Forward<T>(aEvent));
+  }
+
+private:
+  nsRefPtr<RevocableToken> mToken;
+  const nsRefPtr<Target> mTarget;
+  Function mFunction;
+};
+
+/**
+ * Define whether an event data should be copied or moved to the listeners.
+ *
+ * @Copy Data will always be copied. Each listener gets a copy.
+ * @Move Data will always be moved.
+ * @Both Data will be moved when possible or copied when necessary.
+ */
+enum class EventPassMode : int8_t {
+  Copy,
+  Move,
+  Both
+};
+
+class ListenerBase {
+public:
+  ListenerBase() : mToken(new RevocableToken()) {}
+  ~ListenerBase() {
+    MOZ_ASSERT(Token()->IsRevoked(), "Must disconnect the listener.");
+  }
+  RevocableToken* Token() const {
+    return mToken;
+  }
+private:
+  const nsRefPtr<RevocableToken> mToken;
+};
+
+/**
+ * Stored by MediaEventSource to send notifications to the listener.
+ * Since virtual methods can not be templated, this class is specialized
+ * to provide different Dispatch() overloads depending on EventPassMode.
+ */
+template <typename ArgType, EventPassMode Mode = EventPassMode::Copy>
+class Listener : public ListenerBase {
+public:
+  virtual ~Listener() {}
+  virtual void Dispatch(const ArgType& aEvent) = 0;
+};
+
+template <typename ArgType>
+class Listener<ArgType, EventPassMode::Both> : public ListenerBase {
+public:
+  virtual ~Listener() {}
+  virtual void Dispatch(const ArgType& aEvent) = 0;
+  virtual void Dispatch(ArgType&& aEvent) = 0;
+};
+
+template <typename ArgType>
+class Listener<ArgType, EventPassMode::Move> : public ListenerBase {
+public:
+  virtual ~Listener() {}
+  virtual void Dispatch(ArgType&& aEvent) = 0;
+};
+
+/**
+ * Store the registered target thread and function so it knows where and to
+ * whom to send the event data.
+ */
+template <typename Target, typename Function, typename ArgType, EventPassMode>
+class ListenerImpl : public Listener<ArgType, EventPassMode::Copy> {
+public:
+  ListenerImpl(Target* aTarget, const Function& aFunction)
+    : mHelper(ListenerBase::Token(), aTarget, aFunction) {}
+  void Dispatch(const ArgType& aEvent) override {
+    mHelper.Dispatch(aEvent);
+  }
+private:
+  ListenerHelper<Target, Function> mHelper;
+};
+
+template <typename Target, typename Function, typename ArgType>
+class ListenerImpl<Target, Function, ArgType, EventPassMode::Both>
+  : public Listener<ArgType, EventPassMode::Both> {
+public:
+  ListenerImpl(Target* aTarget, const Function& aFunction)
+    : mHelper(ListenerBase::Token(), aTarget, aFunction) {}
+  void Dispatch(const ArgType& aEvent) override {
+    mHelper.Dispatch(aEvent);
+  }
+  void Dispatch(ArgType&& aEvent) override {
+    mHelper.Dispatch(Move(aEvent));
+  }
+private:
+  ListenerHelper<Target, Function> mHelper;
+};
+
+template <typename Target, typename Function, typename ArgType>
+class ListenerImpl<Target, Function, ArgType, EventPassMode::Move>
+  : public Listener<ArgType, EventPassMode::Move> {
+public:
+  ListenerImpl(Target* aTarget, const Function& aFunction)
+    : mHelper(ListenerBase::Token(), aTarget, aFunction) {}
+  void Dispatch(ArgType&& aEvent) override {
+    mHelper.Dispatch(Move(aEvent));
+  }
+private:
+  ListenerHelper<Target, Function> mHelper;
+};
+
+/**
+ * Select EventPassMode based on ListenerMode and if the type is copyable.
+ *
+ * @Copy Selected when ListenerMode is NonExclusive because each listener
+ * must get a copy.
+ *
+ * @Move Selected when ListenerMode is Exclusive and the type is move-only.
+ *
+ * @Both Selected when ListenerMode is Exclusive and the type is copyable.
+ * The data will be moved when possible and copied when necessary.
+ */
+template <typename ArgType, ListenerMode Mode>
+struct PassModePicker {
+  // TODO: pick EventPassMode::Both when we can detect if a type is
+  // copy-constructible to allow copy-only types in Exclusive mode.
+  static const EventPassMode Value =
+    Mode == ListenerMode::NonExclusive ?
+    EventPassMode::Copy : EventPassMode::Move;
+};
+
 } // namespace detail
 
 template <typename T, ListenerMode> class MediaEventSource;
 
 /**
  * Not thread-safe since this is not meant to be shared and therefore only
  * move constructor is provided. Used to hold the result of
  * MediaEventSource<T>::Connect() and call Disconnect() to disconnect the
@@ -162,125 +352,51 @@ private:
 
 /**
  * A generic and thread-safe class to implement the observer pattern.
  */
 template <typename EventType, ListenerMode Mode = ListenerMode::NonExclusive>
 class MediaEventSource {
   static_assert(!IsReference<EventType>::value, "Ref-type not supported!");
   typedef typename detail::EventTypeTraits<EventType>::ArgType ArgType;
-
-  /**
-   * Stored by MediaEventSource to send notifications to the listener.
-   */
-  class Listener {
-  public:
-    Listener() : mToken(new RevocableToken()) {}
-
-    virtual ~Listener() {
-      MOZ_ASSERT(Token()->IsRevoked(), "Must disconnect the listener.");
-    }
-
-    virtual void Dispatch(const ArgType& aEvent) = 0;
-
-    RevocableToken* Token() const {
-      return mToken;
-    }
-
-  private:
-    const nsRefPtr<RevocableToken> mToken;
-  };
-
-  /**
-   * Store the registered target thread and function so it knows where and to
-   * whom to send the event data.
-   */
-  template<typename Target, typename Function>
-  class ListenerImpl : public Listener {
-  public:
-    explicit ListenerImpl(Target* aTarget, const Function& aFunction)
-      : mTarget(aTarget), mFunction(aFunction) {}
+  static const detail::EventPassMode PassMode
+    = detail::PassModePicker<ArgType, Mode>::Value;
+  typedef detail::Listener<ArgType, PassMode> Listener;
 
-    // |Function| takes one argument.
-    void Dispatch(const ArgType& aEvent, TrueType) {
-      // Define our custom runnable to minimize copy of the event data.
-      // NS_NewRunnableFunction will result in 2 copies of the event data.
-      // One is captured by the lambda and the other is the copy of the lambda.
-      class R : public nsRunnable {
-      public:
-        R(RevocableToken* aToken,
-          const Function& aFunction, const ArgType& aEvent)
-          : mToken(aToken), mFunction(aFunction), mEvent(aEvent) {}
-
-        NS_IMETHOD Run() override {
-          // Don't call the listener if it is disconnected.
-          if (!mToken->IsRevoked()) {
-            // Enable move whenever possible since mEvent won't be used anymore.
-            mFunction(Move(mEvent));
-          }
-          return NS_OK;
-        }
-
-      private:
-        nsRefPtr<RevocableToken> mToken;
-        Function mFunction;
-        ArgType mEvent;
-      };
+  template<typename Target, typename Func>
+  using ListenerImpl = detail::ListenerImpl<Target, Func, ArgType, PassMode>;
 
-      nsCOMPtr<nsIRunnable> r = new R(this->Token(), mFunction, aEvent);
-      detail::EventTarget<Target>::Dispatch(mTarget.get(), r.forget());
-    }
-
-    // |Function| takes no arguments. Don't bother passing aEvent.
-    void Dispatch(const ArgType& aEvent, FalseType) {
-      nsRefPtr<RevocableToken> token = this->Token();
-      const Function& function = mFunction;
-      nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
-        // Don't call the listener if it is disconnected.
-        if (!token->IsRevoked()) {
-          function();
-        }
-      });
-      detail::EventTarget<Target>::Dispatch(mTarget.get(), r.forget());
-    }
-
-    void Dispatch(const ArgType& aEvent) override {
-      Dispatch(aEvent, typename detail::TakeArgs<Function>::Type());
-    }
-
-  private:
-    const nsRefPtr<Target> mTarget;
-    Function mFunction;
-  };
+  template <typename Method>
+  using TakeArgs = detail::TakeArgs<Method>;
 
   template<typename Target, typename Function>
   MediaEventListener
   ConnectInternal(Target* aTarget, const Function& aFunction) {
     MutexAutoLock lock(mMutex);
     MOZ_ASSERT(Mode == ListenerMode::NonExclusive || mListeners.IsEmpty());
     auto l = mListeners.AppendElement();
     l->reset(new ListenerImpl<Target, Function>(aTarget, aFunction));
     return MediaEventListener((*l)->Token());
   }
 
   // |Method| takes one argument.
   template <typename Target, typename This, typename Method>
-  MediaEventListener
-  ConnectInternal(Target* aTarget, This* aThis, Method aMethod, TrueType) {
+  typename EnableIf<TakeArgs<Method>::value, MediaEventListener>::Type
+  ConnectInternal(Target* aTarget, This* aThis, Method aMethod) {
     detail::RawPtr<This> thiz(aThis);
     auto f = [=] (ArgType&& aEvent) {
       (thiz.get()->*aMethod)(Move(aEvent));
     };
     return ConnectInternal(aTarget, f);
   }
 
   // |Method| takes no arguments. Don't bother passing the event data.
   template <typename Target, typename This, typename Method>
-  MediaEventListener
-  ConnectInternal(Target* aTarget, This* aThis, Method aMethod, FalseType) {
+  typename EnableIf<!TakeArgs<Method>::value, MediaEventListener>::Type
+  ConnectInternal(Target* aTarget, This* aThis, Method aMethod) {
     detail::RawPtr<This> thiz(aThis);
     auto f = [=] () {
       (thiz.get()->*aMethod)();
     };
     return ConnectInternal(aTarget, f);
   }
 
 public:
@@ -313,59 +429,59 @@ public:
    * cycle for the pending event could still hold a reference to |aThis|.
    *
    * The caller must call MediaEventListener::Disconnect() to avoid dangling
    * pointers.
    */
   template <typename This, typename Method>
   MediaEventListener
   Connect(AbstractThread* aTarget, This* aThis, Method aMethod) {
-    return ConnectInternal(aTarget, aThis, aMethod,
-      typename detail::TakeArgs<Method>::Type());
+    return ConnectInternal(aTarget, aThis, aMethod);
   }
 
   template <typename This, typename Method>
   MediaEventListener
   Connect(nsIEventTarget* aTarget, This* aThis, Method aMethod) {
-    return ConnectInternal(aTarget, aThis, aMethod,
-      typename detail::TakeArgs<Method>::Type());
+    return ConnectInternal(aTarget, aThis, aMethod);
   }
 
 protected:
   MediaEventSource() : mMutex("MediaEventSource::mMutex") {}
 
-  void NotifyInternal(const ArgType& aEvent) {
+  template <typename T>
+  void NotifyInternal(T&& aEvent) {
     MutexAutoLock lock(mMutex);
     for (int32_t i = mListeners.Length() - 1; i >= 0; --i) {
       auto&& l = mListeners[i];
       // Remove disconnected listeners.
       // It is not optimal but is simple and works well.
       if (l->Token()->IsRevoked()) {
         mListeners.RemoveElementAt(i);
         continue;
       }
-      l->Dispatch(aEvent);
+      l->Dispatch(Forward<T>(aEvent));
     }
   }
 
 private:
   Mutex mMutex;
   nsTArray<UniquePtr<Listener>> mListeners;
 };
 
 /**
  * A class to separate the interface of event subject (MediaEventSource)
  * and event publisher. Mostly used as a member variable to publish events
  * to the listeners.
  */
 template <typename EventType, ListenerMode Mode = ListenerMode::NonExclusive>
 class MediaEventProducer : public MediaEventSource<EventType, Mode> {
 public:
-  void Notify(const EventType& aEvent) {
-    this->NotifyInternal(aEvent);
+  template <typename T>
+  void Notify(T&& aEvent) {
+    this->NotifyInternal(Forward<T>(aEvent));
   }
 };
 
 /**
  * Specialization for void type. A dummy bool is passed to NotifyInternal
  * since there is no way to pass a void value.
  */
 template <>
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -778,16 +778,17 @@ MediaFormatReader::NotifyNewOutput(Track
        TrackTypeToStr(aTrack), aSample->mTime, aSample->mDuration);
   auto& decoder = GetDecoderData(aTrack);
   if (!decoder.mOutputRequested) {
     LOG("MediaFormatReader produced output while flushing, discarding.");
     return;
   }
   decoder.mOutput.AppendElement(aSample);
   decoder.mNumSamplesOutput++;
+  decoder.mNumSamplesOutputTotal++;
   ScheduleUpdate(aTrack);
 }
 
 void
 MediaFormatReader::NotifyInputExhausted(TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   LOGV("Decoder has requested more %s data", TrackTypeToStr(aTrack));
@@ -1115,19 +1116,19 @@ MediaFormatReader::Update(TrackType aTra
   }
 
   // Record number of frames decoded and parsed. Automatically update the
   // stats counters using the AutoNotifyDecoded stack-based class.
   AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder);
 
   if (aTrack == TrackInfo::kVideoTrack) {
     uint64_t delta =
-      decoder.mNumSamplesOutput - mLastReportedNumDecodedFrames;
+      decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames;
     a.mDecoded = static_cast<uint32_t>(delta);
-    mLastReportedNumDecodedFrames = decoder.mNumSamplesOutput;
+    mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal;
   }
 
   if (decoder.HasPromise()) {
     needOutput = true;
     if (!decoder.mOutput.IsEmpty()) {
       // We have a decoded sample ready to be returned.
       if (aTrack == TrackType::kVideoTrack) {
         nsCString error;
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -201,16 +201,17 @@ private:
       , mOutputRequested(false)
       , mInputExhausted(false)
       , mError(false)
       , mNeedDraining(false)
       , mDraining(false)
       , mDrainComplete(false)
       , mNumSamplesInput(0)
       , mNumSamplesOutput(0)
+      , mNumSamplesOutputTotal(0)
       , mSizeOfQueue(0)
       , mIsHardwareAccelerated(false)
       , mLastStreamSourceID(UINT32_MAX)
     {}
 
     MediaFormatReader* mOwner;
     // Disambiguate Audio vs Video.
     MediaData::Type mType;
@@ -258,16 +259,17 @@ private:
     // Used for internal seeking when a change of stream is detected.
     Maybe<media::TimeUnit> mTimeThreshold;
 
     // Decoded samples returned my mDecoder awaiting being returned to
     // state machine upon request.
     nsTArray<nsRefPtr<MediaData>> mOutput;
     uint64_t mNumSamplesInput;
     uint64_t mNumSamplesOutput;
+    uint64_t mNumSamplesOutputTotal;
 
     // These get overriden in the templated concrete class.
     // Indicate if we have a pending promise for decoded frame.
     virtual bool HasPromise() = 0;
     virtual void RejectPromise(MediaDecoderReader::NotDecodedReason aReason,
                                const char* aMethodName) = 0;
 
     void ResetDemuxer()
new file mode 100644
--- /dev/null
+++ b/dom/media/XiphExtradata.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "XiphExtradata.h"
+
+namespace mozilla {
+
+bool XiphHeadersToExtradata(MediaByteBuffer* aCodecSpecificConfig,
+                            const nsTArray<const unsigned char*>& aHeaders,
+                            const nsTArray<size_t>& aHeaderLens)
+{
+  size_t nheaders = aHeaders.Length();
+  if (!nheaders || nheaders > 255) return false;
+  aCodecSpecificConfig->AppendElement(nheaders - 1);
+  for (size_t i = 0; i < nheaders - 1; i++) {
+    size_t headerLen;
+    for (headerLen = aHeaderLens[i]; headerLen >= 255; headerLen -= 255) {
+      aCodecSpecificConfig->AppendElement(255);
+    }
+    aCodecSpecificConfig->AppendElement(headerLen);
+  }
+  for (size_t i = 0; i < nheaders; i++) {
+    aCodecSpecificConfig->AppendElements(aHeaders[i], aHeaderLens[i]);
+  }
+  return true;
+}
+
+bool XiphExtradataToHeaders(nsTArray<unsigned char*>& aHeaders,
+                            nsTArray<size_t>& aHeaderLens,
+                            unsigned char* aData,
+                            size_t aAvailable)
+{
+  size_t total = 0;
+  if (aAvailable < 1) {
+    return false;
+  }
+  aAvailable--;
+  int nHeaders = *aData++ + 1;
+  for (int i = 0; i < nHeaders - 1; i++) {
+    size_t headerLen = 0;
+    for (;;) {
+      // After this test, we know that (aAvailable - total > headerLen) and
+      // (headerLen >= 0) so (aAvailable - total > 0). The loop decrements
+      // aAvailable by 1 and total remains fixed, so we know that in the next
+      // iteration (aAvailable - total >= 0). Thus (aAvailable - total) can
+      // never underflow.
+      if (aAvailable - total <= headerLen) {
+        return false;
+      }
+      // Since we know (aAvailable > total + headerLen), this can't overflow
+      // unless total is near 0 and both aAvailable and headerLen are within
+      // 255 bytes of the maximum representable size. However, that is
+      // impossible, since we would have had to have gone through this loop
+      // more than 255 times to make headerLen that large, and thus decremented
+      // aAvailable more than 255 times.
+      headerLen += *aData;
+      aAvailable--;
+      if (*aData++ != 255) break;
+    }
+    // And this check ensures updating total won't cause (aAvailable - total)
+    // to underflow.
+    if (aAvailable - total < headerLen) {
+      return false;
+    }
+    aHeaderLens.AppendElement(headerLen);
+    // Since we know aAvailable >= total + headerLen, this can't overflow.
+    total += headerLen;
+  }
+  aHeaderLens.AppendElement(aAvailable);
+  for (int i = 0; i < nHeaders; i++) {
+    aHeaders.AppendElement(aData);
+    aData += aHeaderLens[i];
+  }
+  return true;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/XiphExtradata.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+#if !defined(XiphExtradata_h)
+#define XiphExtradata_h
+
+#include "MediaData.h"
+
+namespace mozilla {
+
+/* This converts a list of headers to the canonical form of extradata for Xiph
+   codecs in non-Ogg containers. We use it to pass those headers from demuxer
+   to decoder even when demuxing from an Ogg cotainer. */
+bool XiphHeadersToExtradata(MediaByteBuffer* aCodecSpecificConfig,
+                            const nsTArray<const unsigned char*>& aHeaders,
+                            const nsTArray<size_t>& aHeaderLens);
+
+/* This converts a set of extradata back into a list of headers. */
+bool XiphExtradataToHeaders(nsTArray<unsigned char*>& aHeaders,
+                            nsTArray<size_t>& aHeaderLens,
+                            unsigned char* aData,
+                            size_t aAvailable);
+
+} // namespace mozilla
+
+#endif // XiphExtradata_h
--- a/dom/media/gtest/TestMediaEventSource.cpp
+++ b/dom/media/gtest/TestMediaEventSource.cpp
@@ -1,16 +1,17 @@
 /* -*- 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 "gtest/gtest.h"
 
 #include "mozilla/TaskQueue.h"
+#include "mozilla/UniquePtr.h"
 #include "MediaEventSource.h"
 #include "VideoUtils.h"
 
 using namespace mozilla;
 
 /*
  * Test if listeners receive the event data correctly.
  */
@@ -289,8 +290,34 @@ TEST(MediaEventSource, CopyEvent2)
   source.Notify(SomeEvent(i));
 
   queue->BeginShutdown();
   queue->AwaitShutdownAndIdle();
   EXPECT_EQ(i, 0);
   listener1.Disconnect();
   listener2.Disconnect();
 }
+
+/*
+ * Test move-only types.
+ */
+TEST(MediaEventSource, MoveOnly)
+{
+  nsRefPtr<TaskQueue> queue = new TaskQueue(
+    GetMediaThreadPool(MediaThreadType::PLAYBACK));
+
+  MediaEventProducer<UniquePtr<int>, ListenerMode::Exclusive> source;
+
+  auto func = [] (UniquePtr<int>&& aEvent) {
+    EXPECT_EQ(*aEvent, 20);
+  };
+  MediaEventListener listener = source.Connect(queue, func);
+
+  // It is OK to pass an rvalue which is move-only.
+  source.Notify(UniquePtr<int>(new int(20)));
+  // It is an error to pass an lvalue which is move-only.
+  // UniquePtr<int> event(new int(30));
+  // source.Notify(event);
+
+  queue->BeginShutdown();
+  queue->AwaitShutdownAndIdle();
+  listener.Disconnect();
+}
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -6,16 +6,17 @@
 
 #include "ContainerParser.h"
 
 #include "WebMBufferedParser.h"
 #include "mozilla/Endian.h"
 #include "mozilla/ErrorResult.h"
 #include "mp4_demuxer/MoofParser.h"
 #include "mozilla/Logging.h"
+#include "mozilla/Maybe.h"
 #include "MediaData.h"
 #ifdef MOZ_FMP4
 #include "MP4Stream.h"
 #include "mp4_demuxer/AtomType.h"
 #include "mp4_demuxer/ByteReader.h"
 #endif
 #include "SourceBufferResource.h"
 
@@ -172,33 +173,36 @@ public:
       return true;
     }
     // 0x1c53bb6b // Cues
     if (aData->Length() >= 4 &&
         (*aData)[0] == 0x1c && (*aData)[1] == 0x53 && (*aData)[2] == 0xbb &&
         (*aData)[3] == 0x6b) {
       return true;
     }
-    // 0xa3 // SimpleBlock
-    if (aData->Length() >= 1 &&
-        (*aData)[0] == 0xa3) {
-      return true;
-    }
-    // 0xa1 // Block
-    if (aData->Length() >= 1 &&
-        (*aData)[0] == 0xa1) {
-      return true;
-    }
     return false;
   }
 
   bool ParseStartAndEndTimestamps(MediaByteBuffer* aData,
                                   int64_t& aStart, int64_t& aEnd) override
   {
     bool initSegment = IsInitSegmentPresent(aData);
+
+    if (mLastMapping && (initSegment || IsMediaSegmentPresent(aData))) {
+      // The last data contained a complete cluster but we can only detect it
+      // now that a new one is starting.
+      // We use mOffset as end position to ensure that any blocks not reported
+      // by WebMBufferParser are properly skipped.
+      mCompleteMediaSegmentRange = MediaByteRange(mLastMapping.ref().mSyncOffset,
+                                                  mOffset);
+      mLastMapping.reset();
+      MSE_DEBUG(WebMContainerParser, "New cluster found at start, ending previous one");
+      return false;
+    }
+
     if (initSegment) {
       mOffset = 0;
       mParser = WebMBufferedParser(0);
       mOverlappedMapping.Clear();
       mInitData = new MediaByteBuffer();
       mResource = new SourceBufferResource(NS_LITERAL_CSTRING("video/webm"));
       mCompleteMediaHeaderRange = MediaByteRange();
       mCompleteMediaSegmentRange = MediaByteRange();
@@ -237,62 +241,91 @@ public:
       mHasInitData = true;
     }
     mOffset += aData->Length();
 
     if (mapping.IsEmpty()) {
       return false;
     }
 
-    uint32_t endIdx = mapping.Length() - 1;
+    // Calculate media range for first media segment.
 
-    // Calculate media range for first media segment
-    uint32_t segmentEndIdx = endIdx;
-    while (mapping[0].mSyncOffset != mapping[segmentEndIdx].mSyncOffset) {
-      segmentEndIdx -= 1;
-    }
-    if (segmentEndIdx > 0 && mOffset >= mapping[segmentEndIdx].mEndOffset) {
-      mCompleteMediaHeaderRange = MediaByteRange(mParser.mInitEndOffset,
-                                                 mapping[0].mEndOffset);
-      mCompleteMediaSegmentRange = MediaByteRange(mParser.mInitEndOffset,
-                                                  mapping[segmentEndIdx].mEndOffset);
+    // Check if we have a cluster finishing in the current data.
+    uint32_t endIdx = mapping.Length() - 1;
+    bool foundNewCluster = false;
+    while (mapping[0].mSyncOffset != mapping[endIdx].mSyncOffset) {
+      endIdx -= 1;
+      foundNewCluster = true;
     }
 
-    // Exclude frames that we don't have enough data to cover the end of.
-    while (mOffset < mapping[endIdx].mEndOffset && endIdx > 0) {
-      endIdx -= 1;
+    int32_t completeIdx = endIdx;
+    while (completeIdx >= 0 && mOffset < mapping[completeIdx].mEndOffset) {
+      MSE_DEBUG(WebMContainerParser, "block is incomplete, missing: %lld",
+                mapping[completeIdx].mEndOffset - mOffset);
+      completeIdx -= 1;
     }
 
-    if (endIdx == 0) {
+    // Save parsed blocks for which we do not have all data yet.
+    mOverlappedMapping.AppendElements(mapping.Elements() + completeIdx + 1,
+                                      mapping.Length() - completeIdx - 1);
+
+    if (completeIdx < 0) {
+      mLastMapping.reset();
       return false;
     }
 
-    uint64_t frameDuration = mapping[endIdx].mTimecode - mapping[endIdx - 1].mTimecode;
-    aStart = mapping[0].mTimecode / NS_PER_USEC;
-    aEnd = (mapping[endIdx].mTimecode + frameDuration) / NS_PER_USEC;
+    if (mCompleteMediaHeaderRange.IsNull()) {
+      mCompleteMediaHeaderRange = MediaByteRange(mapping[0].mSyncOffset,
+                                                 mapping[0].mEndOffset);
+    }
+    mLastMapping = Some(mapping[completeIdx]);
 
-    MSE_DEBUG(WebMContainerParser, "[%lld, %lld] [fso=%lld, leo=%lld, l=%u endIdx=%u]",
-              aStart, aEnd, mapping[0].mSyncOffset, mapping[endIdx].mEndOffset, mapping.Length(), endIdx);
+    if (foundNewCluster && mOffset >= mapping[endIdx].mEndOffset) {
+      // We now have all information required to delimit a complete cluster.
+      int64_t endOffset = mapping[endIdx+1].mSyncOffset;
+      if (mapping[endIdx+1].mInitOffset > mapping[endIdx].mInitOffset) {
+        // We have a new init segment before this cluster.
+        endOffset = mapping[endIdx+1].mInitOffset;
+      }
+      mCompleteMediaSegmentRange = MediaByteRange(mapping[endIdx].mSyncOffset,
+                                                  endOffset);
+    } else if (mapping[endIdx].mClusterEndOffset >= 0 &&
+               mOffset >= mapping[endIdx].mClusterEndOffset) {
+      mCompleteMediaSegmentRange = MediaByteRange(mapping[endIdx].mSyncOffset,
+                                                  mParser.EndSegmentOffset(mapping[endIdx].mClusterEndOffset));
+    }
 
-    mapping.RemoveElementsAt(0, endIdx + 1);
-    mOverlappedMapping.AppendElements(mapping);
+    if (!completeIdx) {
+      return false;
+    }
+
+    uint64_t frameDuration =
+      mapping[completeIdx].mTimecode - mapping[completeIdx - 1].mTimecode;
+    aStart = mapping[0].mTimecode / NS_PER_USEC;
+    aEnd = (mapping[completeIdx].mTimecode + frameDuration) / NS_PER_USEC;
+
+    MSE_DEBUG(WebMContainerParser, "[%lld, %lld] [fso=%lld, leo=%lld, l=%u processedIdx=%u fs=%lld]",
+              aStart, aEnd, mapping[0].mSyncOffset,
+              mapping[completeIdx].mEndOffset, mapping.Length(), completeIdx,
+              mCompleteMediaSegmentRange.mEnd);
 
     return true;
   }
 
   int64_t GetRoundingError() override
   {
     int64_t error = mParser.GetTimecodeScale() / NS_PER_USEC;
     return error * 2;
   }
 
 private:
   WebMBufferedParser mParser;
   nsTArray<WebMTimeDataOffset> mOverlappedMapping;
   int64_t mOffset;
+  Maybe<WebMTimeDataOffset> mLastMapping;
 };
 
 #ifdef MOZ_FMP4
 class MP4ContainerParser : public ContainerParser {
 public:
   explicit MP4ContainerParser(const nsACString& aType)
     : ContainerParser(aType)
     , mMonitor("MP4ContainerParser Index Monitor")
--- a/dom/media/mediasource/SourceBufferResource.h
+++ b/dom/media/mediasource/SourceBufferResource.h
@@ -95,16 +95,21 @@ public:
   }
 
   virtual size_t SizeOfIncludingThis(
                       MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
+  virtual bool IsExpectingMoreData() override
+  {
+    return false;
+  }
+
   // Used by SourceBuffer.
   void AppendData(MediaByteBuffer* aData);
   void Ended();
   bool IsEnded()
   {
     ReentrantMonitorAutoEnter mon(mMonitor);
     return mEnded;
   }
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -92,16 +92,17 @@ private:
 
 TrackBuffersManager::TrackBuffersManager(dom::SourceBufferAttributes* aAttributes,
                                          MediaSourceDecoder* aParentDecoder,
                                          const nsACString& aType)
   : mInputBuffer(new MediaByteBuffer)
   , mAppendState(AppendState::WAITING_FOR_SEGMENT)
   , mBufferFull(false)
   , mFirstInitializationSegmentReceived(false)
+  , mNewSegmentStarted(false)
   , mActiveTrack(false)
   , mType(aType)
   , mParser(ContainerParser::CreateForMIMEType(aType))
   , mProcessedInput(0)
   , mAppendRunning(false)
   , mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue())
   , mSourceBufferAttributes(aAttributes)
   , mParentDecoder(new nsMainThreadPtrHolder<MediaSourceDecoder>(aParentDecoder, false /* strict */))
@@ -650,31 +651,33 @@ TrackBuffersManager::SegmentParserLoop()
     // steps:
     if (mAppendState == AppendState::WAITING_FOR_SEGMENT) {
       if (mParser->IsInitSegmentPresent(mInputBuffer)) {
         SetAppendState(AppendState::PARSING_INIT_SEGMENT);
         if (mFirstInitializationSegmentReceived) {
           // This is a new initialization segment. Obsolete the old one.
           RecreateParser(false);
         }
+        mNewSegmentStarted = true;
         continue;
       }
       if (mParser->IsMediaSegmentPresent(mInputBuffer)) {
         SetAppendState(AppendState::PARSING_MEDIA_SEGMENT);
+        mNewSegmentStarted = true;
         continue;
       }
       // We have neither an init segment nor a media segment, this is either
       // invalid data or not enough data to detect a segment type.
       MSE_DEBUG("Found invalid or incomplete data.");
       NeedMoreData();
       return;
     }
 
     int64_t start, end;
-    mParser->ParseStartAndEndTimestamps(mInputBuffer, start, end);
+    bool newData = mParser->ParseStartAndEndTimestamps(mInputBuffer, start, end);
     mProcessedInput += mInputBuffer->Length();
 
     // 5. If the append state equals PARSING_INIT_SEGMENT, then run the
     // following steps:
     if (mAppendState == AppendState::PARSING_INIT_SEGMENT) {
       if (mParser->InitSegmentRange().IsNull()) {
         mInputBuffer = nullptr;
         NeedMoreData();
@@ -691,16 +694,32 @@ TrackBuffersManager::SegmentParserLoop()
       }
       // 2. If the input buffer does not contain a complete media segment header yet, then jump to the need more data step below.
       if (mParser->MediaHeaderRange().IsNull()) {
         AppendDataToCurrentInputBuffer(mInputBuffer);
         mInputBuffer = nullptr;
         NeedMoreData();
         return;
       }
+
+      // We can't feed some demuxers (WebMDemuxer) with data that do not have
+      // monotonizally increasing timestamps. So we check if we have a
+      // discontinuity from the previous segment parsed.
+      // If so, recreate a new demuxer to ensure that the demuxer is only fed
+      // monotonically increasing data.
+      if (newData) {
+        if (mNewSegmentStarted && mLastParsedEndTime.isSome() &&
+            start < mLastParsedEndTime.ref().ToMicroseconds()) {
+          ResetDemuxingState();
+          return;
+        }
+        mNewSegmentStarted = false;
+        mLastParsedEndTime = Some(TimeUnit::FromMicroseconds(end));
+      }
+
       // 3. If the input buffer contains one or more complete coded frames, then run the coded frame processing algorithm.
       nsRefPtr<TrackBuffersManager> self = this;
       mProcessingRequest.Begin(CodedFrameProcessing()
           ->Then(GetTaskQueue(), __func__,
                  [self] (bool aNeedMoreData) {
                    self->mProcessingRequest.Complete();
                    if (aNeedMoreData || self->mAbort) {
                      self->NeedMoreData();
@@ -751,16 +770,17 @@ TrackBuffersManager::ShutdownDemuxers()
     mVideoTracks.mDemuxer->BreakCycles();
     mVideoTracks.mDemuxer = nullptr;
   }
   if (mAudioTracks.mDemuxer) {
     mAudioTracks.mDemuxer->BreakCycles();
     mAudioTracks.mDemuxer = nullptr;
   }
   mInputDemuxer = nullptr;
+  mLastParsedEndTime.reset();
 }
 
 void
 TrackBuffersManager::CreateDemuxerforMIMEType()
 {
   ShutdownDemuxers();
 
 #ifdef MOZ_WEBM
@@ -775,16 +795,68 @@ TrackBuffersManager::CreateDemuxerforMIM
     mInputDemuxer = new MP4Demuxer(mCurrentInputBuffer);
     return;
   }
 #endif
   NS_WARNING("Not supported (yet)");
   return;
 }
 
+// We reset the demuxer by creating a new one and initializing it.
+void
+TrackBuffersManager::ResetDemuxingState()
+{
+  MOZ_ASSERT(mParser && mParser->HasInitData());
+  RecreateParser(true);
+  mCurrentInputBuffer = new SourceBufferResource(mType);
+  // The demuxer isn't initialized yet ; we don't want to notify it
+  // that data has been appended yet ; so we simply append the init segment
+  // to the resource.
+  mCurrentInputBuffer->AppendData(mParser->InitData());
+  CreateDemuxerforMIMEType();
+  if (!mInputDemuxer) {
+    RejectAppend(NS_ERROR_FAILURE, __func__);
+    return;
+  }
+  mDemuxerInitRequest.Begin(mInputDemuxer->Init()
+                      ->Then(GetTaskQueue(), __func__,
+                             this,
+                             &TrackBuffersManager::OnDemuxerResetDone,
+                             &TrackBuffersManager::OnDemuxerInitFailed));
+}
+
+void
+TrackBuffersManager::OnDemuxerResetDone(nsresult)
+{
+  MOZ_ASSERT(OnTaskQueue());
+  MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
+  mDemuxerInitRequest.Complete();
+  if (mAbort) {
+    RejectAppend(NS_ERROR_ABORT, __func__);
+    return;
+  }
+
+  // Recreate track demuxers.
+  uint32_t numVideos = mInputDemuxer->GetNumberTracks(TrackInfo::kVideoTrack);
+  if (numVideos) {
+    // We currently only handle the first video track.
+    mVideoTracks.mDemuxer = mInputDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
+    MOZ_ASSERT(mVideoTracks.mDemuxer);
+  }
+
+  uint32_t numAudios = mInputDemuxer->GetNumberTracks(TrackInfo::kAudioTrack);
+  if (numAudios) {
+    // We currently only handle the first audio track.
+    mAudioTracks.mDemuxer = mInputDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
+    MOZ_ASSERT(mAudioTracks.mDemuxer);
+  }
+
+  SegmentParserLoop();
+}
+
 void
 TrackBuffersManager::AppendDataToCurrentInputBuffer(MediaByteBuffer* aData)
 {
   MOZ_ASSERT(mCurrentInputBuffer);
   int64_t offset = mCurrentInputBuffer->GetLength();
   mCurrentInputBuffer->AppendData(aData);
   // A MediaByteBuffer has a maximum size of 2GiB.
   mInputDemuxer->NotifyDataArrived(uint32_t(aData->Length()), offset);
@@ -1051,16 +1123,24 @@ TrackBuffersManager::CodedFrameProcessin
       // Something is not quite right with the data appended. Refuse it.
       // This would typically happen if the previous media segment was partial
       // yet a new complete media segment was added.
       return CodedFrameProcessingPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
     }
     // The mediaRange is offset by the init segment position previously added.
     uint32_t length =
       mediaRange.mEnd - (mProcessedInput - mInputBuffer->Length());
+    if (!length) {
+      // We've completed our earlier media segment and no new data is to be
+      // processed. This happens with some containers that can't detect that a
+      // media segment is ending until a new one starts.
+      nsRefPtr<CodedFrameProcessingPromise> p = mProcessingPromise.Ensure(__func__);
+      CompleteCodedFrameProcessing();
+      return p;
+    }
     nsRefPtr<MediaByteBuffer> segment = new MediaByteBuffer;
     if (!segment->AppendElements(mInputBuffer->Elements(), length, fallible)) {
       return CodedFrameProcessingPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
     }
     AppendDataToCurrentInputBuffer(segment);
     mInputBuffer->RemoveElementsAt(0, length);
   }
 
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -108,16 +108,17 @@ private:
   // All following functions run on the taskqueue.
   nsRefPtr<AppendPromise> InitSegmentParserLoop();
   void ScheduleSegmentParserLoop();
   void SegmentParserLoop();
   void AppendIncomingBuffers();
   void InitializationSegmentReceived();
   void ShutdownDemuxers();
   void CreateDemuxerforMIMEType();
+  void ResetDemuxingState();
   void NeedMoreData();
   void RejectAppend(nsresult aRejectValue, const char* aName);
   // Will return a promise that will be resolved once all frames of the current
   // media segment have been processed.
   nsRefPtr<CodedFrameProcessingPromise> CodedFrameProcessing();
   void CompleteCodedFrameProcessing();
   // Called by ResetParserState. Complete parsing the input buffer for the
   // current media segment.
@@ -146,16 +147,18 @@ private:
   // The current append state as per https://w3c.github.io/media-source/#sourcebuffer-append-state
   // Accessed on both the main thread and the task queue.
   Atomic<AppendState> mAppendState;
   // Buffer full flag as per https://w3c.github.io/media-source/#sourcebuffer-buffer-full-flag.
   // Accessed on both the main thread and the task queue.
   // TODO: Unused for now.
   Atomic<bool> mBufferFull;
   bool mFirstInitializationSegmentReceived;
+  // Set to true once a new segment is started.
+  bool mNewSegmentStarted;
   bool mActiveTrack;
   Maybe<media::TimeUnit> mGroupStartTimestamp;
   media::TimeUnit mGroupEndTimestamp;
   nsCString mType;
 
   // ContainerParser objects and methods.
   // Those are used to parse the incoming input buffer.
 
@@ -166,19 +169,21 @@ private:
 
   // Demuxer objects and methods.
   void AppendDataToCurrentInputBuffer(MediaByteBuffer* aData);
   nsRefPtr<MediaByteBuffer> mInitData;
   nsRefPtr<SourceBufferResource> mCurrentInputBuffer;
   nsRefPtr<MediaDataDemuxer> mInputDemuxer;
   // Length already processed in current media segment.
   uint32_t mProcessedInput;
+  Maybe<media::TimeUnit> mLastParsedEndTime;
 
   void OnDemuxerInitDone(nsresult);
   void OnDemuxerInitFailed(DemuxerFailureReason aFailure);
+  void OnDemuxerResetDone(nsresult);
   MozPromiseRequestHolder<MediaDataDemuxer::InitPromise> mDemuxerInitRequest;
   bool mEncrypted;
 
   void OnDemuxFailed(TrackType aTrack, DemuxerFailureReason aFailure);
   void DoDemuxVideo();
   void OnVideoDemuxCompleted(nsRefPtr<MediaTrackDemuxer::SamplesHolder> aSamples);
   void OnVideoDemuxFailed(DemuxerFailureReason aFailure)
   {
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -144,16 +144,17 @@ EXPORTS += [
     'ThreadPoolCOMListener.h',
     'TimeUnits.h',
     'TimeVarying.h',
     'TrackUnionStream.h',
     'VideoFrameContainer.h',
     'VideoSegment.h',
     'VideoUtils.h',
     'VorbisUtils.h',
+    'XiphExtradata.h',
 ]
 
 EXPORTS.mozilla += [
     'MediaManager.h',
 ]
 
 EXPORTS.mozilla.media.webrtc += [
     'webrtc/WebrtcGlobal.h',
@@ -240,16 +241,17 @@ UNIFIED_SOURCES += [
     'VideoFrameContainer.cpp',
     'VideoPlaybackQuality.cpp',
     'VideoSegment.cpp',
     'VideoStreamTrack.cpp',
     'VideoTrack.cpp',
     'VideoTrackList.cpp',
     'VideoUtils.cpp',
     'WebVTTListener.cpp',
+    'XiphExtradata.cpp',
 ]
 
 if CONFIG['OS_TARGET'] == 'WINNT':
   SOURCES += [ 'ThreadPoolCOMListener.cpp' ]
 
 if CONFIG['MOZ_B2G']:
     SOURCES += [
         'MediaPermissionGonk.cpp',
--- a/dom/media/platforms/agnostic/VorbisDecoder.cpp
+++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "VorbisDecoder.h"
 #include "VorbisUtils.h"
+#include "XiphExtradata.h"
 
 #include "mozilla/PodOperations.h"
 #include "nsAutoPtr.h"
 
 #undef LOG
 #define LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
 
 namespace mozilla {
@@ -66,33 +67,27 @@ VorbisDataDecoder::Shutdown()
 nsRefPtr<MediaDataDecoder::InitPromise>
 VorbisDataDecoder::Init()
 {
   vorbis_info_init(&mVorbisInfo);
   vorbis_comment_init(&mVorbisComment);
   PodZero(&mVorbisDsp);
   PodZero(&mVorbisBlock);
 
-  size_t available = mInfo.mCodecSpecificConfig->Length();
-  uint8_t *p = mInfo.mCodecSpecificConfig->Elements();
-  for(int i = 0; i < 3; i++) {
-    if (available < 2) {
+  nsAutoTArray<unsigned char*,4> headers;
+  nsAutoTArray<size_t,4> headerLens;
+  if (!XiphExtradataToHeaders(headers, headerLens,
+                              mInfo.mCodecSpecificConfig->Elements(),
+                              mInfo.mCodecSpecificConfig->Length())) {
+    return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
+  }
+  for (size_t i = 0; i < headers.Length(); i++) {
+    if (NS_FAILED(DecodeHeader(headers[i], headerLens[i]))) {
       return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
     }
-    available -= 2;
-    size_t length = BigEndian::readUint16(p);
-    p += 2;
-    if (available < length) {
-      return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
-    }
-    available -= length;
-    if (NS_FAILED(DecodeHeader((const unsigned char*)p, length))) {
-      return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
-    }
-    p += length;
   }
 
   MOZ_ASSERT(mPacketCount == 3);
 
   int r = vorbis_synthesis_init(&mVorbisDsp, &mVorbisInfo);
   if (r) {
     return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
   }
--- a/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
@@ -84,17 +84,17 @@ GonkVideoDecoderManager::Init(MediaDataD
   if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
     GVDM_LOG("It is not a valid region");
     return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
   }
 
   mReaderCallback = aCallback;
 
   mReaderTaskQueue = AbstractThread::GetCurrent()->AsTaskQueue();
-  MOZ_ASSERT(!mReaderTaskQueue);
+  MOZ_ASSERT(mReaderTaskQueue);
 
   if (mLooper.get() != nullptr) {
     return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
   }
   // Create ALooper
   mLooper = new ALooper;
   mManagerLooper = new ALooper;
   mManagerLooper->setName("GonkVideoDecoderManager");
--- a/dom/media/webm/WebMBufferedParser.cpp
+++ b/dom/media/webm/WebMBufferedParser.cpp
@@ -29,16 +29,17 @@ VIntLength(unsigned char aFirstByte, uin
   NS_ASSERTION(count >= 1 && count <= 8, "Insane VInt length.");
   return count;
 }
 
 void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
                                 nsTArray<WebMTimeDataOffset>& aMapping,
                                 ReentrantMonitor& aReentrantMonitor)
 {
+  static const uint32_t EBML_ID = 0x1a45dfa3;
   static const uint32_t SEGMENT_ID = 0x18538067;
   static const uint32_t SEGINFO_ID = 0x1549a966;
   static const uint32_t TRACKS_ID = 0x1654AE6B;
   static const uint32_t CLUSTER_ID = 0x1f43b675;
   static const uint32_t TIMECODESCALE_ID = 0x2ad7b1;
   static const unsigned char TIMECODE_ID = 0xe7;
   static const unsigned char BLOCK_ID = 0xa1;
   static const unsigned char SIMPLEBLOCK_ID = 0xa3;
@@ -97,16 +98,22 @@ void WebMBufferedParser::Append(const un
         mVInt = VInt();
         mVIntLeft = mElement.mSize.mValue;
         mState = READ_VINT_REST;
         mNextState = READ_TIMECODESCALE;
         break;
       case CLUSTER_ID:
         mClusterOffset = mCurrentOffset + (p - aBuffer) -
                         (mElement.mID.mLength + mElement.mSize.mLength);
+        // Handle "unknown" length;
+        if (mElement.mSize.mValue + 1 != uint64_t(1) << (mElement.mSize.mLength * 7)) {
+          mClusterEndOffset = mClusterOffset + mElement.mID.mLength + mElement.mSize.mLength + mElement.mSize.mValue;
+        } else {
+          mClusterEndOffset = -1;
+        }
         mState = READ_ELEMENT_ID;
         break;
       case SIMPLEBLOCK_ID:
         /* FALLTHROUGH */
       case BLOCK_ID:
         mBlockSize = mElement.mSize.mValue;
         mBlockTimecode = 0;
         mBlockTimecodeLength = BLOCK_TIMECODE_LENGTH;
@@ -114,16 +121,20 @@ void WebMBufferedParser::Append(const un
                        (mElement.mID.mLength + mElement.mSize.mLength);
         mState = READ_VINT;
         mNextState = READ_BLOCK_TIMECODE;
         break;
       case TRACKS_ID:
         mSkipBytes = mElement.mSize.mValue;
         mState = CHECK_INIT_FOUND;
         break;
+      case EBML_ID:
+        mLastInitStartOffset = mCurrentOffset + (p - aBuffer) -
+                            (mElement.mID.mLength + mElement.mSize.mLength);
+        /* FALLTHROUGH */
       default:
         mSkipBytes = mElement.mSize.mValue;
         mState = SKIP_DATA;
         mNextState = READ_ELEMENT_ID;
         break;
       }
       break;
     case READ_VINT: {
@@ -167,17 +178,18 @@ void WebMBufferedParser::Append(const un
                               mElement.mID.mLength + mElement.mSize.mLength;
           uint32_t idx = aMapping.IndexOfFirstElementGt(endOffset);
           if (idx == 0 || aMapping[idx - 1] != endOffset) {
             // Don't insert invalid negative timecodes.
             if (mBlockTimecode >= 0 || mClusterTimecode >= uint16_t(abs(mBlockTimecode))) {
               MOZ_ASSERT(mGotTimecodeScale);
               uint64_t absTimecode = mClusterTimecode + mBlockTimecode;
               absTimecode *= mTimecodeScale;
-              WebMTimeDataOffset entry(endOffset, absTimecode, mClusterOffset);
+              WebMTimeDataOffset entry(endOffset, absTimecode, mLastInitStartOffset,
+                                       mClusterOffset, mClusterEndOffset);
               aMapping.InsertElementAt(idx, entry);
             }
           }
         }
 
         // Skip rest of block header and the block's payload.
         mBlockSize -= mVInt.mLength;
         mBlockSize -= BLOCK_TIMECODE_LENGTH;
@@ -187,41 +199,54 @@ void WebMBufferedParser::Append(const un
       }
       break;
     case SKIP_DATA:
       if (mSkipBytes) {
         uint32_t left = aLength - (p - aBuffer);
         left = std::min(left, mSkipBytes);
         p += left;
         mSkipBytes -= left;
-      } else {
+      }
+      if (!mSkipBytes) {
+        mBlockEndOffset = mCurrentOffset + (p - aBuffer);
         mState = mNextState;
       }
       break;
     case CHECK_INIT_FOUND:
       if (mSkipBytes) {
         uint32_t left = aLength - (p - aBuffer);
         left = std::min(left, mSkipBytes);
         p += left;
         mSkipBytes -= left;
       }
       if (!mSkipBytes) {
         if (mInitEndOffset < 0) {
           mInitEndOffset = mCurrentOffset + (p - aBuffer);
+          mBlockEndOffset = mCurrentOffset + (p - aBuffer);
         }
         mState = READ_ELEMENT_ID;
       }
       break;
     }
   }
 
   NS_ASSERTION(p == aBuffer + aLength, "Must have parsed to end of data.");
   mCurrentOffset += aLength;
 }
 
+int64_t
+WebMBufferedParser::EndSegmentOffset(int64_t aOffset)
+{
+  if (mLastInitStartOffset > aOffset || mClusterOffset > aOffset) {
+    return std::min(mLastInitStartOffset >= 0 ? mLastInitStartOffset : INT64_MAX,
+                    mClusterOffset >= 0 ? mClusterOffset : INT64_MAX);
+  }
+  return mBlockEndOffset;
+}
+
 // SyncOffsetComparator and TimeComparator are slightly confusing, in that
 // the nsTArray they're used with (mTimeMapping) is sorted by mEndOffset and
 // these comparators are used on the other fields of WebMTimeDataOffset.
 // This is only valid because timecodes are required to be monotonically
 // increasing within a file (thus establishing an ordering relationship with
 // mTimecode), and mEndOffset is derived from mSyncOffset.
 struct SyncOffsetComparator {
   bool Equals(const WebMTimeDataOffset& a, const int64_t& b) const {
@@ -339,16 +364,23 @@ void WebMBufferedState::NotifyDataArrive
     if (mRangeParsers[i].mCurrentOffset >= mRangeParsers[i + 1].mStartOffset) {
       mRangeParsers[i + 1].mStartOffset = mRangeParsers[i].mStartOffset;
       mRangeParsers[i + 1].mInitEndOffset = mRangeParsers[i].mInitEndOffset;
       mRangeParsers.RemoveElementAt(i);
     } else {
       i += 1;
     }
   }
+
+  if (mRangeParsers.IsEmpty()) {
+    return;
+  }
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  mLastBlockOffset = mRangeParsers.LastElement().mBlockEndOffset;
 }
 
 void WebMBufferedState::Reset() {
   mRangeParsers.Clear();
   mTimeMapping.Clear();
 }
 
 void WebMBufferedState::UpdateIndex(const nsTArray<MediaByteRange>& aRanges, MediaResource* aResource)
@@ -393,16 +425,23 @@ void WebMBufferedState::UpdateIndex(cons
 int64_t WebMBufferedState::GetInitEndOffset()
 {
   if (mRangeParsers.IsEmpty()) {
     return -1;
   }
   return mRangeParsers[0].mInitEndOffset;
 }
 
+int64_t WebMBufferedState::GetLastBlockOffset()
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  return mLastBlockOffset;
+}
+
 bool WebMBufferedState::GetStartTime(uint64_t *aTime)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   if (mTimeMapping.IsEmpty()) {
     return false;
   }
 
--- a/dom/media/webm/WebMBufferedParser.h
+++ b/dom/media/webm/WebMBufferedParser.h
@@ -12,49 +12,65 @@
 #include "MediaResource.h"
 
 namespace mozilla {
 
 // Stores a stream byte offset and the scaled timecode of the block at
 // that offset.
 struct WebMTimeDataOffset
 {
-  WebMTimeDataOffset(int64_t aEndOffset, uint64_t aTimecode, int64_t aSyncOffset)
-    : mEndOffset(aEndOffset), mSyncOffset(aSyncOffset), mTimecode(aTimecode)
+  WebMTimeDataOffset(int64_t aEndOffset, uint64_t aTimecode,
+                     int64_t aInitOffset, int64_t aSyncOffset,
+                     int64_t aClusterEndOffset)
+    : mEndOffset(aEndOffset)
+    , mInitOffset(aInitOffset)
+    , mSyncOffset(aSyncOffset)
+    , mClusterEndOffset(aClusterEndOffset)
+    , mTimecode(aTimecode)
   {}
 
   bool operator==(int64_t aEndOffset) const {
     return mEndOffset == aEndOffset;
   }
 
   bool operator!=(int64_t aEndOffset) const {
     return mEndOffset != aEndOffset;
   }
 
   bool operator<(int64_t aEndOffset) const {
     return mEndOffset < aEndOffset;
   }
 
   int64_t mEndOffset;
+  int64_t mInitOffset;
   int64_t mSyncOffset;
+  int64_t mClusterEndOffset;
   uint64_t mTimecode;
 };
 
 // A simple WebM parser that produces data offset to timecode pairs as it
 // consumes blocks.  A new parser is created for each distinct range of data
 // received and begins parsing from the first WebM cluster within that
 // range.  Old parsers are destroyed when their range merges with a later
 // parser or an already parsed range.  The parser may start at any position
 // within the stream.
 struct WebMBufferedParser
 {
   explicit WebMBufferedParser(int64_t aOffset)
-    : mStartOffset(aOffset), mCurrentOffset(aOffset), mInitEndOffset(-1),
-      mState(READ_ELEMENT_ID), mVIntRaw(false), mClusterSyncPos(0),
-      mTimecodeScale(1000000), mGotTimecodeScale(false)
+    : mStartOffset(aOffset)
+    , mCurrentOffset(aOffset)
+    , mInitEndOffset(-1)
+    , mBlockEndOffset(-1)
+    , mState(READ_ELEMENT_ID)
+    , mVIntRaw(false)
+    , mLastInitStartOffset(-1)
+    , mClusterSyncPos(0)
+    , mClusterEndOffset(-1)
+    , mTimecodeScale(1000000)
+    , mGotTimecodeScale(false)
   {
     if (mStartOffset != 0) {
       mState = FIND_CLUSTER_SYNC;
     }
   }
 
   uint32_t GetTimecodeScale() {
     MOZ_ASSERT(mGotTimecodeScale);
@@ -78,29 +94,38 @@ struct WebMBufferedParser
   bool operator==(int64_t aOffset) const {
     return mCurrentOffset == aOffset;
   }
 
   bool operator<(int64_t aOffset) const {
     return mCurrentOffset < aOffset;
   }
 
+  // Returns the start offset of the init (EBML) or media segment (Cluster)
+  // following the aOffset position. If none were found, returns mBlockEndOffset.
+  // This allows to determine the end of the interval containg aOffset.
+  int64_t EndSegmentOffset(int64_t aOffset);
+
   // The offset at which this parser started parsing.  Used to merge
   // adjacent parsers, in which case the later parser adopts the earlier
   // parser's mStartOffset.
   int64_t mStartOffset;
 
-  // Current offset with the stream.  Updated in chunks as Append() consumes
+  // Current offset within the stream.  Updated in chunks as Append() consumes
   // data.
   int64_t mCurrentOffset;
 
-  // Tracks element's end offset. This indicates the end of the init segment.
-  // Will only be set if a Segment Information has been found.
+  // Tracks element's end offset. This indicates the end of the first init
+  // segment. Will only be set if a Segment Information has been found.
   int64_t mInitEndOffset;
 
+  // End offset of the last block parsed.
+  // Will only be set if a complete block has been parsed.
+  int64_t mBlockEndOffset;
+
 private:
   enum State {
     // Parser start state.  Expects to begin at a valid EBML element.  Move
     // to READ_VINT with mVIntRaw true, then return to READ_ELEMENT_SIZE.
     READ_ELEMENT_ID,
 
     // Store element ID read into mVInt into mElement.mID.  Move to
     // READ_VINT with mVIntRaw false, then return to PARSE_ELEMENT.
@@ -170,16 +195,20 @@ private:
   };
 
   EBMLElement mElement;
 
   VInt mVInt;
 
   bool mVIntRaw;
 
+  // EBML start offset. This indicates the start of the last init segment
+  // parsed. Will only be set if an EBML element has been found.
+  int64_t mLastInitStartOffset;
+
   // Current match position within CLUSTER_SYNC_ID.  Used to find sync
   // within arbitrary data.
   uint32_t mClusterSyncPos;
 
   // Number of bytes of mVInt left to read.  mVInt is complete once this
   // reaches 0.
   uint32_t mVIntLeft;
 
@@ -190,16 +219,19 @@ private:
   // Cluster-level timecode.
   uint64_t mClusterTimecode;
 
   // Start offset of the cluster currently being parsed.  Used as the sync
   // point offset for the offset-to-time mapping as each block timecode is
   // been parsed.
   int64_t mClusterOffset;
 
+  // End offset of the cluster currently being parsed. -1 if unknown.
+  int64_t mClusterEndOffset;
+
   // Start offset of the block currently being parsed.  Used as the byte
   // offset for the offset-to-time mapping once the block timecode has been
   // parsed.
   int64_t mBlockOffset;
 
   // Block-level timecode.  This is summed with mClusterTimecode to produce
   // an absolute timecode for the offset-to-time mapping.
   int16_t mBlockTimecode;
@@ -220,52 +252,59 @@ private:
   bool mGotTimecodeScale;
 };
 
 class WebMBufferedState final
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebMBufferedState)
 
 public:
-  WebMBufferedState() : mReentrantMonitor("WebMBufferedState") {
+  WebMBufferedState()
+    : mReentrantMonitor("WebMBufferedState")
+    , mLastBlockOffset(-1)
+  {
     MOZ_COUNT_CTOR(WebMBufferedState);
   }
 
   void NotifyDataArrived(const unsigned char* aBuffer, uint32_t aLength, int64_t aOffset);
   void Reset();
   void UpdateIndex(const nsTArray<MediaByteRange>& aRanges, MediaResource* aResource);
   bool CalculateBufferedForRange(int64_t aStartOffset, int64_t aEndOffset,
                                  uint64_t* aStartTime, uint64_t* aEndTime);
 
   // Returns true if aTime is is present in mTimeMapping and sets aOffset to
   // the latest offset for which decoding can resume without data
   // dependencies to arrive at aTime.
   bool GetOffsetForTime(uint64_t aTime, int64_t* aOffset);
 
   // Returns end offset of init segment or -1 if none found.
   int64_t GetInitEndOffset();
+  // Returns the end offset of the last complete block or -1 if none found.
+  int64_t GetLastBlockOffset();
 
   // Returns start time
   bool GetStartTime(uint64_t *aTime);
 
   // Returns keyframe for time
   bool GetNextKeyframeTime(uint64_t aTime, uint64_t* aKeyframeTime);
 
 private:
   // Private destructor, to discourage deletion outside of Release():
   ~WebMBufferedState() {
     MOZ_COUNT_DTOR(WebMBufferedState);
   }
 
-  // Synchronizes access to the mTimeMapping array.
+  // Synchronizes access to the mTimeMapping array and mLastBlockOffset.
   ReentrantMonitor mReentrantMonitor;
 
   // Sorted (by offset) map of data offsets to timecodes.  Populated
   // on the main thread as data is received and parsed by WebMBufferedParsers.
   nsTArray<WebMTimeDataOffset> mTimeMapping;
+  // The last complete block parsed. -1 if not set.
+  int64_t mLastBlockOffset;
 
   // Sorted (by offset) live parser instances.  Main thread only.
   nsTArray<WebMBufferedParser> mRangeParsers;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -11,16 +11,17 @@
 #include "WebMDemuxer.h"
 #include "WebMBufferedParser.h"
 #include "gfx2DGlue.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SharedThreadPool.h"
 #include "MediaDataDemuxer.h"
 #include "nsAutoRef.h"
 #include "NesteggPacketHolder.h"
+#include "XiphExtradata.h"
 
 #include <algorithm>
 #include <stdint.h>
 
 #define VPX_DONT_DEFINE_STDINT_TYPES
 #include "vpx/vp8dx.h"
 #include "vpx/vpx_decoder.h"
 
@@ -30,50 +31,49 @@ namespace mozilla {
 
 using namespace gfx;
 
 extern PRLogModuleInfo* gMediaDecoderLog;
 extern PRLogModuleInfo* gNesteggLog;
 
 // Functions for reading and seeking using WebMDemuxer required for
 // nestegg_io. The 'user data' passed to these functions is the
-// demuxer's MediaResourceIndex
+// demuxer.
 static int webmdemux_read(void* aBuffer, size_t aLength, void* aUserData)
 {
   MOZ_ASSERT(aUserData);
-  MediaResourceIndex* resource =
-    reinterpret_cast<MediaResourceIndex*>(aUserData);
-  int64_t length = resource->GetLength();
   MOZ_ASSERT(aLength < UINT32_MAX);
+  WebMDemuxer* demuxer = reinterpret_cast<WebMDemuxer*>(aUserData);
+  int64_t length = demuxer->GetEndDataOffset();
   uint32_t count = aLength;
-  if (length >= 0 && count + resource->Tell() > length) {
-    count = uint32_t(length - resource->Tell());
+  int64_t position = demuxer->GetResource()->Tell();
+  if (length >= 0 && count + position > length) {
+    count = length - position;
   }
 
   uint32_t bytes = 0;
-  nsresult rv = resource->Read(static_cast<char*>(aBuffer), count, &bytes);
-  bool eof = !bytes;
+  nsresult rv =
+    demuxer->GetResource()->Read(static_cast<char*>(aBuffer), count, &bytes);
+  bool eof = bytes < aLength;
   return NS_FAILED(rv) ? -1 : eof ? 0 : 1;
 }
 
 static int webmdemux_seek(int64_t aOffset, int aWhence, void* aUserData)
 {
   MOZ_ASSERT(aUserData);
-  MediaResourceIndex* resource =
-    reinterpret_cast<MediaResourceIndex*>(aUserData);
-  nsresult rv = resource->Seek(aWhence, aOffset);
+  WebMDemuxer* demuxer = reinterpret_cast<WebMDemuxer*>(aUserData);
+  nsresult rv = demuxer->GetResource()->Seek(aWhence, aOffset);
   return NS_SUCCEEDED(rv) ? 0 : -1;
 }
 
 static int64_t webmdemux_tell(void* aUserData)
 {
   MOZ_ASSERT(aUserData);
-  MediaResourceIndex* resource =
-    reinterpret_cast<MediaResourceIndex*>(aUserData);
-  return resource->Tell();
+  WebMDemuxer* demuxer = reinterpret_cast<WebMDemuxer*>(aUserData);
+  return demuxer->GetResource()->Tell();
 }
 
 static void webmdemux_log(nestegg* aContext,
                           unsigned int aSeverity,
                           char const* aFormat, ...)
 {
   if (!MOZ_LOG_TEST(gNesteggLog, LogLevel::Debug)) {
     return;
@@ -117,22 +117,25 @@ static void webmdemux_log(nestegg* aCont
 WebMDemuxer::WebMDemuxer(MediaResource* aResource)
   : mResource(aResource)
   , mBufferedState(nullptr)
   , mInitData(nullptr)
   , mContext(nullptr)
   , mVideoTrack(0)
   , mAudioTrack(0)
   , mSeekPreroll(0)
+  , mLastAudioFrameTime(0)
   , mLastVideoFrameTime(0)
   , mAudioCodec(-1)
   , mVideoCodec(-1)
   , mHasVideo(false)
   , mHasAudio(false)
   , mNeedReIndex(true)
+  , mLastWebMBlockOffset(-1)
+  , mIsExpectingMoreData(true)
 {
   if (!gNesteggLog) {
     gNesteggLog = PR_NewLogModule("Nestegg");
   }
 }
 
 WebMDemuxer::~WebMDemuxer()
 {
@@ -247,17 +250,17 @@ WebMDemuxer::Cleanup()
 
 nsresult
 WebMDemuxer::ReadMetadata()
 {
   nestegg_io io;
   io.read = webmdemux_read;
   io.seek = webmdemux_seek;
   io.tell = webmdemux_tell;
-  io.userdata = &mResource;
+  io.userdata = this;
   int64_t maxOffset = mBufferedState->GetInitEndOffset();
   if (maxOffset == -1) {
     maxOffset = mResource.GetLength();
   }
   int r = nestegg_init(&mContext, io, &webmdemux_log, maxOffset);
   if (r == -1) {
     return NS_ERROR_FAILURE;
   }
@@ -373,30 +376,43 @@ WebMDemuxer::ReadMetadata()
       mInfo.mAudio.mChannels = params.channels;
 
       unsigned int nheaders = 0;
       r = nestegg_track_codec_data_count(mContext, track, &nheaders);
       if (r == -1) {
         return NS_ERROR_FAILURE;
       }
 
+      nsAutoTArray<const unsigned char*,4> headers;
+      nsAutoTArray<size_t,4> headerLens;
       for (uint32_t header = 0; header < nheaders; ++header) {
         unsigned char* data = 0;
         size_t length = 0;
         r = nestegg_track_codec_data(mContext, track, header, &data, &length);
         if (r == -1) {
           return NS_ERROR_FAILURE;
         }
-        // Vorbis has 3 headers write length + data for each header
-        if (nheaders > 1) {
-          uint8_t c[2];
-          BigEndian::writeUint16(&c[0], length);
-          mInfo.mAudio.mCodecSpecificConfig->AppendElements(&c[0], 2);
+        headers.AppendElement(data);
+        headerLens.AppendElement(length);
+      }
+
+      // Vorbis has 3 headers, convert to Xiph extradata format to send them to
+      // the demuxer.
+      // TODO: This is already the format WebM stores them in. Would be nice
+      // to avoid having libnestegg split them only for us to pack them again,
+      // but libnestegg does not give us an API to access this data directly.
+      if (nheaders > 1) {
+        if (!XiphHeadersToExtradata(mInfo.mAudio.mCodecSpecificConfig,
+                                    headers, headerLens)) {
+          return NS_ERROR_FAILURE;
         }
-        mInfo.mAudio.mCodecSpecificConfig->AppendElements(data, length);
+      }
+      else {
+        mInfo.mAudio.mCodecSpecificConfig->AppendElements(headers[0],
+                                                          headerLens[0]);
       }
       uint64_t duration = 0;
       r = nestegg_duration(mContext, &duration);
       if (!r) {
         mInfo.mAudio.mDuration = media::TimeUnit::FromNanoseconds(duration).ToMicroseconds();
       }
     }
   }
@@ -423,16 +439,18 @@ WebMDemuxer::EnsureUpToDateIndex()
   nsresult rv = resource->GetCachedRanges(byteRanges);
   if (NS_FAILED(rv) || !byteRanges.Length()) {
     return;
   }
   mBufferedState->UpdateIndex(byteRanges, resource);
   if (!mInitData && mBufferedState->GetInitEndOffset() != -1) {
     mInitData = mResource.MediaReadAt(0, mBufferedState->GetInitEndOffset());
   }
+  mLastWebMBlockOffset = mBufferedState->GetLastBlockOffset();
+  mIsExpectingMoreData = mResource.GetResource()->IsExpectingMoreData();
   mNeedReIndex = false;
 }
 
 void
 WebMDemuxer::NotifyDataArrived(uint32_t aLength, int64_t aOffset)
 {
   WEBM_DEBUG("length: %ld offset: %ld", aLength, aOffset);
   mNeedReIndex = true;
@@ -449,16 +467,18 @@ UniquePtr<EncryptionInfo>
 WebMDemuxer::GetCrypto()
 {
   return nullptr;
 }
 
 bool
 WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSamples)
 {
+  EnsureUpToDateIndex();
+
   nsRefPtr<NesteggPacketHolder> holder(NextPacket(aType));
 
   if (!holder) {
     return false;
   }
 
   int r = 0;
   unsigned int count = 0;
@@ -684,16 +704,20 @@ WebMDemuxer::SeekInternal(const media::T
 
     r = nestegg_offset_seek(mContext, offset);
     if (r == -1) {
       WEBM_DEBUG("and nestegg_offset_seek to %" PRIu64 " failed", offset);
       return NS_ERROR_FAILURE;
     }
     WEBM_DEBUG("got offset from buffered state: %" PRIu64 "", offset);
   }
+
+  mLastAudioFrameTime = 0;
+  mLastVideoFrameTime = 0;
+
   return NS_OK;
 }
 
 media::TimeIntervals
 WebMDemuxer::GetBuffered()
 {
   EnsureUpToDateIndex();
   AutoPinned<MediaResource> resource(mResource.GetResource());
--- a/dom/media/webm/WebMDemuxer.h
+++ b/dom/media/webm/WebMDemuxer.h
@@ -80,16 +80,28 @@ public:
   nsresult Reset();
 
   // Pushes a packet to the front of the audio packet queue.
   virtual void PushAudioPacket(NesteggPacketHolder* aItem);
 
   // Pushes a packet to the front of the video packet queue.
   virtual void PushVideoPacket(NesteggPacketHolder* aItem);
 
+  // Public accessor for nestegg callbacks
+  MediaResourceIndex* GetResource()
+  {
+    return &mResource;
+  }
+
+  int64_t GetEndDataOffset()
+  {
+    return mLastWebMBlockOffset < 0 || mIsExpectingMoreData
+      ? mResource.GetLength() : mLastWebMBlockOffset;
+  }
+
 private:
   friend class WebMTrackDemuxer;
 
   ~WebMDemuxer();
   void Cleanup();
   nsresult InitBufferedState();
   nsresult ReadMetadata();
   void NotifyDataArrived(uint32_t aLength, int64_t aOffset) override;
@@ -147,16 +159,22 @@ private:
   int mAudioCodec;
   // Codec ID of video track
   int mVideoCodec;
 
   // Booleans to indicate if we have audio and/or video data
   bool mHasVideo;
   bool mHasAudio;
   bool mNeedReIndex;
+
+  // The last complete block parsed by the WebMBufferedState. -1 if not set.
+  // We cache those values rather than retrieving them for performance reasons
+  // as nestegg only performs 1-byte read at a time.
+  int64_t mLastWebMBlockOffset;
+  bool mIsExpectingMoreData;
 };
 
 class WebMTrackDemuxer : public MediaTrackDemuxer
 {
 public:
   WebMTrackDemuxer(WebMDemuxer* aParent,
                   TrackInfo::TrackType aType,
                   uint32_t aTrackNumber);
--- a/dom/media/webspeech/recognition/PocketSphinxSpeechRecognitionService.cpp
+++ b/dom/media/webspeech/recognition/PocketSphinxSpeechRecognitionService.cpp
@@ -16,30 +16,33 @@
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsMemory.h"
 
 extern "C" {
 #include "pocketsphinx/pocketsphinx.h"
+#include "sphinxbase/logmath.h"
 #include "sphinxbase/sphinx_config.h"
 #include "sphinxbase/jsgf.h"
 }
 
 namespace mozilla {
 
 using namespace dom;
 
 class DecodeResultTask : public nsRunnable
 {
 public:
   DecodeResultTask(const nsString& hypstring,
+                   float64 confidence,
                    WeakPtr<dom::SpeechRecognition> recognition)
       : mResult(hypstring),
+        mConfidence(confidence),
         mRecognition(recognition),
         mWorkerThread(do_GetCurrentThread())
   {
     MOZ_ASSERT(
       !NS_IsMainThread()); // This should be running on the worker thread
   }
 
   NS_IMETHOD
@@ -54,17 +57,17 @@ public:
     SpeechRecognitionResultList* resultList =
       new SpeechRecognitionResultList(mRecognition);
     SpeechRecognitionResult* result = new SpeechRecognitionResult(mRecognition);
     if (0 < mRecognition->MaxAlternatives()) {
       SpeechRecognitionAlternative* alternative =
         new SpeechRecognitionAlternative(mRecognition);
 
       alternative->mTranscript = mResult;
-      alternative->mConfidence = 100;
+      alternative->mConfidence = mConfidence;
 
       result->mItems.AppendElement(alternative);
     }
     resultList->mItems.AppendElement(result);
 
     event->mRecognitionResultList = resultList;
     NS_DispatchToMainThread(event);
 
@@ -72,16 +75,17 @@ public:
     // around forever... bad!
     // But thread->Shutdown must be called from the main thread, not from the
     // thread itself.
     return mWorkerThread->Shutdown();
   }
 
 private:
   nsString mResult;
+  float64 mConfidence;
   WeakPtr<dom::SpeechRecognition> mRecognition;
   nsCOMPtr<nsIThread> mWorkerThread;
 };
 
 class DecodeTask : public nsRunnable
 {
 public:
   DecodeTask(WeakPtr<dom::SpeechRecognition> recogntion,
@@ -90,33 +94,38 @@ public:
   {
   }
 
   NS_IMETHOD
   Run()
   {
     char const* hyp;
     int rv;
-    int32 score;
+    int32 final;
+    int32 logprob;
+    float64 confidence;
     nsAutoCString hypoValue;
 
     rv = ps_start_utt(mPs);
     rv = ps_process_raw(mPs, &mAudiovector[0], mAudiovector.Length(), FALSE,
                         FALSE);
 
     rv = ps_end_utt(mPs);
+    confidence = 0;
     if (rv >= 0) {
-      hyp = ps_get_hyp(mPs, &score);
-      if (hyp) {
+      hyp = ps_get_hyp_final(mPs, &final);
+      if (hyp && final) {
+        logprob = ps_get_prob(mPs);
+        confidence = logmath_exp(ps_get_logmath(mPs), logprob);
         hypoValue.Assign(hyp);
       }
     }
 
     nsCOMPtr<nsIRunnable> resultrunnable =
-      new DecodeResultTask(NS_ConvertUTF8toUTF16(hypoValue), mRecognition);
+      new DecodeResultTask(NS_ConvertUTF8toUTF16(hypoValue), confidence, mRecognition);
     return NS_DispatchToMainThread(resultrunnable);
   }
 
 private:
   WeakPtr<dom::SpeechRecognition> mRecognition;
   nsTArray<int16_t> mAudiovector;
   ps_decoder_t* mPs;
 };
@@ -149,17 +158,17 @@ PocketSphinxSpeechRecognitionService::Po
 #endif
   tmpFile->AppendRelativePath(NS_LITERAL_STRING("models"));     //
   tmpFile->AppendRelativePath(NS_LITERAL_STRING("dict"));       //
   tmpFile->AppendRelativePath(NS_LITERAL_STRING("en-US.dic")); //
   tmpFile->GetPath(aStringDictPath);
 
   // FOR B2G PATHS HARDCODED (APPEND /DATA ON THE BEGINING, FOR DESKTOP, ONLY
   // MODELS/ RELATIVE TO ROOT
-  mPSConfig = cmd_ln_init(nullptr, ps_args(), TRUE, "-hmm",
+  mPSConfig = cmd_ln_init(nullptr, ps_args(), TRUE, "-bestpath", "yes", "-hmm",
                           ToNewUTF8String(aStringAMPath), // acoustic model
                           "-dict", ToNewUTF8String(aStringDictPath), nullptr);
   if (mPSConfig == nullptr) {
     ISDecoderCreated = false;
   } else {
     mPSHandle = ps_init(mPSConfig);
     if (mPSHandle == nullptr) {
       ISDecoderCreated = false;
@@ -263,21 +272,21 @@ PocketSphinxSpeechRecognitionService::Va
   } else if (aSpeechGrammar) {
     nsAutoString grammar;
     ErrorResult rv;
     aSpeechGrammar->GetSrc(grammar, rv);
 
     int result = ps_set_jsgf_string(mPSHandle, "name",
                                     NS_ConvertUTF16toUTF8(grammar).get());
 
-    ps_set_search(mPSHandle, "name");
-
     if (result != 0) {
       ISGrammarCompiled = false;
     } else {
+      ps_set_search(mPSHandle, "name");
+
       ISGrammarCompiled = true;
     }
   } else {
     ISGrammarCompiled = false;
   }
 
   return ISGrammarCompiled ? NS_OK : NS_ERROR_NOT_INITIALIZED;
 }
--- a/dom/payment/Payment.jsm
+++ b/dom/payment/Payment.jsm
@@ -231,18 +231,19 @@ let PaymentManager =  {
         };
 
 #ifdef MOZ_B2G
         // Let this payment provider access the firefox-accounts API when
         // it's loaded in the trusted UI.
         if (systemAppId != Ci.nsIScriptSecurityManager.NO_APP_ID) {
           this.LOG("Granting firefox-accounts permission to " + provider.uri);
           let uri = Services.io.newURI(provider.uri, null, null);
-          let principal = Services.scriptSecurityManager
-                            .getAppCodebasePrincipal(uri, systemAppId, true);
+          let attrs = {appId: systemAppId, inBrowser: true};
+          let principal =
+            Services.scriptSecurityManager.createCodebasePrincipal(uri, attrs);
 
           Services.perms.addFromPrincipal(principal, "firefox-accounts",
                                           Ci.nsIPermissionManager.ALLOW_ACTION,
                                           Ci.nsIPermissionManager.EXPIRE_SESSION);
         }
 #endif
 
         if (this._debug) {
--- a/dom/permission/PermissionSettings.js
+++ b/dom/permission/PermissionSettings.js
@@ -30,20 +30,24 @@ function PermissionSettings()
 
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "appsService",
                                    "@mozilla.org/AppsService;1",
                                    "nsIAppsService");
 
 PermissionSettings.prototype = {
   get: function get(aPermName, aManifestURL, aOrigin, aBrowserFlag) {
+    // TODO: Bug 1196644 - Add signPKg parameter into PermissionSettings.js
     debug("Get called with: " + aPermName + ", " + aManifestURL + ", " + aOrigin + ", " + aBrowserFlag);
     let uri = Services.io.newURI(aOrigin, null, null);
     let appID = appsService.getAppLocalIdByManifestURL(aManifestURL);
-    let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(uri, appID, aBrowserFlag);
+    let principal =
+      Services.scriptSecurityManager.createCodebasePrincipal(uri,
+                                                             {appId: appID,
+                                                              inBrowser: aBrowserFlag});
     let result = Services.perms.testExactPermanentPermission(principal, aPermName);
 
     switch (result)
     {
       case Ci.nsIPermissionManager.UNKNOWN_ACTION:
         return "unknown";
       case Ci.nsIPermissionManager.ALLOW_ACTION:
         return "allow";
@@ -54,21 +58,22 @@ PermissionSettings.prototype = {
       default:
         dump("Unsupported PermissionSettings Action!\n");
         return "unknown";
     }
   },
 
   isExplicit: function isExplicit(aPermName, aManifestURL, aOrigin,
                                   aBrowserFlag) {
+    // TODO: Bug 1196644 - Add signPKg parameter into PermissionSettings.js
     debug("isExplicit: " + aPermName + ", " + aManifestURL + ", " + aOrigin);
     let uri = Services.io.newURI(aOrigin, null, null);
     let app = appsService.getAppByManifestURL(aManifestURL);
     let principal = Services.scriptSecurityManager
-      .getAppCodebasePrincipal(uri, app.localId, aBrowserFlag);
+      .createCodebasePrincipal(uri, {appId: app.localId, inBrowser: aBrowserFlag});
 
     return isExplicitInPermissionsTable(aPermName,
                                         principal.appStatus,
                                         app.kind);
   },
 
   set: function set(aPermName, aPermValue, aManifestURL, aOrigin,
                     aBrowserFlag) {
@@ -94,19 +99,23 @@ PermissionSettings.prototype = {
       origin: aOrigin,
       manifestURL: aManifestURL,
       value: aPermValue,
       browserFlag: aBrowserFlag
     });
   },
 
   remove: function remove(aPermName, aManifestURL, aOrigin) {
+    // TODO: Bug 1196644 - Add signPKg parameter into PermissionSettings.js
     let uri = Services.io.newURI(aOrigin, null, null);
     let appID = appsService.getAppLocalIdByManifestURL(aManifestURL);
-    let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(uri, appID, true);
+    let principal =
+      Services.scriptSecurityManager.createCodebasePrincipal(uri,
+                                                             {appId: appID,
+                                                              inBrowser: true});
 
     if (principal.appStatus !== Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED) {
       let errorMsg = "PermissionSettings.js: '" + aOrigin + "'" +
                      " is installed or permission is implicit, cannot remove '" +
                      aPermName + "'.";
       Cu.reportError(errorMsg);
       throw new Components.Exception(errorMsg);
     }
--- a/dom/permission/PermissionSettings.jsm
+++ b/dom/permission/PermissionSettings.jsm
@@ -62,19 +62,23 @@ this.PermissionSettingsModule = {
   addPermission: function addPermission(aData, aCallbacks) {
 
     this._internalAddPermission(aData, true, aCallbacks);
 
   },
 
 
   _internalAddPermission: function _internalAddPermission(aData, aAllowAllChanges, aCallbacks) {
+    // TODO: Bug 1196644 - Add signPKg parameter into PermissionSettings.jsm
     let uri = Services.io.newURI(aData.origin, null, null);
     let app = appsService.getAppByManifestURL(aData.manifestURL);
-    let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(uri, app.localId, aData.browserFlag);
+    let principal =
+      Services.scriptSecurityManager.createCodebasePrincipal(uri,
+                                                             {appId: app.localId,
+                                                              inBrowser: aData.browserFlag});
 
     let action;
     switch (aData.value)
     {
       case "unknown":
         action = Ci.nsIPermissionManager.UNKNOWN_ACTION;
         break;
       case "allow":
@@ -98,20 +102,24 @@ this.PermissionSettingsModule = {
       return true;
     } else {
       debug("add Failure: " + aData.origin + " " + app.localId + " " + action);
       return false; // This isn't currently used, see comment on setPermission
     }
   },
 
   getPermission: function getPermission(aPermName, aManifestURL, aOrigin, aBrowserFlag) {
+    // TODO: Bug 1196644 - Add signPKg parameter into PermissionSettings.jsm
     debug("getPermission: " + aPermName + ", " + aManifestURL + ", " + aOrigin);
     let uri = Services.io.newURI(aOrigin, null, null);
     let appID = appsService.getAppLocalIdByManifestURL(aManifestURL);
-    let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(uri, appID, aBrowserFlag);
+    let principal =
+      Services.scriptSecurityManager.createCodebasePrincipal(uri,
+                                                             {appId: appID,
+                                                              inBrowser: aBrowserFlag});
     let result = Services.perms.testExactPermissionFromPrincipal(principal, aPermName);
 
     switch (result)
     {
       case Ci.nsIPermissionManager.UNKNOWN_ACTION:
         return "unknown";
       case Ci.nsIPermissionManager.ALLOW_ACTION:
         return "allow";
--- a/dom/quota/QuotaManager.cpp
+++ b/dom/quota/QuotaManager.cpp
@@ -5283,20 +5283,19 @@ StorageDirectoryHelper::RunOnMainThread(
           return rv;
         }
 
         nsCOMPtr<nsIPrincipal> principal;
         if (originProps.mAppId == kUnknownAppId) {
           rv = secMan->GetSimpleCodebasePrincipal(uri,
                                                   getter_AddRefs(principal));
         } else {
-          rv = secMan->GetAppCodebasePrincipal(uri,
-                                               originProps.mAppId,
-                                               originProps.mInMozBrowser,
-                                               getter_AddRefs(principal));
+          OriginAttributes attrs(originProps.mAppId, originProps.mInMozBrowser);
+          principal = BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+          rv = principal ? NS_OK : NS_ERROR_FAILURE;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
 
         if (mCreate) {
           rv = QuotaManager::GetInfoFromPrincipal(principal,
                                                   &originProps.mGroup,
--- a/dom/tests/mochitest/chrome/test_MozDomFullscreen_event.xul
+++ b/dom/tests/mochitest/chrome/test_MozDomFullscreen_event.xul
@@ -22,19 +22,19 @@ function make_uri(url) {
   var ios = Cc["@mozilla.org/network/io-service;1"].
             getService(Ci.nsIIOService);
   return ios.newURI(url, null, null);
 }
 
 // Ensure "fullscreen" permissions are not present on the test URI.
 var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
 var uri = make_uri("http://mochi.test:8888");
-var principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
-                  .getService(Ci.nsIScriptSecurityManager)
-                  .getNoAppCodebasePrincipal(uri);
+var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
+                            .getService(Ci.nsIScriptSecurityManager);
+var principal = ssm.createCodebasePrincipal(uri, {});
 pm.removeFromPrincipal(principal, "fullscreen");
 
 SpecialPowers.pushPrefEnv({"set": [
   ['full-screen-api.enabled', true],
   ['full-screen-api.allow-trusted-requests-only', false],
   ['full-screen-api.transition-duration.enter', '0 0'],
   ['full-screen-api.transition-duration.leave', '0 0']
 ]}, setup);
--- a/dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
+++ b/dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
@@ -13,21 +13,21 @@ function startTest()
   var ios = Components.classes["@mozilla.org/network/io-service;1"]
     .getService(Components.interfaces.nsIIOService);
   var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
     .getService(Components.interfaces.nsIScriptSecurityManager);
   var dsm = Components.classes["@mozilla.org/dom/localStorage-manager;1"]
     .getService(Components.interfaces.nsIDOMStorageManager);
 
   var uri = ios.newURI(url, "", null);
-  var principal = ssm.getNoAppCodebasePrincipal(uri);
+  var principal = ssm.createCodebasePrincipal(uri, {});
   var storage = dsm.createStorage(window, principal, "");
-  
+
   storage.setItem("chromekey", "chromevalue");
-  
+
   var aframe = document.getElementById("aframe");
   aframe.onload = function()
   {
     is(storage.getItem("chromekey"), "chromevalue");
     is(aframe.contentDocument.getElementById("data").innerHTML, "chromevalue");
     SimpleTest.finish();
   }
   aframe.src = "http://example.com/tests/dom/tests/mochitest/localstorage/frameChromeSlave.html";  
--- a/dom/webidl/InputMethod.webidl
+++ b/dom/webidl/InputMethod.webidl
@@ -187,79 +187,188 @@ interface MozInputContext: EventTarget {
      *
      * A dict is provided in the detail property of the event containing the new values, and
      * an "ownAction" property to denote the event is the result of our own mutation to
      * the input field.
      */
     attribute EventHandler onsurroundingtextchange;
 
     /*
-      * send a character with its key events.
-      * @param modifiers see http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/base/nsIDOMWindowUtils.idl#206
-      * @param repeat indicates whether a key would be sent repeatedly.
-      * @return true if succeeds. Otherwise false if the input context becomes void.
-      * Alternative: sendKey(KeyboardEvent event), but we will likely
-      * waste memory for creating the KeyboardEvent object.
-      * Note that, if you want to send a key n times repeatedly, make sure set
-      * parameter repeat to true and invoke sendKey n-1 times, and then set
-      * repeat to false in the last invoke.
-      */
-    Promise<boolean> sendKey(long keyCode, long charCode, long modifiers, optional boolean repeat);
+     * Send a string/character with its key events. There are two ways of invocating 
+     * the method for backward compability purpose.
+     *
+     * (1) The recommended way, allow specifying DOM level 3 properties like |code|.
+     * @param dictOrKeyCode See MozInputMethodKeyboardEventDict.
+     * @param charCode disregarded
+     * @param modifiers disregarded
+     * @param repeat disregarded
+     *
+     * (2) Deprecated, reserved for backward compability.
+     * @param dictOrKeyCode keyCode of the key to send, should be one of the DOM_VK_ value in KeyboardEvent.
+     * @param charCode charCode of the character, should be 0 for non-printable keys.
+     * @param modifiers this paramater is no longer honored.
+     * @param repeat indicates whether a key would be sent repeatedly.
+     *
+     * @return A promise. Resolve to true if succeeds.
+     *                    Rejects to a string indicating the error.
+     *
+     * Note that, if you want to send a key n times repeatedly, make sure set
+     * parameter repeat to true and invoke sendKey n times, and invoke keyup
+     * after the end of the input.
+     */
+    Promise<boolean> sendKey((MozInputMethodRequiredKeyboardEventDict or long) dictOrKeyCode,
+                             optional long charCode, 
+                             optional long modifiers, 
+                             optional boolean repeat);
+
+    /*
+     * Send a string/character with keydown, and keypress events.
+     * keyup should be called afterwards to ensure properly sequence.
+     *
+     * @param dict See MozInputMethodKeyboardEventDict.
+     *
+     * @return A promise. Resolve to true if succeeds.
+     *                    Rejects to a string indicating the error.
+     */
+    Promise<boolean> keydown(MozInputMethodRequiredKeyboardEventDict dict);
+
+    /*
+     * Send a keyup event. keydown should be called first to ensure properly sequence.
+     *
+     * @param dict See MozInputMethodKeyboardEventDict.
+     *
+     * @return A promise. Resolve to true if succeeds.
+     *                    Rejects to a string indicating the error.
+     *
+     */
+    Promise<boolean> keyup(MozInputMethodRequiredKeyboardEventDict dict);
 
     /*
      * Set current composing text. This method will start composition or update
      * composition if it has started. The composition will be started right
      * before the cursor position and any selected text will be replaced by the
      * composing text. When the composition is started, calling this method can
      * update the text and move cursor winthin the range of the composing text.
      * @param text The new composition text to show.
      * @param cursor The new cursor position relative to the start of the
      * composition text. The cursor should be positioned within the composition
      * text. This means the value should be >= 0 and <= the length of
      * composition text. Defaults to the lenght of composition text, i.e., the
      * cursor will be positioned after the composition text.
      * @param clauses The array of composition clause information. If not set,
      * only one clause is supported.
-     *
+     * @param dict The properties of the keyboard event that cause the composition
+     * to set. keydown or keyup event will be fired if it's necessary.
+     * For compatibility, we recommend that you should always set this argument 
+     * if it's caused by a key operation.
+     * 
      * The composing text, which is shown with underlined style to distinguish
      * from the existing text, is used to compose non-ASCII characters from
      * keystrokes, e.g. Pinyin or Hiragana. The composing text is the
      * intermediate text to help input complex character and is not committed to
      * current input field. Therefore if any text operation other than
      * composition is performed, the composition will automatically end. Same
      * apply when the inputContext is lost during an unfinished composition
      * session.
      *
      * To finish composition and commit text to current input field, an IME
      * should call |endComposition|.
      */
-    // XXXbz what is this promise resolved with?
-    Promise<any> setComposition(DOMString text, optional long cursor,
-                                optional sequence<CompositionClauseParameters> clauses);
+    Promise<boolean> setComposition(DOMString text, 
+                                    optional long cursor,
+                                    optional sequence<CompositionClauseParameters> clauses,
+                                    optional MozInputMethodKeyboardEventDict dict);
 
     /*
      * End composition, clear the composing text and commit given text to
      * current input field. The text will be committed before the cursor
      * position.
      * @param text The text to commited before cursor position. If empty string
      * is given, no text will be committed.
+     * @param dict The properties of the keyboard event that cause the composition
+     * to end. keydown or keyup event will be fired if it's necessary.
+     * For compatibility, we recommend that you should always set this argument 
+     * if it's caused by a key operation.
      *
      * Note that composition always ends automatically with nothing to commit if
      * the composition does not explicitly end by calling |endComposition|, but
      * is interrupted by |sendKey|, |setSelectionRange|,
      * |replaceSurroundingText|, |deleteSurroundingText|, user moving the
      * cursor, changing the focus, etc.
      */
-    // XXXbz what is this promise resolved with?
-    Promise<any> endComposition(optional DOMString text);
+    Promise<boolean> endComposition(optional DOMString text, 
+                                    optional MozInputMethodKeyboardEventDict dict);
 };
 
 enum CompositionClauseSelectionType {
   "raw-input",
   "selected-raw-text",
   "converted-text",
   "selected-converted-text"
 };
 
 dictionary CompositionClauseParameters {
   DOMString selectionType = "raw-input";
   long length;
 };
+
+/*
+ * A MozInputMethodKeyboardEventDictBase contains the following properties,
+ * indicating the properties of the keyboard event caused.
+ *
+ * This is the base dictionary type for us to create two child types that could
+ * be used as argument type in two types of methods, as WebIDL parser required.
+ * 
+ */
+dictionary MozInputMethodKeyboardEventDictBase {
+  /*
+   * String/character to output, or a registered name of non-printable key.
+   * (To be defined in the inheriting dictionary types.)
+   */
+  // DOMString key;
+  /*
+   * String/char indicating the virtual hardware key pressed. Optional.
+   * Must be a value defined in
+   * http://www.w3.org/TR/DOM-Level-3-Events-code/#keyboard-chording-virtual
+   * If your keyboard app emulates physical keyboard layout, this value should
+   * not be empty string. Otherwise, it should be empty string.
+   */
+  DOMString code = "";
+  /*
+   * keyCode of the keyboard event. Optional.
+   * To be disregarded if |key| is an alphanumeric character.
+   * If the key causes inputting a character and if your keyboard app emulates
+   * a physical keyboard layout, this value should be same as the value used
+   * by Firefox for desktop. If the key causes inputting an ASCII character
+   * but if your keyboard app doesn't emulate any physical keyboard layouts,
+   * the value should be proper value for the key value.
+   */
+  long? keyCode;
+  /*
+   * Indicates whether a key would be sent repeatedly. Optional.
+   */
+  boolean repeat = false;
+  /*
+   * Optional. True if |key| property is explicitly referring to a printable key.
+   * When this is set, key will be printable even if the |key| value matches any
+   * of the registered name of non-printable keys.
+   */
+  boolean printable = false;
+};
+
+/*
+ * For methods like setComposition() and endComposition(), the optional
+ * dictionary type argument is really optional when all of it's property
+ * are optional.
+ * This dictionary type is used to denote that argument.
+ */
+dictionary MozInputMethodKeyboardEventDict : MozInputMethodKeyboardEventDictBase {
+  DOMString? key;
+};
+
+/*
+ * For methods like keydown() and keyup(), the dictionary type argument is
+ * considered required only if at least one of it's property is required.
+ * This dictionary type is used to denote that argument.
+ */
+dictionary MozInputMethodRequiredKeyboardEventDict : MozInputMethodKeyboardEventDictBase {
+  required DOMString key;
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource.resource
@@ -0,0 +1,22 @@
+:this file must be enconded in utf8
+:and its Content-Type must be equal to text/event-stream
+
+retry:500
+data: 2
+unknow: unknow
+
+event: other_event_name
+retry:500
+data: 2
+unknow: unknow
+
+event: click
+retry:500
+
+event: blur
+retry:500
+
+event:keypress
+retry:500
+
+
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource.resource^headers^
@@ -0,0 +1,3 @@
+Content-Type: text/event-stream
+Cache-Control: no-cache, must-revalidate
+Access-Control-Allow-Origin: *
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response.html
@@ -0,0 +1,75 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
+  <script type="text/javascript">
+
+  var prefix = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/eventsource/";
+
+  function ok(aCondition, aMessage) {
+    parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*");
+  }
+
+  function doUnregister() {
+    navigator.serviceWorker.getRegistration().then(swr => {
+      swr.unregister().then(function(result) {
+        ok(result, "Unregister should return true.");
+        parent.postMessage({status: "callback", data: "done"}, "*");
+      }, function(e) {
+        ok(false, "Unregistering the SW failed with " + e);
+      });
+    });
+  }
+
+  function doEventSource() {
+    var source = new EventSource(prefix + "eventsource.resource");
+    source.onmessage = function(e) {
+      source.onmessage = null;
+      source.close();
+      ok(true, "EventSource should work with cors responses");
+      doUnregister();
+    };
+    source.onerror = function(error) {
+      source.onerror = null;
+      source.close();
+      ok(false, "Something went wrong");
+    };
+  }
+
+  function onLoad() {
+    if (!parent) {
+      dump("eventsource/eventsource_cors_response.html shouldn't be launched directly!");
+    }
+
+    window.addEventListener("message", function onMessage(e) {
+      if (e.data.status === "callback") {
+        switch(e.data.data) {
+          case "eventsource":
+            doEventSource();
+            window.removeEventListener("message", onMessage);
+            break;
+          default:
+            ok(false, "Something went wrong")
+            break
+        }
+      }
+    });
+
+    navigator.serviceWorker.ready.then(function() {
+      parent.postMessage({status: "callback", data: "ready"}, "*");
+    });
+
+    navigator.serviceWorker.addEventListener("message", function(event) {
+      parent.postMessage(event.data, "*");
+    });
+  }
+
+  </script>
+</head>
+<body onload="onLoad()">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response_intercept_worker.js
@@ -0,0 +1,19 @@
+// Cross origin request
+var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/eventsource/';
+
+self.importScripts('eventsource_worker_helper.js');
+
+self.addEventListener('fetch', function (event) {
+  var request = event.request;
+  var url = new URL(request.url);
+
+  if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') {
+    return;
+  }
+
+  ok(request.mode === 'cors', 'EventSource should make a CORS request');
+  var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'cors'});
+  event.respondWith(fetch(fetchRequest).then((fetchResponse) => {
+    return fetchResponse;
+  }));
+});
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response.html
@@ -0,0 +1,75 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
+  <script type="text/javascript">
+
+  var prefix = "https://example.com/tests/dom/workers/test/serviceworkers/eventsource/";
+
+  function ok(aCondition, aMessage) {
+    parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*");
+  }
+
+  function doUnregister() {
+    navigator.serviceWorker.getRegistration().then(swr => {
+      swr.unregister().then(function(result) {
+        ok(result, "Unregister should return true.");
+        parent.postMessage({status: "callback", data: "done"}, "*");
+      }, function(e) {
+        ok(false, "Unregistering the SW failed with " + e);
+      });
+    });
+  }
+
+  function doEventSource() {
+    var source = new EventSource(prefix + "eventsource.resource");
+    source.onmessage = function(e) {
+      source.onmessage = null;
+      source.close();
+      ok(false, "Something went wrong");
+    };
+    source.onerror = function(error) {
+      source.onerror = null;
+      source.close();
+      ok(true, "EventSource should not work with mixed content cors responses");
+      doUnregister();
+    };
+  }
+
+  function onLoad() {
+    if (!parent) {
+      dump("eventsource/eventsource_cors_response.html shouldn't be launched directly!");
+    }
+
+    window.addEventListener("message", function onMessage(e) {
+      if (e.data.status === "callback") {
+        switch(e.data.data) {
+          case "eventsource":
+            doEventSource();
+            window.removeEventListener("message", onMessage);
+            break;
+          default:
+            ok(false, "Something went wrong")
+            break
+        }
+      }
+    });
+
+    navigator.serviceWorker.ready.then(function() {
+      parent.postMessage({status: "callback", data: "ready"}, "*");
+    });
+
+    navigator.serviceWorker.addEventListener("message", function(event) {
+      parent.postMessage(event.data, "*");
+    });
+  }
+
+  </script>
+</head>
+<body onload="onLoad()">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response_intercept_worker.js
@@ -0,0 +1,18 @@
+var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/eventsource/';
+
+self.importScripts('eventsource_worker_helper.js');
+
+self.addEventListener('fetch', function (event) {
+  var request = event.request;
+  var url = new URL(request.url);
+
+  if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') {
+    return;
+  }
+
+  ok(request.mode === 'cors', 'EventSource should make a CORS request');
+  var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'cors'});
+  event.respondWith(fetch(fetchRequest).then((fetchResponse) => {
+    return fetchResponse;
+  }));
+});
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response.html
@@ -0,0 +1,75 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
+  <script type="text/javascript">
+
+  var prefix = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/eventsource/";
+
+  function ok(aCondition, aMessage) {
+    parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*");
+  }
+
+  function doUnregister() {
+    navigator.serviceWorker.getRegistration().then(swr => {
+      swr.unregister().then(function(result) {
+        ok(result, "Unregister should return true.");
+        parent.postMessage({status: "callback", data: "done"}, "*");
+      }, function(e) {
+        ok(false, "Unregistering the SW failed with " + e);
+      });
+    });
+  }
+
+  function doEventSource() {
+    var source = new EventSource(prefix + "eventsource.resource");
+    source.onmessage = function(e) {
+      source.onmessage = null;
+      source.close();
+      ok(false, "Something went wrong");
+    };
+    source.onerror = function(error) {
+      source.onerror = null;
+      source.close();
+      ok(true, "EventSource should not work with opaque responses");
+      doUnregister();
+    };
+  }
+
+  function onLoad() {
+    if (!parent) {
+      dump("eventsource/eventsource_opaque_response.html shouldn't be launched directly!");
+    }
+
+    window.addEventListener("message", function onMessage(e) {
+      if (e.data.status === "callback") {
+        switch(e.data.data) {
+          case "eventsource":
+            doEventSource();
+            window.removeEventListener("message", onMessage);
+            break;
+          default:
+            ok(false, "Something went wrong")
+            break
+        }
+      }
+    });
+
+    navigator.serviceWorker.ready.then(function() {
+      parent.postMessage({status: "callback", data: "ready"}, "*");
+    });
+
+    navigator.serviceWorker.addEventListener("message", function(event) {
+      parent.postMessage(event.data, "*");
+    });
+  }
+
+  </script>
+</head>
+<body onload="onLoad()">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response_intercept_worker.js
@@ -0,0 +1,19 @@
+// Cross origin request
+var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/eventsource/';
+
+self.importScripts('eventsource_worker_helper.js');
+
+self.addEventListener('fetch', function (event) {
+  var request = event.request;
+  var url = new URL(request.url);
+
+  if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') {
+    return;
+  }
+
+  ok(request.mode === 'cors', 'EventSource should make a CORS request');
+  var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'no-cors'});
+  event.respondWith(fetch(fetchRequest).then((fetchResponse) => {
+    return fetchResponse;
+  }));
+});
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource_register_worker.html
@@ -0,0 +1,27 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
+  <script type="text/javascript">
+
+  function getURLParam (aTarget, aValue) {
+    return decodeURI(aTarget.search.replace(new RegExp("^(?:.*[&\\?]" + encodeURI(aValue).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1"));
+  }
+
+  function onLoad() {
+    navigator.serviceWorker.ready.then(function() {
+      parent.postMessage({status: "callback", data: "done"}, "*");
+    });
+
+    navigator.serviceWorker.register(getURLParam(document.location, "script"), {scope: "."});
+  }
+
+  </script>
+</head>
+<body onload="onLoad()">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response.html
@@ -0,0 +1,75 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
+  <script type="text/javascript">
+
+  var prefix = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/eventsource/";
+
+  function ok(aCondition, aMessage) {
+    parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*");
+  }
+
+  function doUnregister() {
+    navigator.serviceWorker.getRegistration().then(swr => {
+      swr.unregister().then(function(result) {
+        ok(result, "Unregister should return true.");
+        parent.postMessage({status: "callback", data: "done"}, "*");
+      }, function(e) {
+        ok(false, "Unregistering the SW failed with " + e);
+      });
+    });
+  }
+
+  function doEventSource() {
+    var source = new EventSource(prefix + "eventsource.resource");
+    source.onmessage = function(e) {
+      source.onmessage = null;
+      source.close();
+      ok(true, "EventSource should work with synthetic responses");
+      doUnregister();
+    };
+    source.onerror = function(error) {
+      source.onmessage = null;
+      source.close();
+      ok(false, "Something went wrong");
+    };
+  }
+
+  function onLoad() {
+    if (!parent) {
+      dump("eventsource/eventsource_synthetic_response.html shouldn't be launched directly!");
+    }
+
+    window.addEventListener("message", function onMessage(e) {
+      if (e.data.status === "callback") {
+        switch(e.data.data) {
+          case "eventsource":
+            doEventSource();
+            window.removeEventListener("message", onMessage);
+            break;
+          default:
+            ok(false, "Something went wrong")
+            break
+        }
+      }
+    });
+
+    navigator.serviceWorker.ready.then(function() {
+      parent.postMessage({status: "callback", data: "ready"}, "*");
+    });
+
+    navigator.serviceWorker.addEventListener("message", function(event) {
+      parent.postMessage(event.data, "*");
+    });
+  }
+
+  </script>
+</head>
+<body onload="onLoad()">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response_intercept_worker.js
@@ -0,0 +1,24 @@
+self.importScripts('eventsource_worker_helper.js');
+
+self.addEventListener('fetch', function (event) {
+  var request = event.request;
+  var url = new URL(request.url);
+
+  if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') {
+    return;
+  }
+
+  ok(request.mode === 'cors', 'EventSource should make a CORS request');
+  var headerList = {
+    'Content-Type': 'text/event-stream',
+    'Cache-Control': 'no-cache, must-revalidate'
+  };
+  var headers = new Headers(headerList);
+  var init = {
+    headers: headers,
+    mode: 'cors'
+  };
+  var body = 'data: data0\r\r';
+  var response = new Response(body, init);
+  event.respondWith(response);
+});
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/eventsource/eventsource_worker_helper.js
@@ -0,0 +1,12 @@
+function ok(aCondition, aMessage) {
+  return new Promise(function(resolve, reject) {
+    self.clients.matchAll().then(function(res) {
+      if (!res.length) {
+        reject();
+        return;
+      }
+      res[0].postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage});
+      resolve();
+    });
+  });
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/plugin/plugins.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<script>
+  var obj, embed;
+
+  function ok(v, msg) {
+    window.opener.postMessage({status: "ok", result: !!v, message: msg}, "*");
+  }
+
+  function finish() {
+    document.documentElement.removeChild(obj);
+    document.documentElement.removeChild(embed);
+    window.opener.postMessage({status: "done"}, "*");
+  }
+
+  function test_object() {
+    obj = document.createElement("object");
+    obj.setAttribute('data', "object");
+    document.documentElement.appendChild(obj);
+  }
+
+  function test_embed() {
+    embed = document.createElement("embed");
+    embed.setAttribute('src', "embed");
+    document.documentElement.appendChild(embed);
+  }
+
+  navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+    if (e.data.context === "object") {
+      ok(false, "<object> should not be intercepted");
+    } else if (e.data.context === "embed") {
+      ok(false, "<embed> should not be intercepted");
+    } else if (e.data.context === "fetch" && e.data.resource === "foo.txt") {
+      navigator.serviceWorker.removeEventListener("message", onMessage);
+      finish();
+    }
+  }, false);
+
+  test_object();
+  test_embed();
+  // SW will definitely intercept fetch API, use this to see if plugins are
+  // intercepted before fetch().
+  fetch("foo.txt");
+</script>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/plugin/worker.js
@@ -0,0 +1,7 @@
+self.addEventListener("fetch", function(event) {
+  var resource = event.request.url.split('/').pop();
+  if (event.client) {
+    event.client.postMessage({context: event.request.context,
+                              resource: resource});
+  }
+});
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -151,16 +151,19 @@ support-files =
   sw_clients/navigator.html
   eval_worker.js
   test_eval_not_allowed.html^headers^
   opaque_intercept_worker.js
   notify_loaded.js
   test_request_context.js
   fetch_event_client.js
   sw_clients/dummy.html
+  fetch/plugin/worker.js
+  fetch/plugin/plugins.html
+  eventsource/*
 
 [test_app_protocol.html]
 skip-if = release_build
 [test_bug1151916.html]
 [test_claim.html]
 [test_claim_fetch.html]
 [test_claim_oninstall.html]
 [test_close.html]
@@ -239,8 +242,10 @@ skip-if = toolkit == "android" || toolki
 [test_strict_mode_warning.html]
 [test_third_party_iframes.html]
 [test_unregister.html]
 [test_workerUnregister.html]
 [test_workerUpdate.html]
 [test_workerupdatefoundevent.html]
 [test_opaque_intercept.html]
 [test_fetch_event_client_postmessage.html]
+[test_eventsource_intercept.html]
+[test_not_intercept_plugin.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_eventsource_intercept.html
@@ -0,0 +1,105 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+  function testFrame(src) {
+    return new Promise(function(resolve, reject) {
+      var iframe = document.createElement("iframe");
+      iframe.src = src;
+      window.onmessage = function(e) {
+        if (e.data.status == "callback") {
+          switch(e.data.data) {
+            case "ok":
+              ok(e.data.condition, e.data.message);
+              break;
+            case "ready":
+              iframe.contentWindow.postMessage({status: "callback", data: "eventsource"}, "*");
+              break;
+            case "done":
+              window.onmessage = null;
+              iframe.src = "about:blank";
+              document.body.removeChild(iframe);
+              iframe = null;
+              resolve();
+              break;
+            default:
+              ok(false, "Something went wrong");
+              break;
+          }
+        } else {
+          ok(false, "Something went wrong");
+        }
+      };
+      document.body.appendChild(iframe);
+    });
+  }
+
+  function runTest() {
+      Promise.resolve()
+        .then(() => {
+        info("Going to intercept and test opaque responses");
+        return testFrame("eventsource/eventsource_register_worker.html" +
+                         "?script=eventsource_opaque_response_intercept_worker.js");
+      })
+      .then(() => {
+        return testFrame("eventsource/eventsource_opaque_response.html");
+      })
+      .then(() => {
+        info("Going to intercept and test cors responses");
+        return testFrame("eventsource/eventsource_register_worker.html" +
+                         "?script=eventsource_cors_response_intercept_worker.js");
+      })
+      .then(() => {
+        return testFrame("eventsource/eventsource_cors_response.html");
+      })
+      .then(() => {
+        info("Going to intercept and test synthetic responses");
+        return testFrame("eventsource/eventsource_register_worker.html" +
+                         "?script=eventsource_synthetic_response_intercept_worker.js");
+      })
+      .then(() => {
+        return testFrame("eventsource/eventsource_synthetic_response.html");
+      })
+      .then(() => {
+        info("Going to intercept and test mixed content cors responses");
+        return testFrame("https://example.com/tests/dom/workers/test/serviceworkers/" +
+                         "eventsource/eventsource_register_worker.html" +
+                         "?script=eventsource_mixed_content_cors_response_intercept_worker.js");
+      })
+      .then(() => {
+        return testFrame("https://example.com/tests/dom/workers/test/serviceworkers/" +
+                         "eventsource/eventsource_mixed_content_cors_response.html");
+      })
+      .then(SimpleTest.finish)
+      .catch(function(e) {
+        ok(false, "Some test failed with error " + e);
+        SimpleTest.finish();
+      });
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+    ["dom.serviceWorkers.interception.enabled", true],
+    ["dom.serviceWorkers.interception.opaque.enabled", true],
+    ["dom.caches.enabled", true],
+  ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_not_intercept_plugin.html
@@ -0,0 +1,80 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1187766 - Test loading plugins scenarios with fetch interception.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+  SimpleTest.requestCompleteLog();
+
+  var registration;
+  function simpleRegister() {
+    var p = navigator.serviceWorker.register("./fetch/plugin/worker.js", { scope: "./fetch/plugin/" });
+    return p.then(function(swr) {
+      registration = swr;
+      return new Promise(function(resolve) {
+        swr.installing.onstatechange = resolve;
+      });
+    });
+  }
+
+  function unregister() {
+    return registration.unregister().then(function(success) {
+      ok(success, "Service worker should be unregistered successfully");
+    }, function(e) {
+      dump("SW unregistration error: " + e + "\n");
+    });
+  }
+
+  function testPlugins() {
+    var p = new Promise(function(resolve, reject) {
+      window.onmessage = function(e) {
+        if (e.data.status == "ok") {
+          ok(e.data.result, e.data.message);
+        } else if (e.data.status == "done") {
+          window.onmessage = null;
+          w.close();
+          resolve();
+        }
+      }
+    });
+
+    var w = window.open("fetch/plugin/plugins.html");
+    return p;
+  }
+
+  function runTest() {
+    simpleRegister()
+      .then(testPlugins)
+      .then(unregister)
+      .then(function() {
+        SimpleTest.finish();
+      }).catch(function(e) {
+        ok(false, "Some test failed with error " + e);
<