Compare commits
527
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e2e19d639 | ||
|
|
0379f29f80 | ||
|
|
186d4b7132 | ||
|
|
a85b4739e4 | ||
|
|
dad82a67d6 | ||
|
|
1359f67e56 | ||
|
|
a68e524a90 | ||
|
|
e354a428b7 | ||
|
|
031f4f5504 | ||
|
|
98a18afe5b | ||
|
|
0c7e7591b7 | ||
|
|
c3617331a1 | ||
|
|
3415e1d32d | ||
|
|
f687581b7f | ||
|
|
6d9f234c60 | ||
|
|
2067485b27 | ||
|
|
9cd981b485 | ||
|
|
88f7cba9c9 | ||
|
|
6cea01f811 | ||
|
|
0fdd23e0b4 | ||
|
|
ad4b2ecefa | ||
|
|
cc14ee0148 | ||
|
|
483420f253 | ||
|
|
4d76651ff2 | ||
|
|
33d29efe43 | ||
|
|
416d9683fe | ||
|
|
3481496454 | ||
|
|
299090f227 | ||
|
|
9d03184aa2 | ||
|
|
7598e16eaa | ||
|
|
85f0301dd8 | ||
|
|
d3ed316788 | ||
|
|
131409a24b | ||
|
|
4a052de454 | ||
|
|
c36eae9bf5 | ||
|
|
da09b1d972 | ||
|
|
93addf962f | ||
|
|
c09fb2cd97 | ||
|
|
ad7ffc0525 | ||
|
|
500ece900a | ||
|
|
f90106bd7e | ||
|
|
5a0b5b05da | ||
|
|
77692ac0bf | ||
|
|
1a24ac966e | ||
|
|
c26df1a48a | ||
|
|
0a2eb24435 | ||
|
|
2e2855cf04 | ||
|
|
73d78767c8 | ||
|
|
5b6c6b98d2 | ||
|
|
f23839412e | ||
|
|
86b77f1284 | ||
|
|
ecda3426e6 | ||
|
|
eae71465d0 | ||
|
|
19b76a68ac | ||
|
|
9365fbd8ad | ||
|
|
431f782f17 | ||
|
|
ee870812ff | ||
|
|
434379eb34 | ||
|
|
2f3f8e13eb | ||
|
|
ef7ff8da4b | ||
|
|
2733585a3f | ||
|
|
039b67f6c6 | ||
|
|
c0e6ba933e | ||
|
|
4665c79582 | ||
|
|
7c82808510 | ||
|
|
c5cb2983e6 | ||
|
|
9c5999c8bc | ||
|
|
8313fea7c5 | ||
|
|
5fc444a0b6 | ||
|
|
d2be38e92b | ||
|
|
fc5d7a94b7 | ||
|
|
8206716e8d | ||
|
|
376bae925b | ||
|
|
494ec06817 | ||
|
|
ef8565458e | ||
|
|
537a5d447e | ||
|
|
da24537414 | ||
|
|
9700f5557b | ||
|
|
0c63f5a273 | ||
|
|
16baa04533 | ||
|
|
eac3436960 | ||
|
|
09513970d8 | ||
|
|
1a19248860 | ||
|
|
df5696425e | ||
|
|
d8a518b4db | ||
|
|
a343d564a6 | ||
|
|
5d6aca7e55 | ||
|
|
705e2e17be | ||
|
|
53adec5e96 | ||
|
|
88d2d5eef6 | ||
|
|
5e6a0efb65 | ||
|
|
0c35e43441 | ||
|
|
9477683481 | ||
|
|
465a18f342 | ||
|
|
4435cfeeae | ||
|
|
8577fab815 | ||
|
|
972e0410ca | ||
|
|
b805d9da66 | ||
|
|
948268ffde | ||
|
|
bddc8b630c | ||
|
|
68f456b5fd | ||
|
|
9475b959d7 | ||
|
|
17e261ae9b | ||
|
|
1122d6cba6 | ||
|
|
79201ad741 | ||
|
|
c3a35f75a2 | ||
|
|
6bb4747ff3 | ||
|
|
26b3d4c151 | ||
|
|
31e7e7e66c | ||
|
|
396549c5ac | ||
|
|
0f87c37c5d | ||
|
|
482ef6ac19 | ||
|
|
b26511f287 | ||
|
|
da908629c1 | ||
|
|
6a6777e73e | ||
|
|
c7dff3002f | ||
|
|
9f009467d8 | ||
|
|
d3d61f478c | ||
|
|
5ed6e2e676 | ||
|
|
3089da7aeb | ||
|
|
26201f3795 | ||
|
|
4ca103d1b8 | ||
|
|
1d594e9b3e | ||
|
|
b29ef13cd6 | ||
|
|
16c5b95b91 | ||
|
|
e848bea43f | ||
|
|
4c4bc3ca0f | ||
|
|
014e4b6e7f | ||
|
|
ac589d72a2 | ||
|
|
14746314c4 | ||
|
|
18c6aa0252 | ||
|
|
40c9c92116 | ||
|
|
e9a1859915 | ||
|
|
de225cca0d | ||
|
|
476dec2351 | ||
|
|
1affd523ec | ||
|
|
21668cd7d3 | ||
|
|
713ada0ccf | ||
|
|
a4e18703e9 | ||
|
|
9fafaedafb | ||
|
|
e923f5aacd | ||
|
|
3f7b0b7164 | ||
|
|
dd32c96ac6 | ||
|
|
71a3487554 | ||
|
|
517ab728ac | ||
|
|
4d38cb76c2 | ||
|
|
98e96503e6 | ||
|
|
a75c4164e1 | ||
|
|
958b02c358 | ||
|
|
5b9f0fd796 | ||
|
|
6cdb875cc5 | ||
|
|
f7e1c71930 | ||
|
|
3e37869990 | ||
|
|
ce47a81c57 | ||
|
|
e7cffa5316 | ||
|
|
50124e9ea6 | ||
|
|
d8befb4395 | ||
|
|
d38d687150 | ||
|
|
cd76bf5aa1 | ||
|
|
851e691089 | ||
|
|
055ccba3c3 | ||
|
|
6cc4505e14 | ||
|
|
8caa472402 | ||
|
|
f9ee95f207 | ||
|
|
88b54a8b67 | ||
|
|
744da8ddb3 | ||
|
|
6a1b4a9e3a | ||
|
|
7669ddb4b7 | ||
|
|
8edbed3761 | ||
|
|
125b8ae43c | ||
|
|
59dbe81c92 | ||
|
|
00c570d27b | ||
|
|
2fbefbbc58 | ||
|
|
59b4e5ceb2 | ||
|
|
6ffdfa793e | ||
|
|
fb9484caa3 | ||
|
|
4390724666 | ||
|
|
a69e82b0d7 | ||
|
|
3d82baddf3 | ||
|
|
60d420fe71 | ||
|
|
9e2c43705b | ||
|
|
f0e4bf4035 | ||
|
|
1c811a1476 | ||
|
|
43c816cd19 | ||
|
|
409d3e66de | ||
|
|
c7aa6e40f6 | ||
|
|
8744033a34 | ||
|
|
4b7f6babe3 | ||
|
|
059af6d121 | ||
|
|
9e2751484a | ||
|
|
cd86d9ce3f | ||
|
|
6da7d30e76 | ||
|
|
953a2eab75 | ||
|
|
f3c063a868 | ||
|
|
41448d401d | ||
|
|
679e53697c | ||
|
|
047e44d5c7 | ||
|
|
eff173129a | ||
|
|
a578a28dd8 | ||
|
|
c64fe52b28 | ||
|
|
338cbff242 | ||
|
|
c03bb68372 | ||
|
|
e736dc2d34 | ||
|
|
8ed007c4e8 | ||
|
|
466b773179 | ||
|
|
092ff20c60 | ||
|
|
f6e1f851d0 | ||
|
|
98fef024cd | ||
|
|
c47d20f3f3 | ||
|
|
3e108a2c4e | ||
|
|
e3275643e2 | ||
|
|
6c2fef44cf | ||
|
|
acbce95aa3 | ||
|
|
468a37eab8 | ||
|
|
9d7d704523 | ||
|
|
a28ce48a16 | ||
|
|
630e34a966 | ||
|
|
fc408bf1e2 | ||
|
|
3a2adcff71 | ||
|
|
dabadd700c | ||
|
|
826464c80b | ||
|
|
f17feba05f | ||
|
|
4493017fd8 | ||
|
|
bae06ce171 | ||
|
|
f2ef28d09c | ||
|
|
b9f3a03a5f | ||
|
|
5b21c547de | ||
|
|
ee7201bbb4 | ||
|
|
b2af611dc1 | ||
|
|
e0226ccae6 | ||
|
|
9c1d44f5f6 | ||
|
|
ab5a651d55 | ||
|
|
ae59eb00e5 | ||
|
|
9d9d93381e | ||
|
|
d022966201 | ||
|
|
c2ed566b06 | ||
|
|
2f5798b5db | ||
|
|
31c074ce15 | ||
|
|
f19504608e | ||
|
|
adc21f14fa | ||
|
|
828c8cc46f | ||
|
|
bc399f7871 | ||
|
|
024b7500d1 | ||
|
|
9b1b294e29 | ||
|
|
33bc5535f2 | ||
|
|
3076df399c | ||
|
|
aa43190b9a | ||
|
|
ae476cc42c | ||
|
|
a434994229 | ||
|
|
8d980914af | ||
|
|
97f69387f4 | ||
|
|
8d6c043b89 | ||
|
|
41bab244fc | ||
|
|
fb73c3aa3b | ||
|
|
078a424cf3 | ||
|
|
576ac835f0 | ||
|
|
ac4240f055 | ||
|
|
233feee823 | ||
|
|
df8d3c4b0a | ||
|
|
d3abfaff82 | ||
|
|
5cf6aa9065 | ||
|
|
1491d55375 | ||
|
|
f53901c567 | ||
|
|
a63ea3fdfb | ||
|
|
fe7e00d47f | ||
|
|
face1e6e9b | ||
|
|
22b189727f | ||
|
|
9ad97da85b | ||
|
|
5f9d4f115e | ||
|
|
29e2547f26 | ||
|
|
24f8c4f052 | ||
|
|
52821c2e85 | ||
|
|
2b0f6aea16 | ||
|
|
31adccdffc | ||
|
|
b13cd385a4 | ||
|
|
3091ff4e57 | ||
|
|
8ca01873c0 | ||
|
|
013e19eed6 | ||
|
|
6a353ef32d | ||
|
|
05ad5bcb5b | ||
|
|
42742eceae | ||
|
|
ffdba3d91e | ||
|
|
1f32c17fcf | ||
|
|
8b26fcd0de | ||
|
|
1a2a779aaf | ||
|
|
dc14e5a906 | ||
|
|
fb8347c7d2 | ||
|
|
aa0d7fc060 | ||
|
|
04e13794b2 | ||
|
|
2421d31e02 | ||
|
|
0148ae5a60 | ||
|
|
ec38c883c6 | ||
|
|
e3c44ebafa | ||
|
|
c9764f76ec | ||
|
|
a1d573eb13 | ||
|
|
94e2874370 | ||
|
|
a13035b1de | ||
|
|
d7c90a831a | ||
|
|
243c2bd498 | ||
|
|
e57d4347cd | ||
|
|
5732cf1f17 | ||
|
|
ae4149e7ee | ||
|
|
40ef2a5756 | ||
|
|
15a0dd6e4c | ||
|
|
c1b1638522 | ||
|
|
c805a6a526 | ||
|
|
82222eb7b4 | ||
|
|
0dc2b249dc | ||
|
|
f2eeb76969 | ||
|
|
6445a1bdb4 | ||
|
|
94df491338 | ||
|
|
a6ceedf7b2 | ||
|
|
47c6fe9cee | ||
|
|
64370b64d2 | ||
|
|
523201f3e4 | ||
|
|
997616e308 | ||
|
|
f5761df1c7 | ||
|
|
c566c36791 | ||
|
|
5a4478b409 | ||
|
|
cce4862b70 | ||
|
|
c06a9ae523 | ||
|
|
aa108a39d6 | ||
|
|
b87775f75a | ||
|
|
d72c98277b | ||
|
|
9f07ad27f5 | ||
|
|
b1440d285b | ||
|
|
d78912ec17 | ||
|
|
d454045363 | ||
|
|
44adc9e25c | ||
|
|
7cde54d976 | ||
|
|
13eac0ae3f | ||
|
|
0d1d366685 | ||
|
|
9536cf49ce | ||
|
|
bd9042426a | ||
|
|
76f10666f4 | ||
|
|
c8b74d4cbd | ||
|
|
25ed473471 | ||
|
|
304dfb8da2 | ||
|
|
2c4410681f | ||
|
|
5b3edcd412 | ||
|
|
8dadf744e3 | ||
|
|
e7268f447a | ||
|
|
d3830bea8d | ||
|
|
4ebfc47dff | ||
|
|
9694f5c216 | ||
|
|
1264824127 | ||
|
|
b5f8fa6479 | ||
|
|
af3a27e838 | ||
|
|
1d4532ac5e | ||
|
|
2e8442e6ec | ||
|
|
bbd7436086 | ||
|
|
61dccf9933 | ||
|
|
cbcf9b9d8e | ||
|
|
d28497f2cf | ||
|
|
919a966dc2 | ||
|
|
b618b3fb76 | ||
|
|
e7fd4ba61b | ||
|
|
608ecc9549 | ||
|
|
61b688198a | ||
|
|
7bcc49489a | ||
|
|
45193b1a82 | ||
|
|
f404243371 | ||
|
|
a943408c35 | ||
|
|
34ccfbcc0e | ||
|
|
4731658b7e | ||
|
|
4a3caaf522 | ||
|
|
edaddb1e5d | ||
|
|
028e97d11e | ||
|
|
a6d113f87b | ||
|
|
413cb600cc | ||
|
|
4ed26484ee | ||
|
|
2a77e5662d | ||
|
|
1555b1d89c | ||
|
|
080877ccec | ||
|
|
dcf9837607 | ||
|
|
5de28553a1 | ||
|
|
6249e60f41 | ||
|
|
fb305d451f | ||
|
|
154b94d350 | ||
|
|
ea2d754647 | ||
|
|
0063a28576 | ||
|
|
4e9a2f783e | ||
|
|
84acd8c0ef | ||
|
|
fba59dfc4b | ||
|
|
7ff4b0d120 | ||
|
|
13e658a200 | ||
|
|
abf74404c5 | ||
|
|
cfc61b4c5d | ||
|
|
012669ee58 | ||
|
|
6f635acadc | ||
|
|
fc4d64665f | ||
|
|
ada110f579 | ||
|
|
1356ecde96 | ||
|
|
3d07029232 | ||
|
|
3f2b011ee6 | ||
|
|
9a9e4e0c9a | ||
|
|
58f3b2c581 | ||
|
|
b356d64f4e | ||
|
|
9a0807a62c | ||
|
|
ffab95c3a3 | ||
|
|
4a290b16b0 | ||
|
|
4e252574e0 | ||
|
|
94f456f834 | ||
|
|
37bbb4152d | ||
|
|
fc62ce0076 | ||
|
|
b9d00e8c27 | ||
|
|
a2a0dec11d | ||
|
|
3e9621aa59 | ||
|
|
03875cd609 | ||
|
|
d9fb68d21b | ||
|
|
df2b82e2cb | ||
|
|
c34cceb678 | ||
|
|
6343937b2c | ||
|
|
b9894f1e93 | ||
|
|
c8bafb9faa | ||
|
|
ecd5436c58 | ||
|
|
b019e959d4 | ||
|
|
ab4cc6ebae | ||
|
|
925b33158b | ||
|
|
daf859d84c | ||
|
|
b66e0ffdc4 | ||
|
|
8f266bb6e7 | ||
|
|
2e888727e0 | ||
|
|
bc165aebf8 | ||
|
|
9d0a8340d7 | ||
|
|
abc044bc61 | ||
|
|
a061db91dd | ||
|
|
0494d561c5 | ||
|
|
41fed1ebf8 | ||
|
|
a585aeacf0 | ||
|
|
814fa6e3ba | ||
|
|
541508cd83 | ||
|
|
2770a0588c | ||
|
|
88f392397b | ||
|
|
6efb151520 | ||
|
|
058474229d | ||
|
|
eb87672d4c | ||
|
|
4b9789888f | ||
|
|
cc98224267 | ||
|
|
7001a57aef | ||
|
|
173320cf7d | ||
|
|
5f243826b4 | ||
|
|
3d4474a977 | ||
|
|
06bc499a0d | ||
|
|
c127f58ef3 | ||
|
|
5e6006a2bd | ||
|
|
ac9253e987 | ||
|
|
8778d48290 | ||
|
|
fb0c204f2b | ||
|
|
e6888aa2bd | ||
|
|
d14c78b29d | ||
|
|
76e7290c1a | ||
|
|
d19fb35c6a | ||
|
|
94554142e9 | ||
|
|
1c04b522d3 | ||
|
|
84d3226fb1 | ||
|
|
42d1d20efd | ||
|
|
3505c2050a | ||
|
|
1139e11f3a | ||
|
|
b19541f1d4 | ||
|
|
30653de864 | ||
|
|
7a82ec5764 | ||
|
|
e73a2fb0fc | ||
|
|
b14db5e91e | ||
|
|
2d752c9259 | ||
|
|
362a84565c | ||
|
|
d8ef6bf7af | ||
|
|
825fb3a207 | ||
|
|
d1345b1da6 | ||
|
|
4f95caf7fb | ||
|
|
528ae6aeb0 | ||
|
|
ea0a08fcab | ||
|
|
7ac438f6ad | ||
|
|
3142f95469 | ||
|
|
74b3ce90d7 | ||
|
|
ebd7672d6b | ||
|
|
1f78e93f1d | ||
|
|
69502301fa | ||
|
|
d10e8a5b06 | ||
|
|
fb173db64b | ||
|
|
5bc4892122 | ||
|
|
58670d66e5 | ||
|
|
4ba7e05b96 | ||
|
|
2dc264047e | ||
|
|
6d5b934dc0 | ||
|
|
87ef3606e2 | ||
|
|
4ced37546e | ||
|
|
cb61e1a354 | ||
|
|
b83dd4e5e8 | ||
|
|
95907ddf88 | ||
|
|
5f8d70dacd | ||
|
|
4bf2bf74b1 | ||
|
|
b575f4a516 | ||
|
|
47a7de8aa4 | ||
|
|
1a5e2e32e1 | ||
|
|
d06666cd12 | ||
|
|
24ee0680b1 | ||
|
|
66a4e02856 | ||
|
|
9fea63c9d4 | ||
|
|
fc5b767f5d | ||
|
|
302d46a0f9 | ||
|
|
d2683b0ff7 | ||
|
|
92d5680b85 | ||
|
|
f9a1ebf886 | ||
|
|
a68021d41e | ||
|
|
82ac99acb5 | ||
|
|
c623c47157 | ||
|
|
da835adb48 | ||
|
|
a9c9be4d21 | ||
|
|
0682d4ac0e | ||
|
|
77bca114d1 | ||
|
|
59f6ae3d74 | ||
|
|
1fba5c3cc7 | ||
|
|
3d99e06afe | ||
|
|
ed2a457b0a | ||
|
|
b1b57cefa3 | ||
|
|
6619e475ab | ||
|
|
f369fbd6f7 | ||
|
|
2db2e0fe58 | ||
|
|
10a37f462e | ||
|
|
922edb1d93 | ||
|
|
cba46e5628 | ||
|
|
3173108a3f | ||
|
|
5f4d519194 | ||
|
|
2ad17268ed | ||
|
|
888c35be89 | ||
|
|
73e06b6df9 |
No files matched your search
@@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Issue #1971 - Update to ICU 63 requires clobber
|
||||
Touching ICU's build system requires a CLOBBER. (Issue #2165)
|
||||
+8
-15
@@ -160,18 +160,12 @@ endif
|
||||
.PHONY: install-manifests
|
||||
install-manifests: $(addprefix install-,$(install_manifests))
|
||||
|
||||
# process_install_manifest needs to be invoked with --no-remove when building
|
||||
# js as standalone because automated builds are building nspr separately and
|
||||
# that would remove the resulting files.
|
||||
# Eventually, a standalone js build would just be able to build nspr itself,
|
||||
# removing the need for the former.
|
||||
ifdef JS_STANDALONE
|
||||
NO_REMOVE=1
|
||||
endif
|
||||
|
||||
.PHONY: $(addprefix install-,$(subst /,_,$(install_manifests)))
|
||||
.PHONY: $(addprefix install-,$(install_manifests))
|
||||
$(addprefix install-,$(install_manifests)): install-%: $(install_manifest_depends)
|
||||
$(addprefix $(call py_action,process_install_manifest,$(if $(NO_REMOVE),--no-remove )$*) ,$(wildcard _build_manifests/install/$(subst /,_,$*)))
|
||||
$(addprefix $(call py_action,process_install_manifest,--track install_$(subst /,_,$*).track $*) ,$(wildcard _build_manifests/install/$(subst /,_,$*)))
|
||||
|
||||
# Dummy wrapper rule to allow the faster backend to piggy back
|
||||
$(addprefix install-,$(subst /,_,$(filter dist/%,$(install_manifests)))): install-dist_%: install-dist/% ;
|
||||
|
||||
.PHONY: install-tests
|
||||
install-tests: install-test-files
|
||||
@@ -181,10 +175,9 @@ install-tests: install-test-files
|
||||
.PHONY: run-tests-deps
|
||||
run-tests-deps: $(install_manifest_depends)
|
||||
|
||||
# Force --no-remove, because $objdir/_tests is handled by multiple manifests.
|
||||
.PHONY: install-test-files
|
||||
install-test-files:
|
||||
$(call py_action,process_install_manifest,--no-remove _tests _build_manifests/install/_test_files)
|
||||
$(call py_action,process_install_manifest,--track install__test_files.track _tests _build_manifests/install/_test_files)
|
||||
|
||||
include $(topsrcdir)/build/moz-automation.mk
|
||||
|
||||
@@ -204,13 +197,13 @@ ifndef NO_PROFILE_GUIDED_OPTIMIZE
|
||||
ifneq ($(OS_ARCH)_$(GNU_CC), WINNT_)
|
||||
recurse_pre-export:: install-manifests
|
||||
binaries::
|
||||
@$(MAKE) install-manifests NO_REMOVE=1 install_manifests=dist/include
|
||||
@$(MAKE) install-manifests install_manifests=dist/include
|
||||
endif
|
||||
endif
|
||||
else # !MOZ_PROFILE_USE (normal build)
|
||||
recurse_pre-export:: install-manifests
|
||||
binaries::
|
||||
@$(MAKE) install-manifests NO_REMOVE=1 install_manifests=dist/include
|
||||
@$(MAKE) install-manifests install_manifests=dist/include
|
||||
endif
|
||||
|
||||
# For historical reasons that are unknown, $(DIST)/sdk is always blown away
|
||||
|
||||
@@ -33,5 +33,7 @@ If you fork this repository to perform your own work on it, please consider offe
|
||||
improvement patches upstream to its origin to mutually improve the platform and build
|
||||
a future for XUL.
|
||||
|
||||
## Looking for Pale Moon and Basilisk?
|
||||
As of [`Issue #969`](https://repo.palemoon.org/MoonchildProductions/UXP/issues/969), release versions `28.9.0` and `2020.03.04` respectively, they have been split off into their own dedicated repositories located at [`MoonchildProductions/Pale-Moon`](https://repo.palemoon.org/MoonchildProductions/Pale-Moon) and [`MoonchildProductions/Basilisk`](https://repo.palemoon.org/MoonchildProductions/Basilisk).
|
||||
## Looking for Pale Moon?
|
||||
As of [`Issue #969`](https://repo.palemoon.org/MoonchildProductions/UXP/issues/969), release version `28.9.0`,
|
||||
applications have been split off into their own dedicated (front-end) repositories, with Pale Moon located at
|
||||
[`MoonchildProductions/Pale-Moon`](https://repo.palemoon.org/MoonchildProductions/Pale-Moon).
|
||||
@@ -34,11 +34,10 @@ if test -n "$USE_ICU"; then
|
||||
ICU_DATA_FILE="icudt${version}l.dat"
|
||||
|
||||
dnl We won't build ICU data as a separate file when building
|
||||
dnl JS standalone so that embedders don't have to deal with it.
|
||||
dnl We also don't do it on Windows because sometimes the file goes
|
||||
dnl missing -- possibly due to overzealous antivirus software? --
|
||||
dnl which prevents the browser from starting up :(
|
||||
if test -z "$JS_STANDALONE"; then
|
||||
dnl ICU as a shared library, as we need to fold the data into
|
||||
dnl the shared library in order for consumers like Spidermonkey
|
||||
dnl to use it without code duplication.
|
||||
if test -z "$MOZ_SHARED_ICU"; then
|
||||
MOZ_ICU_DATA_ARCHIVE=1
|
||||
else
|
||||
MOZ_ICU_DATA_ARCHIVE=
|
||||
@@ -46,17 +45,18 @@ if test -n "$USE_ICU"; then
|
||||
fi
|
||||
|
||||
AC_SUBST(MOZ_ICU_VERSION)
|
||||
AC_SUBST(ENABLE_INTL_API)
|
||||
AC_SUBST(USE_ICU)
|
||||
AC_SUBST(ICU_DATA_FILE)
|
||||
AC_SUBST(MOZ_ICU_DATA_ARCHIVE)
|
||||
AC_SUBST(MOZ_SHARED_ICU)
|
||||
|
||||
if test -n "$USE_ICU"; then
|
||||
if test -z "$YASM" -a -z "$GNU_AS" -a "$COMPILE_ENVIRONMENT"; then
|
||||
AC_MSG_ERROR([Building ICU requires either yasm or a GNU assembler. If you do not have either of those available for this platform you must use --without-intl-api])
|
||||
fi
|
||||
dnl We build ICU as a static library.
|
||||
AC_DEFINE(U_STATIC_IMPLEMENTATION)
|
||||
if test -z "$MOZ_SHARED_ICU"; then
|
||||
AC_DEFINE(U_STATIC_IMPLEMENTATION)
|
||||
fi
|
||||
dnl Source files that use ICU should have control over which parts of the ICU
|
||||
dnl namespace they want to use.
|
||||
AC_DEFINE(U_USING_ICU_NAMESPACE,0)
|
||||
|
||||
@@ -156,7 +156,9 @@ def old_configure_options(*options):
|
||||
'--enable-accessibility',
|
||||
'--enable-address-sanitizer',
|
||||
'--enable-alsa',
|
||||
'--enable-appcompat-guid',
|
||||
'--enable-av1',
|
||||
'--enable-jxl',
|
||||
'--enable-b2g-bt',
|
||||
'--enable-b2g-camera',
|
||||
'--enable-b2g-ril',
|
||||
@@ -225,6 +227,7 @@ def old_configure_options(*options):
|
||||
'--enable-safe-browsing',
|
||||
'--enable-sandbox',
|
||||
'--enable-security-sqlstore',
|
||||
'--enable-shared-icu',
|
||||
'--enable-shared-media',
|
||||
'--enable-signmar',
|
||||
'--enable-simulator',
|
||||
@@ -272,6 +275,7 @@ def old_configure_options(*options):
|
||||
'--with-ios-sdk',
|
||||
'--with-jitreport-granularity',
|
||||
'--with-macbundlename-prefix',
|
||||
'--with-macbundle-identity',
|
||||
'--with-macos-private-frameworks',
|
||||
'--with-macos-sdk',
|
||||
'--with-nspr-cflags',
|
||||
@@ -306,7 +310,6 @@ def old_configure_options(*options):
|
||||
'--disable-browser-statusbar',
|
||||
'--disable-sync',
|
||||
'--disable-personas',
|
||||
'--enable-phoenix-extensions',
|
||||
|
||||
# Below are configure flags used by Basilisk
|
||||
'--disable-webextensions',
|
||||
|
||||
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
@@ -287,8 +287,6 @@ public:
|
||||
|
||||
virtual bool AddonHasPermission(const nsAString& aPerm);
|
||||
|
||||
virtual bool IsOnCSSUnprefixingWhitelist() override { return false; }
|
||||
|
||||
virtual bool IsCodebasePrincipal() const { return false; };
|
||||
|
||||
static BasePrincipal* Cast(nsIPrincipal* aPrin) { return static_cast<BasePrincipal*>(aPrin); }
|
||||
|
||||
+1
-10
@@ -21,7 +21,7 @@ interface nsIDOMDocument;
|
||||
[ptr] native JSPrincipals(JSPrincipals);
|
||||
[ptr] native PrincipalArray(nsTArray<nsCOMPtr<nsIPrincipal> >);
|
||||
|
||||
[scriptable, builtinclass, uuid(3da7b133-f1a0-4de9-a2bc-5c49014c1077)]
|
||||
[scriptable, builtinclass, uuid(f75f502d-79fd-48be-a079-e5a7b8f80c8b)]
|
||||
interface nsIPrincipal : nsISerializable
|
||||
{
|
||||
/**
|
||||
@@ -333,15 +333,6 @@ interface nsIPrincipal : nsISerializable
|
||||
* Returns true iff this is the system principal.
|
||||
*/
|
||||
[infallible] readonly attribute boolean isSystemPrincipal;
|
||||
|
||||
/**
|
||||
* Returns true if this principal's origin is recognized as being on the
|
||||
* whitelist of sites that can use the CSS Unprefixing Service.
|
||||
*
|
||||
* (This interface provides a trivial implementation, just returning false;
|
||||
* subclasses can implement something more complex as-needed.)
|
||||
*/
|
||||
[noscript,notxpcom,nostdcall] bool IsOnCSSUnprefixingWhitelist();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -85,7 +85,7 @@ nsJSPrincipals::Destroy(JSPrincipals *jsprin)
|
||||
#ifdef DEBUG
|
||||
|
||||
// Defined here so one can do principals->dump() in the debugger
|
||||
JS_PUBLIC_API(void)
|
||||
void
|
||||
JSPrincipals::dump()
|
||||
{
|
||||
if (debugToken == nsJSPrincipals::DEBUG_TOKEN) {
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
static bool gIsWhitelistingTestDomains = false;
|
||||
static bool gCodeBasePrincipalSupport = false;
|
||||
|
||||
static bool URIIsImmutable(nsIURI* aURI)
|
||||
@@ -61,10 +60,6 @@ NS_IMPL_CI_INTERFACE_GETTER(nsPrincipal,
|
||||
/* static */ void
|
||||
nsPrincipal::InitializeStatics()
|
||||
{
|
||||
Preferences::AddBoolVarCache(
|
||||
&gIsWhitelistingTestDomains,
|
||||
"layout.css.unprefixing-service.include-test-domains");
|
||||
|
||||
Preferences::AddBoolVarCache(&gCodeBasePrincipalSupport,
|
||||
"signed.applets.codebase_principal_support",
|
||||
false);
|
||||
@@ -483,196 +478,6 @@ nsPrincipal::Write(nsIObjectOutputStream* aStream)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Helper-function to indicate whether the CSS Unprefixing Service
|
||||
// whitelist should include dummy domains that are only intended for
|
||||
// use in testing. (Controlled by a pref.)
|
||||
static inline bool
|
||||
IsWhitelistingTestDomains()
|
||||
{
|
||||
return gIsWhitelistingTestDomains;
|
||||
}
|
||||
|
||||
// Checks if the given URI's host is on our "full domain" whitelist
|
||||
// (i.e. if it's an exact match against a domain that needs unprefixing)
|
||||
static bool
|
||||
IsOnFullDomainWhitelist(nsIURI* aURI)
|
||||
{
|
||||
nsAutoCString hostStr;
|
||||
nsresult rv = aURI->GetHost(hostStr);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
// NOTE: This static whitelist is expected to be short. If that changes,
|
||||
// we should consider a different representation; e.g. hash-set, prefix tree.
|
||||
static const nsLiteralCString sFullDomainsOnWhitelist[] = {
|
||||
// 0th entry only active when testing:
|
||||
NS_LITERAL_CSTRING("test1.example.org"),
|
||||
NS_LITERAL_CSTRING("map.baidu.com"),
|
||||
NS_LITERAL_CSTRING("3g.163.com"),
|
||||
NS_LITERAL_CSTRING("3glogo.gtimg.com"), // for 3g.163.com
|
||||
NS_LITERAL_CSTRING("info.3g.qq.com"), // for 3g.qq.com
|
||||
NS_LITERAL_CSTRING("3gimg.qq.com"), // for 3g.qq.com
|
||||
NS_LITERAL_CSTRING("img.m.baidu.com"), // for [shucheng|ks].baidu.com
|
||||
NS_LITERAL_CSTRING("m.mogujie.com"),
|
||||
NS_LITERAL_CSTRING("touch.qunar.com"),
|
||||
NS_LITERAL_CSTRING("mjs.sinaimg.cn"), // for sina.cn
|
||||
NS_LITERAL_CSTRING("static.qiyi.com"), // for m.iqiyi.com
|
||||
NS_LITERAL_CSTRING("cdn.kuaidi100.com"), // for m.kuaidi100.com
|
||||
NS_LITERAL_CSTRING("m.pc6.com"),
|
||||
NS_LITERAL_CSTRING("m.haosou.com"),
|
||||
NS_LITERAL_CSTRING("m.mi.com"),
|
||||
NS_LITERAL_CSTRING("wappass.baidu.com"),
|
||||
NS_LITERAL_CSTRING("m.video.baidu.com"),
|
||||
NS_LITERAL_CSTRING("m.video.baidu.com"),
|
||||
NS_LITERAL_CSTRING("imgcache.gtimg.cn"), // for m.v.qq.com
|
||||
NS_LITERAL_CSTRING("s.tabelog.jp"),
|
||||
NS_LITERAL_CSTRING("s.yimg.jp"), // for s.tabelog.jp
|
||||
NS_LITERAL_CSTRING("i.yimg.jp"), // for *.yahoo.co.jp
|
||||
NS_LITERAL_CSTRING("ai.yimg.jp"), // for *.yahoo.co.jp
|
||||
NS_LITERAL_CSTRING("m.finance.yahoo.co.jp"),
|
||||
NS_LITERAL_CSTRING("daily.c.yimg.jp"), // for sp.daily.co.jp
|
||||
NS_LITERAL_CSTRING("stat100.ameba.jp"), // for ameblo.jp
|
||||
NS_LITERAL_CSTRING("user.ameba.jp"), // for ameblo.jp
|
||||
NS_LITERAL_CSTRING("www.goo.ne.jp"),
|
||||
NS_LITERAL_CSTRING("x.gnst.jp"), // for mobile.gnavi.co.jp
|
||||
NS_LITERAL_CSTRING("c.x.gnst.jp"), // for mobile.gnavi.co.jp
|
||||
NS_LITERAL_CSTRING("www.smbc-card.com"),
|
||||
NS_LITERAL_CSTRING("static.card.jp.rakuten-static.com"), // for rakuten-card.co.jp
|
||||
NS_LITERAL_CSTRING("img.travel.rakuten.co.jp"), // for travel.rakuten.co.jp
|
||||
NS_LITERAL_CSTRING("img.mixi.net"), // for mixi.jp
|
||||
NS_LITERAL_CSTRING("girlschannel.net"),
|
||||
NS_LITERAL_CSTRING("www.fancl.co.jp"),
|
||||
NS_LITERAL_CSTRING("s.cosme.net"),
|
||||
NS_LITERAL_CSTRING("www.sapporobeer.jp"),
|
||||
NS_LITERAL_CSTRING("www.mapion.co.jp"),
|
||||
NS_LITERAL_CSTRING("touch.navitime.co.jp"),
|
||||
NS_LITERAL_CSTRING("sp.mbga.jp"),
|
||||
NS_LITERAL_CSTRING("ava-a.sp.mbga.jp"), // for sp.mbga.jp
|
||||
NS_LITERAL_CSTRING("www.ntv.co.jp"),
|
||||
NS_LITERAL_CSTRING("mobile.suntory.co.jp"), // for suntory.jp
|
||||
NS_LITERAL_CSTRING("www.aeonsquare.net"),
|
||||
NS_LITERAL_CSTRING("mw.nikkei.com"),
|
||||
NS_LITERAL_CSTRING("www.nhk.or.jp"),
|
||||
NS_LITERAL_CSTRING("www.tokyo-sports.co.jp"),
|
||||
NS_LITERAL_CSTRING("www.bellemaison.jp"),
|
||||
NS_LITERAL_CSTRING("www.kuronekoyamato.co.jp"),
|
||||
NS_LITERAL_CSTRING("formassist.jp"), // for orico.jp
|
||||
NS_LITERAL_CSTRING("sp.m.reuters.co.jp"),
|
||||
NS_LITERAL_CSTRING("www.atre.co.jp"),
|
||||
NS_LITERAL_CSTRING("www.jtb.co.jp"),
|
||||
NS_LITERAL_CSTRING("www.sharp.co.jp"),
|
||||
NS_LITERAL_CSTRING("www.biccamera.com"),
|
||||
NS_LITERAL_CSTRING("weathernews.jp"),
|
||||
NS_LITERAL_CSTRING("cache.ymail.jp"), // for www.yamada-denkiweb.com
|
||||
};
|
||||
static const size_t sNumFullDomainsOnWhitelist =
|
||||
MOZ_ARRAY_LENGTH(sFullDomainsOnWhitelist);
|
||||
|
||||
// Skip 0th (dummy) entry in whitelist, unless a pref is enabled.
|
||||
const size_t firstWhitelistIdx = IsWhitelistingTestDomains() ? 0 : 1;
|
||||
|
||||
for (size_t i = firstWhitelistIdx; i < sNumFullDomainsOnWhitelist; ++i) {
|
||||
if (hostStr == sFullDomainsOnWhitelist[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks if the given URI's host is on our "base domain" whitelist
|
||||
// (i.e. if it's a subdomain of some host that we've whitelisted as needing
|
||||
// unprefixing for all its subdomains)
|
||||
static bool
|
||||
IsOnBaseDomainWhitelist(nsIURI* aURI)
|
||||
{
|
||||
static const nsLiteralCString sBaseDomainsOnWhitelist[] = {
|
||||
// 0th entry only active when testing:
|
||||
NS_LITERAL_CSTRING("test2.example.org"),
|
||||
NS_LITERAL_CSTRING("tbcdn.cn"), // for m.taobao.com
|
||||
NS_LITERAL_CSTRING("alicdn.com"), // for m.taobao.com
|
||||
NS_LITERAL_CSTRING("dpfile.com"), // for m.dianping.com
|
||||
NS_LITERAL_CSTRING("hao123img.com"), // for hao123.com
|
||||
NS_LITERAL_CSTRING("tabelog.k-img.com"), // for s.tabelog.com
|
||||
NS_LITERAL_CSTRING("tsite.jp"), // for *.tsite.jp
|
||||
};
|
||||
static const size_t sNumBaseDomainsOnWhitelist =
|
||||
MOZ_ARRAY_LENGTH(sBaseDomainsOnWhitelist);
|
||||
|
||||
nsCOMPtr<nsIEffectiveTLDService> tldService =
|
||||
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
|
||||
|
||||
if (tldService) {
|
||||
// Skip 0th test-entry in whitelist, unless the testing pref is enabled.
|
||||
const size_t firstWhitelistIdx = IsWhitelistingTestDomains() ? 0 : 1;
|
||||
|
||||
// Right now, the test base-domain "test2.example.org" is the only entry in
|
||||
// its whitelist with a nonzero "depth". So we'll only bother going beyond
|
||||
// 0 depth (to 1) if that entry is enabled. (No point in slowing down the
|
||||
// normal codepath, for the benefit of a disabled test domain.) If we add a
|
||||
// "real" base-domain with a depth of >= 1 to our whitelist, we can get rid
|
||||
// of this conditional & just make this a static variable.
|
||||
const uint32_t maxSubdomainDepth = IsWhitelistingTestDomains() ? 1 : 0;
|
||||
|
||||
for (uint32_t subdomainDepth = 0;
|
||||
subdomainDepth <= maxSubdomainDepth; ++subdomainDepth) {
|
||||
|
||||
// Get the base domain (to depth |subdomainDepth|) from passed-in URI:
|
||||
nsAutoCString baseDomainStr;
|
||||
nsresult rv = tldService->GetBaseDomain(aURI, subdomainDepth,
|
||||
baseDomainStr);
|
||||
if (NS_FAILED(rv)) {
|
||||
// aURI doesn't have |subdomainDepth| levels of subdomains. If we got
|
||||
// here without a match yet, then aURI is not on our whitelist.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare the base domain against each entry in our whitelist:
|
||||
for (size_t i = firstWhitelistIdx; i < sNumBaseDomainsOnWhitelist; ++i) {
|
||||
if (baseDomainStr == sBaseDomainsOnWhitelist[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The actual (non-cached) implementation of IsOnCSSUnprefixingWhitelist():
|
||||
static bool
|
||||
IsOnCSSUnprefixingWhitelistImpl(nsIURI* aURI)
|
||||
{
|
||||
// Check scheme, so we can drop any non-HTTP/HTTPS URIs right away
|
||||
nsAutoCString schemeStr;
|
||||
nsresult rv = aURI->GetScheme(schemeStr);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
// Only proceed if scheme is "http" or "https"
|
||||
if (!(StringBeginsWith(schemeStr, NS_LITERAL_CSTRING("http")) &&
|
||||
(schemeStr.Length() == 4 ||
|
||||
(schemeStr.Length() == 5 && schemeStr[4] == 's')))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (IsOnFullDomainWhitelist(aURI) ||
|
||||
IsOnBaseDomainWhitelist(aURI));
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
nsPrincipal::IsOnCSSUnprefixingWhitelist()
|
||||
{
|
||||
if (mIsOnCSSUnprefixingWhitelist.isNothing()) {
|
||||
// Value not cached -- perform our lazy whitelist-check.
|
||||
// (NOTE: If our URI is mutable, we just assume it's not on the whitelist,
|
||||
// since our caching strategy won't work. This isn't expected to be common.)
|
||||
mIsOnCSSUnprefixingWhitelist.emplace(
|
||||
mCodebaseImmutable &&
|
||||
IsOnCSSUnprefixingWhitelistImpl(mCodebase));
|
||||
}
|
||||
|
||||
return *mIsOnCSSUnprefixingWhitelist;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
NS_IMPL_CLASSINFO(nsExpandedPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
|
||||
@@ -837,15 +642,6 @@ nsExpandedPrincipal::AddonHasPermission(const nsAString& aPerm)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsExpandedPrincipal::IsOnCSSUnprefixingWhitelist()
|
||||
{
|
||||
// CSS Unprefixing Whitelist is a per-origin thing; doesn't really make sense
|
||||
// for an expanded principal. (And probably shouldn't be needed.)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsExpandedPrincipal::GetScriptLocation(nsACString& aStr)
|
||||
{
|
||||
|
||||
@@ -25,7 +25,6 @@ public:
|
||||
NS_IMETHOD GetDomain(nsIURI** aDomain) override;
|
||||
NS_IMETHOD SetDomain(nsIURI* aDomain) override;
|
||||
NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
|
||||
virtual bool IsOnCSSUnprefixingWhitelist() override;
|
||||
bool IsCodebasePrincipal() const override { return true; }
|
||||
nsresult GetOriginInternal(nsACString& aOrigin) override;
|
||||
|
||||
@@ -55,7 +54,6 @@ public:
|
||||
bool mCodebaseImmutable;
|
||||
bool mDomainImmutable;
|
||||
bool mInitialized;
|
||||
mozilla::Maybe<bool> mIsOnCSSUnprefixingWhitelist; // Lazily-computed
|
||||
|
||||
protected:
|
||||
virtual ~nsPrincipal();
|
||||
@@ -81,7 +79,6 @@ public:
|
||||
NS_IMETHOD SetDomain(nsIURI* aDomain) override;
|
||||
NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
|
||||
virtual bool AddonHasPermission(const nsAString& aPerm) override;
|
||||
virtual bool IsOnCSSUnprefixingWhitelist() override;
|
||||
virtual nsresult GetScriptLocation(nsACString &aStr) override;
|
||||
nsresult GetOriginInternal(nsACString& aOrigin) override;
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ SECTION_INSERT_BEFORE = [
|
||||
('.text', '.fini'),
|
||||
('.rodata', '.rodata1'),
|
||||
('.data.rel.ro', '.dynamic'),
|
||||
('.kPStaticModules', '.data.rel.ro'),
|
||||
('.data', '.data1'),
|
||||
]
|
||||
|
||||
|
||||
BIN
Binary file not shown.
Vendored
+9
-2
@@ -3,8 +3,6 @@
|
||||
# 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/.
|
||||
|
||||
Library('icu')
|
||||
|
||||
DIRS += [
|
||||
'common',
|
||||
'data',
|
||||
@@ -14,5 +12,14 @@ DIRS += [
|
||||
if CONFIG['MOZ_ICU_DATA_ARCHIVE']:
|
||||
DIRS += ['stubdata']
|
||||
USE_LIBS += ['icustubdata']
|
||||
Library('icu')
|
||||
else:
|
||||
USE_LIBS += ['icudata']
|
||||
SharedLibrary('icu')
|
||||
SHARED_LIBRARY_NAME = 'icu' + CONFIG['MOZ_ICU_VERSION']
|
||||
# Linking ICU gets too spammy in warnings with MSVC
|
||||
if CONFIG['_MSC_VER'] and not CONFIG['CLANG_CL']:
|
||||
LDFLAGS += [
|
||||
'/IGNORE:4217', # symbol defined in $1.obj is defined in $2.obj
|
||||
'/IGNORE:4286', # similar with LNK4217
|
||||
]
|
||||
Vendored
+3
@@ -41,6 +41,9 @@ if CONFIG['CPU_ARCH'] == 'arm':
|
||||
if CONFIG['MOZ_FFVPX']:
|
||||
external_dirs += ['media/ffvpx']
|
||||
|
||||
if CONFIG["MOZ_JXL"]:
|
||||
external_dirs += ["media/libjxl", "media/highway"]
|
||||
|
||||
external_dirs += [
|
||||
'media/kiss_fft',
|
||||
'media/libcubeb',
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# 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/.
|
||||
|
||||
# /!\ Please make sure to update the following comment when you touch this
|
||||
# file. Thank you /!\
|
||||
|
||||
# The traditional Mozilla build system relied on going through the entire
|
||||
# build tree a number of times with different targets, and many of the
|
||||
# things happening at each step required other things happening in previous
|
||||
# steps without any documentation of those dependencies.
|
||||
#
|
||||
# This build system establishes what files or operations are needed for the
|
||||
# build, and applying the necessary rules to have those in place, relying on
|
||||
# make dependencies to get them going.
|
||||
#
|
||||
# Only non-compiled parts of the application and platform are built and
|
||||
# supported here (a few other things are also left out).
|
||||
#
|
||||
# This file contains rules and dependencies to get things working. The intent
|
||||
# is for a Makefile to define some dependencies and variables, and include
|
||||
# this file. What needs to be defined there, and ends up being generated by
|
||||
# python/mozbuild/mozbuild/backend/fastermake.py is the following:
|
||||
# - TOPSRCDIR/TOPOBJDIR, respectively the top source directory and the top
|
||||
# object directory
|
||||
# - PYTHON, the path to the python executable
|
||||
# - ACDEFINES, which contains a set of -Dvar=name to be used during
|
||||
# preprocessing
|
||||
# - INSTALL_MANIFESTS, which defines the list of base directories handled
|
||||
# by install manifests, see further below
|
||||
#
|
||||
# A convention used between this file and the Makefile including it is that
|
||||
# global Make variables names are uppercase, while "local" Make variables
|
||||
# applied to specific targets are lowercase.
|
||||
|
||||
# Targets to be triggered for a default build
|
||||
default: $(addprefix install-,$(INSTALL_MANIFESTS))
|
||||
|
||||
ifndef NO_XPIDL
|
||||
# Targets from the recursive make backend to be built for a default build
|
||||
default: $(TOPOBJDIR)/config/makefiles/xpidl/xpidl
|
||||
endif
|
||||
|
||||
# Mac builds require copying files in dist/bin/*.app
|
||||
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
||||
default:
|
||||
$(MAKE) -C $(TOPOBJDIR)/$(MOZ_BUILD_APP)/app repackage
|
||||
endif
|
||||
|
||||
.PHONY: FORCE
|
||||
|
||||
# Extra define to trigger some workarounds. We should strive to limit the
|
||||
# use of those. As of writing the only ones are in
|
||||
# toolkit/content/buildconfig.html and browser/locales/jar.mn.
|
||||
ACDEFINES += -DBUILD_FASTER
|
||||
|
||||
# Files under the faster/ sub-directory, however, are not meant to use the
|
||||
# fallback
|
||||
$(TOPOBJDIR)/faster/%: ;
|
||||
|
||||
# Generic rule to fall back to the recursive make backend.
|
||||
# This needs to stay after other $(TOPOBJDIR)/* rules because GNU Make
|
||||
# <3.82 apply pattern rules in definition order, not stem length like
|
||||
# modern GNU Make.
|
||||
$(TOPOBJDIR)/%: FORCE
|
||||
$(MAKE) -C $(dir $@) $(notdir $@)
|
||||
|
||||
# Install files using install manifests
|
||||
#
|
||||
# The list of base directories is given in INSTALL_MANIFESTS. The
|
||||
# corresponding install manifests are named correspondingly, with forward
|
||||
# slashes replaced with underscores, and prefixed with `install_`. That is,
|
||||
# the install manifest for `dist/bin` would be `install_dist_bin`.
|
||||
$(addprefix install-,$(INSTALL_MANIFESTS)): install-%: $(addprefix $(TOPOBJDIR)/,buildid.h source-repo.h)
|
||||
@# For now, force preprocessed files to be reprocessed every time.
|
||||
@# The overhead is not that big, and this avoids waiting for proper
|
||||
@# support for defines tracking in process_install_manifest.
|
||||
@touch install_$(subst /,_,$*)
|
||||
@# BOOKMARKS_INCLUDE_DIR is for bookmarks.html only.
|
||||
$(PYTHON) -m mozbuild.action.process_install_manifest \
|
||||
--track install_$(subst /,_,$*).track \
|
||||
$(TOPOBJDIR)/$* \
|
||||
-DAB_CD=en-US \
|
||||
-DBOOKMARKS_INCLUDE_DIR=$(TOPSRCDIR)/browser/locales/en-US/profile \
|
||||
$(ACDEFINES) \
|
||||
install_$(subst /,_,$*)
|
||||
|
||||
# ============================================================================
|
||||
# Below is a set of additional dependencies and variables used to build things
|
||||
# that are not supported by data in moz.build.
|
||||
|
||||
# The xpidl target in config/makefiles/xpidl requires the install manifest for
|
||||
# dist/idl to have been processed.
|
||||
$(TOPOBJDIR)/config/makefiles/xpidl/xpidl: $(TOPOBJDIR)/install-dist_idl
|
||||
|
||||
# It also requires all the install manifests for dist/bin to have been processed
|
||||
# because it adds interfaces.manifest references with buildlist.py.
|
||||
$(TOPOBJDIR)/config/makefiles/xpidl/xpidl: $(addprefix install-,$(filter dist/bin%,$(INSTALL_MANIFESTS)))
|
||||
@@ -8,4 +8,4 @@
|
||||
# Referenced by milestone.py.
|
||||
#--------------------------------------------------------
|
||||
|
||||
5.2.0
|
||||
6.1.0
|
||||
@@ -810,6 +810,7 @@ sym.h
|
||||
synch.h
|
||||
syncmgr.h
|
||||
sys/atomic_op.h
|
||||
sys/auxv.h
|
||||
sys/bitypes.h
|
||||
sys/byteorder.h
|
||||
syscall.h
|
||||
|
||||
@@ -26,16 +26,19 @@
|
||||
key="&closeCmd.key;"
|
||||
command="toolbox-cmd-close"
|
||||
modifiers="accel"/>
|
||||
<key id="toolbox-key-toggle"
|
||||
key="&toggleToolbox.key;"
|
||||
command="toolbox-cmd-close"
|
||||
modifiers="accel,shift"
|
||||
disabled="true"/>
|
||||
#ifdef XP_MACOSX
|
||||
<key id="toolbox-key-toggle-osx"
|
||||
key="&toggleToolbox.key;"
|
||||
command="toolbox-cmd-close"
|
||||
modifiers="accel,alt"
|
||||
disabled="true"/>
|
||||
#else
|
||||
<key id="toolbox-key-toggle"
|
||||
key="&toggleToolbox.key;"
|
||||
command="toolbox-cmd-close"
|
||||
modifiers="accel,shift"
|
||||
disabled="true"/>
|
||||
#endif
|
||||
<key id="toolbox-key-toggle-F12"
|
||||
keycode="&toggleToolboxF12.keycode;"
|
||||
keytext="&toggleToolboxF12.keytext;"
|
||||
|
||||
@@ -25,10 +25,8 @@
|
||||
<script>
|
||||
"use strict";
|
||||
var host = document.querySelector("#shadow");
|
||||
if (host.createShadowRoot) {
|
||||
var root = host.createShadowRoot();
|
||||
root.innerHTML = "<h3>Shadow DOM</h3><select multiple></select>";
|
||||
}
|
||||
var root = host.attachShadow({ mode: "open" });
|
||||
root.innerHTML = "<h3>Shadow DOM</h3><select multiple></select>";
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -104,7 +104,7 @@ devtools.jar:
|
||||
content/commandline/commandline.css (commandline/commandline.css)
|
||||
content/commandline/commandlineoutput.xhtml (commandline/commandlineoutput.xhtml)
|
||||
content/commandline/commandlinetooltip.xhtml (commandline/commandlinetooltip.xhtml)
|
||||
content/framework/toolbox-window.xul (framework/toolbox-window.xul)
|
||||
* content/framework/toolbox-window.xul (framework/toolbox-window.xul)
|
||||
content/framework/toolbox-options.xhtml (framework/toolbox-options.xhtml)
|
||||
content/framework/toolbox.xul (framework/toolbox.xul)
|
||||
content/framework/toolbox-init.js (framework/toolbox-init.js)
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
.tabs .all-tabs-menu {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
offset-inline-end: 0;
|
||||
inset-inline-end: 0;
|
||||
width: 15px;
|
||||
height: 100%;
|
||||
border-inline-start: 1px solid var(--theme-splitter-color);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
|
||||
* Copyright (C) 2011 Google Inc. All rights reserved.
|
||||
* Copyright (C) 2009 Mozilla Foundation. All rights reserved.
|
||||
* Copyright (C) 2022 Moonchild Productions. All rights reserved.
|
||||
* Copyright (C) 2022, 2023 Moonchild Productions. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -138,7 +138,8 @@ const Curl = {
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
let header = headers[i];
|
||||
if (header.name.toLowerCase() === "accept-encoding") {
|
||||
addParam("--compressed");
|
||||
// Ignore transfer encoding (compression) as not all commonly installed
|
||||
// versions of curl support this.
|
||||
continue;
|
||||
}
|
||||
if (ignoredHeaders.has(header.name.toLowerCase())) {
|
||||
@@ -397,41 +398,55 @@ const CurlUtils = {
|
||||
|
||||
/**
|
||||
* Escape util function for Windows systems.
|
||||
* Credit: Google DevTools
|
||||
*/
|
||||
escapeStringWin: function (str) {
|
||||
/*
|
||||
Replace the backtick character ` with `` in order to escape it.
|
||||
The backtick character is an escape character in PowerShell and
|
||||
can, among other things, be used to disable the effect of some
|
||||
of the other escapes created below.
|
||||
Because the cmd.exe parser and the MS Crt arguments parsers use some
|
||||
of the same escape characters, they can interact with each other in
|
||||
terrible ways, meaning the order of operations is critical here.
|
||||
|
||||
Replace dollar sign because of commands in powershell when using
|
||||
double quotes. e.g $(calc.exe).
|
||||
1. Replace \ with \\ first, because it is an escape character for
|
||||
certain conditions in both parsers.
|
||||
|
||||
2. Replace double quote chars with two double quotes (not by escaping
|
||||
with \") because it is recognized by both the cmd.exe and MS Crt
|
||||
arguments parsers.
|
||||
|
||||
Also see http://www.rlmueller.net/PowerShellEscape.htm for details.
|
||||
3. Escape ` and $ so commands do not get executed, e.g $(calc.exe) or
|
||||
`\$(calc.exe)
|
||||
|
||||
4. Escape all characters we are not sure about with ^, to ensure it
|
||||
gets to the MS Crt arguments parser safely.
|
||||
|
||||
Replace quote by double quote (but not by \") because it is
|
||||
recognized by both cmd.exe and MS Crt arguments parser.
|
||||
5. The % character is special because the MS Crt arguments parser will
|
||||
try and look for environment variables and fill them in, in-place. We
|
||||
cannot escape them with % and cannot escape them with ^ (because it's
|
||||
cmd.exe's escape, not the MS Crt arguments parser). So, we can get the
|
||||
cmd.exe parser to escape the character after it, if it is followed by
|
||||
a valid starting character of an environment variable.
|
||||
This ensures we do not try and double-escape another ^ if it was placed
|
||||
by the previous replace.
|
||||
|
||||
Replace % by "%" because it could be expanded to an environment
|
||||
variable value. So %% becomes "%""%". Even if an env variable ""
|
||||
(2 doublequotes) is declared, the cmd.exe will not
|
||||
substitute it with its value.
|
||||
6. We replace \r and \r\n with \n; this allows us to consistently
|
||||
escape all new lines in the next replace.
|
||||
|
||||
Replace each backslash with double backslash to make sure
|
||||
MS Crt arguments parser won't collapse them.
|
||||
|
||||
Replace new line outside of quotes since cmd.exe doesn't let
|
||||
us do it inside.
|
||||
7. Lastly, we replace new lines with ^ and TWO new lines, because the
|
||||
first new line is there to enact the escape command, and the second is
|
||||
the character to escape (in this case new line).
|
||||
The extra " enables escaping new lines with ^ within quotes in cmd.exe.
|
||||
*/
|
||||
return "\"" +
|
||||
str.replaceAll("`", "``")
|
||||
.replaceAll("$", "`$")
|
||||
.replaceAll('"', '""')
|
||||
.replaceAll("%", '"%"')
|
||||
.replace(/\\/g, "\\\\")
|
||||
.replace(/[\r\n]+/g, "\"^$&\"") + "\"";
|
||||
const encapsChars = '"';
|
||||
return (
|
||||
encapsChars +
|
||||
str
|
||||
.replace(/\\/g, "\\\\")
|
||||
.replace(/"/g, '""')
|
||||
.replace(/[`$]/g, "\\$&")
|
||||
.replace(/[^a-zA-Z0-9\s_\-:=+~\/.',?;()*\$&\\{}\"`]/g, "^$&")
|
||||
.replace(/%(?=[a-zA-Z0-9_])/g, "%^")
|
||||
.replace(/\r\n?/g, "\n")
|
||||
.replace(/\n/g, '"^\r\n\r\n"')
|
||||
+ encapsChars);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1032,7 +1032,7 @@ OutputPanel.prototype._resize = function () {
|
||||
break;
|
||||
}
|
||||
|
||||
this.document.body.style.width = "-moz-max-content";
|
||||
this.document.body.style.width = "max-content";
|
||||
let style = this._frame.contentWindow.getComputedStyle(this.document.body);
|
||||
let frameWidth = parseInt(style.width, 10);
|
||||
let width = Math.min(maxWidth, frameWidth);
|
||||
|
||||
@@ -652,7 +652,7 @@ checkbox:-moz-focusring {
|
||||
.devtools-searchinput-clear {
|
||||
position: absolute;
|
||||
top: 3.5px;
|
||||
offset-inline-end: 7px;
|
||||
inset-inline-end: 7px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
width: 16px;
|
||||
|
||||
@@ -216,7 +216,7 @@
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
offset-inline-start: 0;
|
||||
inset-inline-start: 0;
|
||||
width: 12px;
|
||||
height: 22px;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
@@ -521,8 +521,8 @@
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
offset-inline-start: 3px;
|
||||
offset-block-start: 3px;
|
||||
inset-inline-start: 3px;
|
||||
inset-block-start: 3px;
|
||||
box-shadow: 0px 0px 0px black;
|
||||
border: solid 1px #fff;
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
|
||||
// Set up a basic shadow DOM
|
||||
var host = document.querySelector('#shadow');
|
||||
if (host.createShadowRoot) {
|
||||
var root = host.createShadowRoot();
|
||||
if (host.attachShadow) {
|
||||
let root = host.attachShadow({ mode: "open" });
|
||||
root.innerHTML = '<h3>Shadow <em>DOM</em></h3><select multiple></select>';
|
||||
}
|
||||
|
||||
|
||||
@@ -2209,13 +2209,13 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"-moz-max-content",
|
||||
"-moz-min-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
"min-content",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
@@ -2232,13 +2232,13 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"-moz-max-content",
|
||||
"-moz-min-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
"min-content",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
@@ -3144,10 +3144,10 @@ exports.CSS_PROPERTIES = {
|
||||
"mix-blend-mode",
|
||||
"object-fit",
|
||||
"object-position",
|
||||
"offset-block-end",
|
||||
"offset-block-start",
|
||||
"offset-inline-end",
|
||||
"offset-inline-start",
|
||||
"inset-block-end",
|
||||
"inset-block-start",
|
||||
"inset-inline-end",
|
||||
"inset-inline-start",
|
||||
"opacity",
|
||||
"order",
|
||||
"-moz-orient",
|
||||
@@ -3304,9 +3304,7 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-mac-source-list-selection",
|
||||
"-moz-mac-vibrancy-dark",
|
||||
"-moz-mac-vibrancy-light",
|
||||
"-moz-max-content",
|
||||
"-moz-middle-with-baseline",
|
||||
"-moz-min-content",
|
||||
"-moz-none",
|
||||
"-moz-popup",
|
||||
"-moz-pre-space",
|
||||
@@ -3362,6 +3360,7 @@ exports.CSS_PROPERTIES = {
|
||||
"bottom",
|
||||
"bottom-outside",
|
||||
"break-all",
|
||||
"break-spaces",
|
||||
"break-word",
|
||||
"butt",
|
||||
"button",
|
||||
@@ -3493,6 +3492,7 @@ exports.CSS_PROPERTIES = {
|
||||
"margin-box",
|
||||
"match-source",
|
||||
"mathematical",
|
||||
"max-content",
|
||||
"medium",
|
||||
"menuarrow",
|
||||
"menubar",
|
||||
@@ -3510,6 +3510,7 @@ exports.CSS_PROPERTIES = {
|
||||
"meterbar",
|
||||
"meterchunk",
|
||||
"middle",
|
||||
"min-content",
|
||||
"miter",
|
||||
"mixed",
|
||||
"multiply",
|
||||
@@ -6000,13 +6001,13 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"-moz-max-content",
|
||||
"-moz-min-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
"min-content",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
@@ -6023,13 +6024,13 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"-moz-max-content",
|
||||
"-moz-min-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"content",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
"min-content",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
@@ -6816,12 +6817,12 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"-moz-max-content",
|
||||
"-moz-min-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
"min-content",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
@@ -6903,8 +6904,101 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"-moz-max-content",
|
||||
"-moz-min-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
"min-content",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
"inset": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
"top",
|
||||
"right",
|
||||
"bottom",
|
||||
"left"
|
||||
],
|
||||
"supports": [
|
||||
6,
|
||||
8
|
||||
],
|
||||
"values": [
|
||||
"-moz-calc",
|
||||
"auto",
|
||||
"calc",
|
||||
"inherit",
|
||||
"initial",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
"inset-block-end": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
"inset-block-end"
|
||||
],
|
||||
"supports": [
|
||||
6,
|
||||
8
|
||||
],
|
||||
"values": [
|
||||
"-moz-calc",
|
||||
"auto",
|
||||
"calc",
|
||||
"inherit",
|
||||
"initial",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
"inset-block-start": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
"inset-block-start"
|
||||
],
|
||||
"supports": [
|
||||
6,
|
||||
8
|
||||
],
|
||||
"values": [
|
||||
"-moz-calc",
|
||||
"auto",
|
||||
"calc",
|
||||
"inherit",
|
||||
"initial",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
"inset-inline-end": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
"inset-inline-end"
|
||||
],
|
||||
"supports": [
|
||||
6,
|
||||
8
|
||||
],
|
||||
"values": [
|
||||
"-moz-calc",
|
||||
"auto",
|
||||
"calc",
|
||||
"inherit",
|
||||
"initial",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
"inset-inline-start": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
"inset-inline-start"
|
||||
],
|
||||
"supports": [
|
||||
6,
|
||||
8
|
||||
],
|
||||
"values": [
|
||||
"-moz-calc",
|
||||
"auto",
|
||||
"calc",
|
||||
"inherit",
|
||||
@@ -7651,11 +7745,11 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"-moz-max-content",
|
||||
"-moz-min-content",
|
||||
"calc",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
"min-content",
|
||||
"none",
|
||||
"unset"
|
||||
]
|
||||
@@ -7673,11 +7767,11 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"-moz-max-content",
|
||||
"-moz-min-content",
|
||||
"calc",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
"min-content",
|
||||
"none",
|
||||
"unset"
|
||||
]
|
||||
@@ -7695,11 +7789,11 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"-moz-max-content",
|
||||
"-moz-min-content",
|
||||
"calc",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
"min-content",
|
||||
"none",
|
||||
"unset"
|
||||
]
|
||||
@@ -7735,12 +7829,12 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"-moz-max-content",
|
||||
"-moz-min-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
"min-content",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
@@ -7757,12 +7851,12 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"-moz-max-content",
|
||||
"-moz-min-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
"min-content",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
@@ -7779,12 +7873,12 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"-moz-max-content",
|
||||
"-moz-min-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
"min-content",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
@@ -7851,7 +7945,7 @@ exports.CSS_PROPERTIES = {
|
||||
"offset-block-end": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
"offset-block-end"
|
||||
"inset-block-end"
|
||||
],
|
||||
"supports": [
|
||||
6,
|
||||
@@ -7869,7 +7963,7 @@ exports.CSS_PROPERTIES = {
|
||||
"offset-block-start": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
"offset-block-start"
|
||||
"inset-block-start"
|
||||
],
|
||||
"supports": [
|
||||
6,
|
||||
@@ -7887,7 +7981,7 @@ exports.CSS_PROPERTIES = {
|
||||
"offset-inline-end": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
"offset-inline-end"
|
||||
"inset-inline-end"
|
||||
],
|
||||
"supports": [
|
||||
6,
|
||||
@@ -7905,7 +7999,7 @@ exports.CSS_PROPERTIES = {
|
||||
"offset-inline-start": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
"offset-inline-start"
|
||||
"inset-inline-start"
|
||||
],
|
||||
"supports": [
|
||||
6,
|
||||
@@ -9543,12 +9637,12 @@ exports.CSS_PROPERTIES = {
|
||||
"-moz-available",
|
||||
"-moz-calc",
|
||||
"-moz-fit-content",
|
||||
"-moz-max-content",
|
||||
"-moz-min-content",
|
||||
"auto",
|
||||
"calc",
|
||||
"inherit",
|
||||
"initial",
|
||||
"max-content",
|
||||
"min-content",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -190,23 +190,6 @@ LoadContext::GetOriginAttributes(JS::MutableHandleValue aAttrs)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LoadContext::IsTrackingProtectionOn(bool* aIsTrackingProtectionOn)
|
||||
{
|
||||
MOZ_ASSERT(mIsNotNull);
|
||||
|
||||
if (Preferences::GetBool("privacy.trackingprotection.enabled", false)) {
|
||||
*aIsTrackingProtectionOn = true;
|
||||
} else if ((mOriginAttributes.mPrivateBrowsingId > 0) &&
|
||||
Preferences::GetBool("privacy.trackingprotection.pbmode.enabled", false)) {
|
||||
*aIsTrackingProtectionOn = true;
|
||||
} else {
|
||||
*aIsTrackingProtectionOn = false;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// LoadContext::nsIInterfaceRequestor
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -13651,21 +13651,6 @@ nsDocShell::GetNestedFrameId(uint64_t* aId)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::IsTrackingProtectionOn(bool* aIsTrackingProtectionOn)
|
||||
{
|
||||
if (Preferences::GetBool("privacy.trackingprotection.enabled", false)) {
|
||||
*aIsTrackingProtectionOn = true;
|
||||
} else if (UsePrivateBrowsing() &&
|
||||
Preferences::GetBool("privacy.trackingprotection.pbmode.enabled", false)) {
|
||||
*aIsTrackingProtectionOn = true;
|
||||
} else {
|
||||
*aIsTrackingProtectionOn = false;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::GetIsContent(bool* aIsContent)
|
||||
{
|
||||
|
||||
@@ -233,7 +233,6 @@ public:
|
||||
NS_IMETHOD GetUseRemoteTabs(bool*) override;
|
||||
NS_IMETHOD SetRemoteTabs(bool) override;
|
||||
NS_IMETHOD GetOriginAttributes(JS::MutableHandle<JS::Value>) override;
|
||||
NS_IMETHOD IsTrackingProtectionOn(bool*) override;
|
||||
|
||||
// Restores a cached presentation from history (mLSHE).
|
||||
// This method swaps out the content viewer and simulates loads for
|
||||
|
||||
@@ -137,19 +137,4 @@ interface nsILoadContext : nsISupports
|
||||
#endif
|
||||
%}
|
||||
|
||||
/**
|
||||
* Returns true if tracking protection is enabled for the load context.
|
||||
*/
|
||||
boolean IsTrackingProtectionOn();
|
||||
|
||||
%{C++
|
||||
/**
|
||||
* De-XPCOMed getter to make call-sites cleaner.
|
||||
*/
|
||||
bool UseTrackingProtection() {
|
||||
bool usingTP;
|
||||
IsTrackingProtectionOn(&usingTP);
|
||||
return usingTP;
|
||||
}
|
||||
%}
|
||||
};
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "TimelineConsumers.h"
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "jsapi.h"
|
||||
#include "nsAppRunner.h" // for XRE_IsContentProcess, XRE_IsParentProcess
|
||||
#include "nsDocShell.h"
|
||||
|
||||
@@ -125,6 +126,9 @@ TimelineConsumers::AddConsumer(nsDocShell* aDocShell)
|
||||
UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
|
||||
MOZ_ASSERT(!observed);
|
||||
|
||||
if (mActiveConsumers == 0) {
|
||||
JS::SetProfileTimelineRecordingEnabled(true);
|
||||
}
|
||||
mActiveConsumers++;
|
||||
|
||||
ObservedDocShell* obsDocShell = new ObservedDocShell(aDocShell);
|
||||
@@ -144,6 +148,9 @@ TimelineConsumers::RemoveConsumer(nsDocShell* aDocShell)
|
||||
MOZ_ASSERT(observed);
|
||||
|
||||
mActiveConsumers--;
|
||||
if (mActiveConsumers == 0) {
|
||||
JS::SetProfileTimelineRecordingEnabled(false);
|
||||
}
|
||||
|
||||
// Clear all markers from the `mTimelineMarkers` store.
|
||||
observed.get()->ClearMarkers();
|
||||
|
||||
@@ -536,23 +536,5 @@ StyleChildrenIterator::IsNeeded(const Element* aElement)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
nsIContent*
|
||||
StyleChildrenIterator::GetNextChild()
|
||||
{
|
||||
while (nsIContent* child = AllChildrenIterator::GetNextChild()) {
|
||||
if (IsNativeAnonymousImplementationOfPseudoElement(child)) {
|
||||
// Skip any native-anonymous children that are used to implement pseudo-
|
||||
// elements. These match pseudo-element selectors instead of being
|
||||
// considered a child of their host, and thus the style system needs to
|
||||
// handle them separately.
|
||||
} else {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
+51
-16
@@ -7,6 +7,8 @@
|
||||
#define ChildIterator_h
|
||||
|
||||
#include "nsIContent.h"
|
||||
#include "nsIContentInlines.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Iterates over the children on a node. If a child is an insertion point,
|
||||
@@ -17,9 +19,6 @@
|
||||
* binding's <xbl:content> element.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
class nsIContent;
|
||||
|
||||
namespace mozilla {
|
||||
@@ -189,20 +188,33 @@ protected:
|
||||
class AllChildrenIterator : private FlattenedChildIterator
|
||||
{
|
||||
public:
|
||||
AllChildrenIterator(const nsIContent* aNode, uint32_t aFlags,
|
||||
bool aStartAtBeginning = true) :
|
||||
FlattenedChildIterator(aNode, aFlags, aStartAtBeginning),
|
||||
mAnonKidsIdx(aStartAtBeginning ? UINT32_MAX : 0),
|
||||
mFlags(aFlags), mPhase(aStartAtBeginning ? eAtBegin : eAtEnd) { }
|
||||
AllChildrenIterator(const nsIContent* aNode,
|
||||
uint32_t aFlags,
|
||||
bool aStartAtBeginning = true)
|
||||
: FlattenedChildIterator(aNode, aFlags, aStartAtBeginning)
|
||||
, mAnonKidsIdx(aStartAtBeginning ? UINT32_MAX : 0)
|
||||
, mFlags(aFlags), mPhase(aStartAtBeginning ? eAtBegin : eAtEnd)
|
||||
{
|
||||
}
|
||||
|
||||
AllChildrenIterator(AllChildrenIterator&& aOther)
|
||||
: FlattenedChildIterator(Move(aOther)),
|
||||
mAnonKids(Move(aOther.mAnonKids)), mAnonKidsIdx(aOther.mAnonKidsIdx),
|
||||
mFlags(aOther.mFlags), mPhase(aOther.mPhase)
|
||||
: FlattenedChildIterator(Move(aOther))
|
||||
, mAnonKids(Move(aOther.mAnonKids))
|
||||
, mAnonKidsIdx(aOther.mAnonKidsIdx)
|
||||
, mFlags(aOther.mFlags)
|
||||
, mPhase(aOther.mPhase)
|
||||
#ifdef DEBUG
|
||||
, mMutationGuard(aOther.mMutationGuard)
|
||||
, mMutationGuard(aOther.mMutationGuard)
|
||||
#endif
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
AllChildrenIterator& operator=(AllChildrenIterator&& aOther)
|
||||
{
|
||||
this->~AllChildrenIterator();
|
||||
new (this)AllChildrenIterator(Move(aOther));
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
~AllChildrenIterator() { MOZ_ASSERT(!mMutationGuard.Mutated(0)); }
|
||||
@@ -268,20 +280,43 @@ private:
|
||||
*/
|
||||
class StyleChildrenIterator : private AllChildrenIterator {
|
||||
public:
|
||||
explicit StyleChildrenIterator(const nsIContent* aContent)
|
||||
StyleChildrenIterator(const nsIContent* aContent,
|
||||
bool aStartAtBeginning = true)
|
||||
: AllChildrenIterator(aContent,
|
||||
nsIContent::eAllChildren |
|
||||
nsIContent::eSkipDocumentLevelNativeAnonymousContent)
|
||||
nsIContent::eSkipDocumentLevelNativeAnonymousContent,
|
||||
aStartAtBeginning)
|
||||
{
|
||||
MOZ_COUNT_CTOR(StyleChildrenIterator);
|
||||
}
|
||||
|
||||
StyleChildrenIterator(StyleChildrenIterator&& aOther)
|
||||
: AllChildrenIterator(Move(aOther))
|
||||
{
|
||||
MOZ_COUNT_CTOR(StyleChildrenIterator);
|
||||
}
|
||||
|
||||
StyleChildrenIterator& operator=(StyleChildrenIterator&& aOther)
|
||||
{
|
||||
AllChildrenIterator::operator=(Move(aOther));
|
||||
return *this;
|
||||
}
|
||||
|
||||
~StyleChildrenIterator() { MOZ_COUNT_DTOR(StyleChildrenIterator); }
|
||||
|
||||
nsIContent* GetNextChild();
|
||||
static nsIContent* GetParent(const nsIContent& aContent)
|
||||
{
|
||||
nsINode* node = aContent.GetFlattenedTreeParentNodeForStyle();
|
||||
return node && node->IsContent() ? node->AsContent() : nullptr;
|
||||
}
|
||||
|
||||
// Returns true if we cannot find all the children we need to style by
|
||||
// traversing the siblings of the first child.
|
||||
static bool IsNeeded(const Element* aParent);
|
||||
|
||||
using AllChildrenIterator::GetNextChild;
|
||||
using AllChildrenIterator::GetPreviousChild;
|
||||
using AllChildrenIterator::Seek;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsHTMLTags.h"
|
||||
#include "jsapi.h"
|
||||
#include "ShadowRoot.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@@ -415,6 +416,95 @@ CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType
|
||||
reactionsStack->EnqueueCallbackReaction(aCustomElement, Move(callback));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class CandidateFinder
|
||||
{
|
||||
public:
|
||||
CandidateFinder(nsTArray<nsWeakPtr>&& aCandidates, nsIDocument* aDoc);
|
||||
nsTArray<nsCOMPtr<Element>> OrderedCandidates();
|
||||
|
||||
private:
|
||||
bool Traverse(Element* aRoot, nsTArray<nsCOMPtr<Element>>& aOrderedElements);
|
||||
|
||||
nsCOMPtr<nsIDocument> mDoc;
|
||||
nsInterfaceHashtable<nsPtrHashKey<Element>, Element> mCandidates;
|
||||
};
|
||||
|
||||
CandidateFinder::CandidateFinder(nsTArray<nsWeakPtr>&& aCandidates,
|
||||
nsIDocument* aDoc)
|
||||
: mDoc(aDoc)
|
||||
, mCandidates(aCandidates.Length())
|
||||
{
|
||||
MOZ_ASSERT(mDoc);
|
||||
for (auto& candidate : aCandidates) {
|
||||
nsCOMPtr<Element> elem = do_QueryReferent(candidate);
|
||||
if (!elem) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Element* key = elem.get();
|
||||
mCandidates.Put(key, elem.forget());
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<nsCOMPtr<Element>>
|
||||
CandidateFinder::OrderedCandidates()
|
||||
{
|
||||
if (mCandidates.Count() == 1) {
|
||||
// Fast path for one candidate.
|
||||
for (auto iter = mCandidates.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsTArray<nsCOMPtr<Element>> rval({ Move(iter.Data()) });
|
||||
iter.Remove();
|
||||
return rval;
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<nsCOMPtr<Element>> orderedElements(mCandidates.Count());
|
||||
for (Element* child = mDoc->GetFirstElementChild(); child; child = child->GetNextElementSibling()) {
|
||||
if (!Traverse(child->AsElement(), orderedElements)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return orderedElements;
|
||||
}
|
||||
|
||||
bool
|
||||
CandidateFinder::Traverse(Element* aRoot, nsTArray<nsCOMPtr<Element>>& aOrderedElements)
|
||||
{
|
||||
nsCOMPtr<Element> elem = mCandidates.Get(aRoot);
|
||||
if (elem) {
|
||||
mCandidates.Remove(aRoot);
|
||||
aOrderedElements.AppendElement(Move(elem));
|
||||
if (mCandidates.Count() == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ShadowRoot* root = aRoot->GetShadowRoot()) {
|
||||
// First iterate the children of the shadow root if aRoot is a shadow host.
|
||||
for (Element* child = root->GetFirstElementChild(); child;
|
||||
child = child->GetNextElementSibling()) {
|
||||
if (!Traverse(child, aOrderedElements)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate the explicit children of aRoot.
|
||||
for (Element* child = aRoot->GetFirstElementChild(); child;
|
||||
child = child->GetNextElementSibling()) {
|
||||
if (!Traverse(child, aOrderedElements)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
CustomElementRegistry::UpgradeCandidates(nsIAtom* aKey,
|
||||
CustomElementDefinition* aDefinition,
|
||||
@@ -426,18 +516,13 @@ CustomElementRegistry::UpgradeCandidates(nsIAtom* aKey,
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Bug 1326028 - Upgrade custom element in shadow-including tree order
|
||||
nsAutoPtr<nsTArray<nsWeakPtr>> candidates;
|
||||
mCandidatesMap.RemoveAndForget(aKey, candidates);
|
||||
if (candidates) {
|
||||
CustomElementReactionsStack* reactionsStack =
|
||||
docGroup->CustomElementReactionsStack();
|
||||
for (size_t i = 0; i < candidates->Length(); ++i) {
|
||||
nsCOMPtr<Element> elem = do_QueryReferent(candidates->ElementAt(i));
|
||||
if (!elem) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CandidateFinder finder(Move(*candidates), mWindow->GetExtantDoc());
|
||||
for (auto& elem : finder.OrderedCandidates()) {
|
||||
reactionsStack->EnqueueUpgradeReaction(elem, aDefinition);
|
||||
}
|
||||
}
|
||||
@@ -791,6 +876,45 @@ CustomElementRegistry::Define(const nsAString& aName,
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
TryUpgrade(nsINode& aNode)
|
||||
{
|
||||
Element* element = aNode.IsElement() ? aNode.AsElement() : nullptr;
|
||||
if (element) {
|
||||
CustomElementData* ceData = element->GetCustomElementData();
|
||||
if (ceData) {
|
||||
NodeInfo* nodeInfo = element->NodeInfo();
|
||||
nsIAtom* typeAtom = ceData->GetCustomElementType();
|
||||
CustomElementDefinition* definition =
|
||||
nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(),
|
||||
nodeInfo->NameAtom(),
|
||||
nodeInfo->NamespaceID(),
|
||||
typeAtom);
|
||||
if (definition) {
|
||||
nsContentUtils::EnqueueUpgradeReaction(element, definition);
|
||||
}
|
||||
}
|
||||
|
||||
if (ShadowRoot* root = element->GetShadowRoot()) {
|
||||
for (Element* child = root->GetFirstElementChild(); child;
|
||||
child = child->GetNextElementSibling()) {
|
||||
TryUpgrade(*child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Element* child = aNode.GetFirstElementChild(); child;
|
||||
child = child->GetNextElementSibling()) {
|
||||
TryUpgrade(*child);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CustomElementRegistry::Upgrade(nsINode& aRoot)
|
||||
{
|
||||
TryUpgrade(aRoot);
|
||||
}
|
||||
|
||||
void
|
||||
CustomElementRegistry::Get(JSContext* aCx, const nsAString& aName,
|
||||
JS::MutableHandle<JS::Value> aRetVal)
|
||||
|
||||
@@ -525,6 +525,8 @@ public:
|
||||
JS::MutableHandle<JS::Value> aRetVal);
|
||||
|
||||
already_AddRefed<Promise> WhenDefined(const nsAString& aName, ErrorResult& aRv);
|
||||
|
||||
void Upgrade(nsINode& aRoot);
|
||||
};
|
||||
|
||||
class MOZ_RAII AutoCEReaction final {
|
||||
|
||||
@@ -4,11 +4,14 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DocumentOrShadowRoot.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/dom/StyleSheetList.h"
|
||||
#include "nsDocument.h"
|
||||
#include "nsFocusManager.h"
|
||||
#include "ShadowRoot.h"
|
||||
#include "XULDocument.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsSVGUtils.h"
|
||||
|
||||
class nsINode;
|
||||
class nsIDocument;
|
||||
@@ -144,5 +147,158 @@ DocumentOrShadowRoot::GetRetargetedFocusedElement()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Element*
|
||||
DocumentOrShadowRoot::GetPointerLockElement()
|
||||
{
|
||||
nsCOMPtr<Element> pointerLockedElement =
|
||||
do_QueryReferent(EventStateManager::sPointerLockedElement);
|
||||
if (!pointerLockedElement) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent* retargetedPointerLockedElement = Retarget(pointerLockedElement);
|
||||
return
|
||||
retargetedPointerLockedElement && retargetedPointerLockedElement->IsElement() ?
|
||||
retargetedPointerLockedElement->AsElement() : nullptr;
|
||||
}
|
||||
|
||||
Element*
|
||||
DocumentOrShadowRoot::GetFullscreenElement()
|
||||
{
|
||||
if (!AsNode().IsInComposedDoc()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Element* element = AsNode().OwnerDoc()->FullScreenStackTop();
|
||||
NS_ASSERTION(!element ||
|
||||
element->State().HasState(NS_EVENT_STATE_FULL_SCREEN),
|
||||
"Fullscreen element should have fullscreen styles applied");
|
||||
|
||||
nsIContent* retargeted = Retarget(element);
|
||||
if (retargeted && retargeted->IsElement()) {
|
||||
return retargeted->AsElement();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Element*
|
||||
DocumentOrShadowRoot::ElementFromPoint(float aX, float aY)
|
||||
{
|
||||
return ElementFromPointHelper(aX, aY, false, true);
|
||||
}
|
||||
|
||||
void
|
||||
DocumentOrShadowRoot::ElementsFromPoint(float aX, float aY,
|
||||
nsTArray<RefPtr<Element>>& aElements)
|
||||
{
|
||||
ElementsFromPointHelper(aX, aY, nsIDocument::FLUSH_LAYOUT, aElements);
|
||||
}
|
||||
|
||||
Element*
|
||||
DocumentOrShadowRoot::ElementFromPointHelper(float aX, float aY,
|
||||
bool aIgnoreRootScrollFrame,
|
||||
bool aFlushLayout)
|
||||
{
|
||||
AutoTArray<RefPtr<Element>, 1> elementArray;
|
||||
ElementsFromPointHelper(aX, aY,
|
||||
((aIgnoreRootScrollFrame ? nsIDocument::IGNORE_ROOT_SCROLL_FRAME : 0) |
|
||||
(aFlushLayout ? nsIDocument::FLUSH_LAYOUT : 0) |
|
||||
nsIDocument::IS_ELEMENT_FROM_POINT),
|
||||
elementArray);
|
||||
if (elementArray.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return elementArray[0];
|
||||
}
|
||||
|
||||
void
|
||||
DocumentOrShadowRoot::ElementsFromPointHelper(float aX, float aY,
|
||||
uint32_t aFlags,
|
||||
nsTArray<RefPtr<mozilla::dom::Element>>& aElements)
|
||||
{
|
||||
// As per the the spec, we return null if either coord is negative
|
||||
if (!(aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) && (aX < 0 || aY < 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
|
||||
nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
|
||||
nsPoint pt(x, y);
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = AsNode().OwnerDoc();
|
||||
|
||||
// Make sure the layout information we get is up-to-date, and
|
||||
// ensure we get a root frame (for everything but XUL)
|
||||
if (aFlags & nsIDocument::FLUSH_LAYOUT) {
|
||||
doc->FlushPendingNotifications(Flush_Layout);
|
||||
}
|
||||
|
||||
nsIPresShell* ps = doc->GetShell();
|
||||
if (!ps) {
|
||||
return;
|
||||
}
|
||||
nsIFrame* rootFrame = ps->GetRootFrame();
|
||||
|
||||
// XUL docs, unlike HTML, have no frame tree until everything's done loading
|
||||
if (!rootFrame) {
|
||||
return; // return null to premature XUL callers as a reminder to wait
|
||||
}
|
||||
|
||||
nsTArray<nsIFrame*> outFrames;
|
||||
// Emulate what GetFrameAtPoint does, since we want all the frames under our
|
||||
// point.
|
||||
nsLayoutUtils::GetFramesForArea(rootFrame, nsRect(pt, nsSize(1, 1)), outFrames,
|
||||
nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
|
||||
((aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
|
||||
|
||||
// Dunno when this would ever happen, as we should at least have a root frame under us?
|
||||
if (outFrames.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Used to filter out repeated elements in sequence.
|
||||
nsIContent* lastAdded = nullptr;
|
||||
|
||||
for (uint32_t i = 0; i < outFrames.Length(); i++) {
|
||||
nsIContent* node = doc->GetContentInThisDocument(outFrames[i]);
|
||||
|
||||
if (!node || !node->IsElement()) {
|
||||
// If this helper is called via ElementsFromPoint, we need to make sure
|
||||
// our frame is an element. Otherwise return whatever the top frame is
|
||||
// even if it isn't the top-painted element.
|
||||
// SVG 'text' element's SVGTextFrame doesn't respond to hit-testing, so
|
||||
// if 'node' is a child of such an element then we need to manually defer
|
||||
// to the parent here.
|
||||
if (!(aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) &&
|
||||
!outFrames[i]->IsSVGText()) {
|
||||
continue;
|
||||
}
|
||||
node = node->GetParent();
|
||||
if (node) {
|
||||
ShadowRoot* shadow = ShadowRoot::FromNode(node);
|
||||
if (shadow) {
|
||||
node = shadow->Host();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//XXXsmaug There is plenty of unspec'ed behavior here
|
||||
// https://github.com/w3c/webcomponents/issues/735
|
||||
// https://github.com/w3c/webcomponents/issues/736
|
||||
node = Retarget(node);
|
||||
|
||||
if (node && node != lastAdded) {
|
||||
aElements.AppendElement(node->AsElement());
|
||||
lastAdded = node;
|
||||
// If this helper is called via ElementFromPoint, just return the first
|
||||
// element we find.
|
||||
if (aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -114,6 +114,32 @@ public:
|
||||
|
||||
~DocumentOrShadowRoot() = default;
|
||||
|
||||
Element* GetPointerLockElement();
|
||||
Element* GetFullscreenElement();
|
||||
|
||||
Element* ElementFromPoint(float aX, float aY);
|
||||
void ElementsFromPoint(float aX, float aY,
|
||||
nsTArray<RefPtr<mozilla::dom::Element>>& aElements);
|
||||
|
||||
/**
|
||||
* Helper for nsIDOMDocument::elementFromPoint implementation that allows
|
||||
* ignoring the scroll frame and/or avoiding layout flushes.
|
||||
*
|
||||
* @see nsIDOMWindowUtils::elementFromPoint
|
||||
*/
|
||||
Element* ElementFromPointHelper(float aX, float aY,
|
||||
bool aIgnoreRootScrollFrame,
|
||||
bool aFlushLayout);
|
||||
enum ElementsFromPointFlags
|
||||
{
|
||||
IGNORE_ROOT_SCROLL_FRAME = 1,
|
||||
FLUSH_LAYOUT = 2,
|
||||
IS_ELEMENT_FROM_POINT = 4
|
||||
};
|
||||
|
||||
void ElementsFromPointHelper(float aX, float aY, uint32_t aFlags,
|
||||
nsTArray<RefPtr<mozilla::dom::Element>>& aElements);
|
||||
|
||||
protected:
|
||||
nsIContent* Retarget(nsIContent* aContent) const;
|
||||
|
||||
|
||||
+12
-21
@@ -1122,18 +1122,6 @@ Element::AttachShadow(const ShadowRootInit& aInit, ErrorResult& aError)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return AttachShadowInternal(aInit.mMode == ShadowRootMode::Closed, aError);
|
||||
}
|
||||
|
||||
already_AddRefed<ShadowRoot>
|
||||
Element::CreateShadowRoot(ErrorResult& aError)
|
||||
{
|
||||
return AttachShadowInternal(false, aError);
|
||||
}
|
||||
|
||||
already_AddRefed<ShadowRoot>
|
||||
Element::AttachShadowInternal(bool aClosed, ErrorResult& aError)
|
||||
{
|
||||
/**
|
||||
* 3. If context object is a shadow host, then throw
|
||||
* an "InvalidStateError" DOMException.
|
||||
@@ -1179,8 +1167,9 @@ Element::AttachShadowInternal(bool aClosed, ErrorResult& aError)
|
||||
* context object???s node document, host is context object,
|
||||
* and mode is init???s mode.
|
||||
*/
|
||||
bool isClosed = (aInit.mMode == ShadowRootMode::Closed);
|
||||
RefPtr<ShadowRoot> shadowRoot =
|
||||
new ShadowRoot(this, aClosed, nodeInfo.forget(), protoBinding);
|
||||
new ShadowRoot(this, isClosed, nodeInfo.forget(), protoBinding);
|
||||
|
||||
shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc());
|
||||
|
||||
@@ -1717,10 +1706,11 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
if (!hadParent) {
|
||||
uint32_t editableDescendantChange = EditableInclusiveDescendantCount(this);
|
||||
if (editableDescendantChange != 0) {
|
||||
// If we are binding a subtree root to the document, we need to update
|
||||
// the editable descendant count of all the ancestors.
|
||||
// If we are binding a subtree root to the document, we need to update
|
||||
// the editable descendant count of all the ancestors. However, we don't
|
||||
// cross the Shadow DOM boundary (expected behavior is unclear).
|
||||
nsIContent* parent = GetParent();
|
||||
while (parent) {
|
||||
while (parent && parent->IsElement()) {
|
||||
parent->ChangeEditableDescendantCount(editableDescendantChange);
|
||||
parent = parent->GetParent();
|
||||
}
|
||||
@@ -3428,9 +3418,9 @@ Element::Closest(const nsAString& aSelector, ErrorResult& aResult)
|
||||
matchingContext.AddScopeElement(this);
|
||||
for (nsINode* node = this; node; node = node->GetParentNode()) {
|
||||
if (node->IsElement() &&
|
||||
nsCSSRuleProcessor::SelectorListMatches(node->AsElement(),
|
||||
matchingContext,
|
||||
selectorList)) {
|
||||
nsCSSRuleProcessor::RestrictedSelectorListMatches(node->AsElement(),
|
||||
matchingContext,
|
||||
selectorList)) {
|
||||
return node->AsElement();
|
||||
}
|
||||
}
|
||||
@@ -3454,8 +3444,9 @@ Element::Matches(const nsAString& aSelector, ErrorResult& aError)
|
||||
TreeMatchContext::eNeverMatchVisited);
|
||||
matchingContext.SetHasSpecifiedScope();
|
||||
matchingContext.AddScopeElement(this);
|
||||
return nsCSSRuleProcessor::SelectorListMatches(this, matchingContext,
|
||||
selectorList);
|
||||
return nsCSSRuleProcessor::RestrictedSelectorListMatches(this,
|
||||
matchingContext,
|
||||
selectorList);
|
||||
}
|
||||
|
||||
static const nsAttrValue::EnumTable kCORSAttributeTable[] = {
|
||||
|
||||
@@ -470,9 +470,6 @@ protected:
|
||||
mState &= ~aStates;
|
||||
}
|
||||
|
||||
already_AddRefed<ShadowRoot> AttachShadowInternal(bool aClosed,
|
||||
ErrorResult& aError);
|
||||
|
||||
private:
|
||||
// Need to allow the ESM, nsGlobalWindow, and the focus manager to
|
||||
// set our state
|
||||
@@ -930,9 +927,6 @@ public:
|
||||
void SetSlot(const nsAString& aName, ErrorResult& aError);
|
||||
void GetSlot(nsAString& aName);
|
||||
|
||||
// [deprecated] Shadow DOM v0
|
||||
already_AddRefed<ShadowRoot> CreateShadowRoot(ErrorResult& aError);
|
||||
|
||||
ShadowRoot *FastGetShadowRoot() const
|
||||
{
|
||||
nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
|
||||
|
||||
+116
-15
@@ -797,6 +797,22 @@ FindChromeAccessOnlySubtreeOwner(nsIContent* aContent)
|
||||
return aContent;
|
||||
}
|
||||
|
||||
already_AddRefed<nsINode>
|
||||
FindChromeAccessOnlySubtreeOwner(EventTarget* aTarget)
|
||||
{
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(aTarget);
|
||||
if (!node || !node->ChromeOnlyAccess()) {
|
||||
return node.forget();
|
||||
}
|
||||
|
||||
if (!node->IsContent()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
node = FindChromeAccessOnlySubtreeOwner(node->AsContent());
|
||||
return node.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
||||
{
|
||||
@@ -817,24 +833,11 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
||||
// chrome access only subtree or if we are about to propagate out of
|
||||
// a shadow root to a shadow root host.
|
||||
((this == aVisitor.mEvent->mOriginalTarget &&
|
||||
!ChromeOnlyAccess()) || isAnonForEvents || GetShadowRoot())) {
|
||||
!ChromeOnlyAccess()) || isAnonForEvents)) {
|
||||
nsCOMPtr<nsIContent> relatedTarget =
|
||||
do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->mRelatedTarget);
|
||||
if (relatedTarget &&
|
||||
relatedTarget->OwnerDoc() == OwnerDoc()) {
|
||||
|
||||
// In the web components case, we may need to stop propagation of events
|
||||
// at shadow root host.
|
||||
if (GetShadowRoot()) {
|
||||
nsIContent* adjustedTarget =
|
||||
Event::GetShadowRelatedTarget(this, relatedTarget);
|
||||
if (this == adjustedTarget) {
|
||||
aVisitor.SetParentTarget(nullptr, false);
|
||||
aVisitor.mCanHandle = false;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// If current target is anonymous for events or we know that related
|
||||
// target is descendant of an element which is anonymous for events,
|
||||
// we may want to stop event propagation.
|
||||
@@ -950,6 +953,104 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
||||
} else {
|
||||
aVisitor.SetParentTarget(GetComposedDoc(), false);
|
||||
}
|
||||
|
||||
if (!ChromeOnlyAccess() && !aVisitor.mRelatedTargetRetargetedInCurrentScope) {
|
||||
// We don't support Shadow DOM in native anonymous content yet.
|
||||
aVisitor.mRelatedTargetRetargetedInCurrentScope = true;
|
||||
if (aVisitor.mEvent->mOriginalRelatedTarget) {
|
||||
// https://dom.spec.whatwg.org/#concept-event-dispatch
|
||||
// Step 3.
|
||||
// "Let relatedTarget be the result of retargeting event's relatedTarget
|
||||
// against target if event's relatedTarget is non-null, and null
|
||||
// otherwise."
|
||||
//
|
||||
// This is a bit complicated because the event might be from native
|
||||
// anonymous content, but we need to deal with non-native anonymous
|
||||
// content there.
|
||||
bool initialTarget = this == aVisitor.mEvent->mOriginalTarget;
|
||||
nsCOMPtr<nsINode> originalTargetAsNode;
|
||||
// Use of mOriginalTargetIsInAnon is an optimization here.
|
||||
if (!initialTarget && aVisitor.mOriginalTargetIsInAnon) {
|
||||
originalTargetAsNode =
|
||||
FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalTarget);
|
||||
initialTarget = originalTargetAsNode == this;
|
||||
}
|
||||
if (initialTarget) {
|
||||
nsCOMPtr<nsINode> relatedTargetAsNode =
|
||||
FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalRelatedTarget);
|
||||
if (!originalTargetAsNode) {
|
||||
originalTargetAsNode =
|
||||
do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
|
||||
}
|
||||
|
||||
if (relatedTargetAsNode && originalTargetAsNode) {
|
||||
nsINode* retargetedRelatedTarget =
|
||||
nsContentUtils::Retarget(relatedTargetAsNode, originalTargetAsNode);
|
||||
if (originalTargetAsNode == retargetedRelatedTarget &&
|
||||
retargetedRelatedTarget != relatedTargetAsNode) {
|
||||
// Step 4.
|
||||
// "If target is relatedTarget and target is not event's
|
||||
// relatedTarget, then return true."
|
||||
aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
|
||||
// Old code relies on mTarget to point to the first element which
|
||||
// was not added to the event target chain because of mCanHandle
|
||||
// being false, but in Shadow DOM case mTarget really should
|
||||
// point to a node in Shadow DOM.
|
||||
aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Part of step 5. Retargeting target has happened already higher
|
||||
// up in this method.
|
||||
// "Append to an event path with event, target, targetOverride,
|
||||
// relatedTarget, and false."
|
||||
aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
|
||||
}
|
||||
} else {
|
||||
nsCOMPtr<nsINode> relatedTargetAsNode =
|
||||
FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalRelatedTarget);
|
||||
if (relatedTargetAsNode) {
|
||||
// Step 11.3.
|
||||
// "Let relatedTarget be the result of retargeting event's
|
||||
// relatedTarget against parent if event's relatedTarget is non-null,
|
||||
// and null otherwise.".
|
||||
nsINode* retargetedRelatedTarget =
|
||||
nsContentUtils::Retarget(relatedTargetAsNode, this);
|
||||
nsCOMPtr<nsINode> targetInKnownToBeHandledScope =
|
||||
FindChromeAccessOnlySubtreeOwner(aVisitor.mTargetInKnownToBeHandledScope);
|
||||
if (nsContentUtils::ContentIsShadowIncludingDescendantOf(
|
||||
this, targetInKnownToBeHandledScope->SubtreeRoot())) {
|
||||
// Part of step 11.4.
|
||||
// "If target's root is a shadow-including inclusive ancestor of
|
||||
// parent, then"
|
||||
// "...Append to an event path with event, parent, null, relatedTarget,
|
||||
// " and slot-in-closed-tree."
|
||||
aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
|
||||
} else if (this == retargetedRelatedTarget) {
|
||||
// Step 11.5
|
||||
// "Otherwise, if parent and relatedTarget are identical, then set
|
||||
// parent to null."
|
||||
aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
|
||||
// Old code relies on mTarget to point to the first element which
|
||||
// was not added to the event target chain because of mCanHandle
|
||||
// being false, but in Shadow DOM case mTarget really should
|
||||
// point to a node in Shadow DOM.
|
||||
aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
|
||||
return NS_OK;
|
||||
} else {
|
||||
// Step 11.6
|
||||
aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (slot) {
|
||||
// Inform that we're about to exit the current scope.
|
||||
aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -1917,7 +2018,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
|
||||
}
|
||||
|
||||
nsAutoCString orphan;
|
||||
if (!tmp->IsInUncomposedDoc() &&
|
||||
if (!tmp->IsInComposedDoc() &&
|
||||
// Ignore xbl:content, which is never in the document and hence always
|
||||
// appears to be orphaned.
|
||||
!tmp->NodeInfo()->Equals(nsGkAtoms::content, kNameSpaceID_XBL)) {
|
||||
|
||||
+58
-25
@@ -9,6 +9,7 @@
|
||||
#include "mozilla/dom/DocumentFragment.h"
|
||||
#include "ChildIterator.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsDOMClassInfoID.h"
|
||||
#include "nsIDOMHTMLElement.h"
|
||||
#include "nsIStyleSheetLinkingElement.h"
|
||||
@@ -101,6 +102,23 @@ ShadowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
return mozilla::dom::ShadowRootBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther)
|
||||
{
|
||||
size_t sheetCount = aOther->SheetCount();
|
||||
for (size_t i = 0; i < sheetCount; ++i) {
|
||||
StyleSheet* sheet = aOther->SheetAt(i);
|
||||
if (sheet && sheet->IsApplicable()) {
|
||||
// TODO: Remove AsGecko() call once Stylo is removed.
|
||||
RefPtr<CSSStyleSheet> clonedSheet =
|
||||
sheet->AsGecko()->Clone(nullptr, nullptr, nullptr, nullptr);
|
||||
if (clonedSheet) {
|
||||
AppendStyleSheet(*clonedSheet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShadowRoot*
|
||||
ShadowRoot::FromNode(nsINode* aNode)
|
||||
{
|
||||
@@ -145,6 +163,19 @@ ShadowRoot::AddSlot(HTMLSlotElement* aSlot)
|
||||
|
||||
oldSlot->RemoveAssignedNode(assignedNode);
|
||||
currentSlot->AppendAssignedNode(assignedNode);
|
||||
|
||||
Element* restyleElement;
|
||||
if (assignedNode->IsElement()) {
|
||||
restyleElement = assignedNode->AsElement();
|
||||
} else {
|
||||
// This is likely a text node. Use the host instead.
|
||||
restyleElement = GetHost();
|
||||
}
|
||||
if (restyleElement) {
|
||||
nsLayoutUtils::PostRestyleEvent(
|
||||
restyleElement, eRestyle_Subtree, nsChangeHint(0));
|
||||
}
|
||||
|
||||
doEnqueueSlotChange = true;
|
||||
}
|
||||
|
||||
@@ -262,6 +293,13 @@ ShadowRoot::InsertSheet(StyleSheet* aSheet,
|
||||
}
|
||||
|
||||
nsINode* sheetOwningNode = SheetAt(i)->GetOwnerNode();
|
||||
|
||||
if (!sheetOwningNode) {
|
||||
// Keep moving; all sheets with a sheetOwner come after all
|
||||
// sheets without an owning Node
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nsContentUtils::PositionIsBefore(aLinkingContent, sheetOwningNode)) {
|
||||
InsertSheetAt(i, *aSheet);
|
||||
mProtoBinding->InsertStyleSheetAt(i, aSheet);
|
||||
@@ -311,6 +349,8 @@ ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
||||
{
|
||||
aVisitor.mCanHandle = true;
|
||||
aVisitor.mRootOfClosedTree = IsClosed();
|
||||
// Inform that we're about to exit the current scope.
|
||||
aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
|
||||
|
||||
// https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6
|
||||
if (!aVisitor.mEvent->mFlags.mComposed) {
|
||||
@@ -333,12 +373,10 @@ ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
||||
nsIContent* shadowHost = GetHost();
|
||||
aVisitor.SetParentTarget(shadowHost, false);
|
||||
|
||||
if (aVisitor.mOriginalTargetIsInAnon) {
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
|
||||
if (content && content->GetBindingParent() == shadowHost) {
|
||||
aVisitor.mEventTargetAtParent = shadowHost;
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
|
||||
if (content && content->GetBindingParent() == shadowHost) {
|
||||
aVisitor.mEventTargetAtParent = shadowHost;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -488,25 +526,6 @@ ShadowRoot::Host()
|
||||
return host->AsElement();
|
||||
}
|
||||
|
||||
bool
|
||||
ShadowRoot::ApplyAuthorStyles()
|
||||
{
|
||||
return mProtoBinding->InheritsStyle();
|
||||
}
|
||||
|
||||
void
|
||||
ShadowRoot::SetApplyAuthorStyles(bool aApplyAuthorStyles)
|
||||
{
|
||||
mProtoBinding->SetInheritsStyle(aApplyAuthorStyles);
|
||||
|
||||
nsIPresShell* shell = OwnerDoc()->GetShell();
|
||||
if (shell) {
|
||||
OwnerDoc()->BeginUpdate(UPDATE_STYLE);
|
||||
shell->RecordShadowStyleChange(this);
|
||||
OwnerDoc()->EndUpdate(UPDATE_STYLE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ShadowRoot::AttributeChanged(nsIDocument* aDocument,
|
||||
Element* aElement,
|
||||
@@ -580,6 +599,13 @@ ShadowRoot::ContentInserted(nsIDocument* aDocument,
|
||||
if (slot && slot->GetContainingShadow() == this &&
|
||||
slot->AssignedNodes().IsEmpty()) {
|
||||
slot->EnqueueSlotChangeEvent();
|
||||
return;
|
||||
}
|
||||
|
||||
// XXX: The following makes the host destroy its frames and force a
|
||||
// restyle for cases where the content isn't slotted.
|
||||
if (aContainer == this) {
|
||||
DistributionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -616,6 +642,13 @@ ShadowRoot::ContentRemoved(nsIDocument* aDocument,
|
||||
if (slot && slot->GetContainingShadow() == this &&
|
||||
slot->AssignedNodes().IsEmpty()) {
|
||||
slot->EnqueueSlotChangeEvent();
|
||||
return;
|
||||
}
|
||||
|
||||
// XXX: The following makes the host destroy its frames and force a
|
||||
// restyle for cases where the content isn't slotted.
|
||||
if (aContainer == this) {
|
||||
DistributionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,13 +59,16 @@ public:
|
||||
// [deprecated] Shadow DOM v0
|
||||
void InsertSheet(StyleSheet* aSheet, nsIContent* aLinkingContent);
|
||||
void RemoveSheet(StyleSheet* aSheet);
|
||||
bool ApplyAuthorStyles();
|
||||
void SetApplyAuthorStyles(bool aApplyAuthorStyles);
|
||||
StyleSheetList* StyleSheets()
|
||||
{
|
||||
return &DocumentOrShadowRoot::EnsureDOMStyleSheets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones internal state, for example stylesheets, of aOther to 'this'.
|
||||
*/
|
||||
void CloneInternalDataFrom(ShadowRoot* aOther);
|
||||
|
||||
/**
|
||||
* Distributes all the explicit children of the pool host to the content
|
||||
* insertion points in this ShadowRoot.
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
<body>
|
||||
<div id="host"></div>
|
||||
<script>
|
||||
var s = host.createShadowRoot();
|
||||
s.innerHTML = '<input type="range" />';
|
||||
host.attachShadow({ mode: "open" }).innerHTML = '<input type="range" />';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,7 +3,7 @@
|
||||
<body>
|
||||
<script>
|
||||
var x = document.createElement('span');
|
||||
x.createShadowRoot();
|
||||
x.attachShadow({ mode: "open" });
|
||||
x.id = 'a';
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<style>
|
||||
#foo {
|
||||
overflow: scroll;
|
||||
height: 100px;
|
||||
}
|
||||
</style>
|
||||
<div id="foo">
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
Mozilla Firefox<br>
|
||||
<script>
|
||||
foo.createShadowRoot().innerHTML = "<content></content>";
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
function fun_0() {
|
||||
document.implementation.createDocument('', '', null).adoptNode(o2);
|
||||
}
|
||||
|
||||
o1 = document.createElement('map');
|
||||
o2 = document.createElement('iframe');
|
||||
document.documentElement.appendChild(o1);
|
||||
document.documentElement.appendChild(o2);
|
||||
o1.textContent = 'x';
|
||||
document.addEventListener('DOMNodeRemoved', fun_0, false);
|
||||
o1.innerText = 'x';
|
||||
</script>
|
||||
</head>
|
||||
</html>
|
||||
@@ -209,6 +209,7 @@ load 1230422.html
|
||||
load 1251361.html
|
||||
load 1304437.html
|
||||
pref(clipboard.autocopy,true) load 1385272-1.html
|
||||
load 1393806.html
|
||||
pref(dom.webcomponents.enabled,true) load 1341693.html
|
||||
pref(dom.webcomponents.enabled,true) load 1419799.html
|
||||
pref(dom.webcomponents.enabled,false) load 1422931.html
|
||||
+4
-3
@@ -222,7 +222,7 @@ EXPORTS.mozilla.dom += [
|
||||
'WindowOrientationObserver.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'AnonymousContent.cpp',
|
||||
'Attr.cpp',
|
||||
'BarProps.cpp',
|
||||
@@ -238,7 +238,6 @@ SOURCES += [
|
||||
'DirectionalityUtils.cpp',
|
||||
'DocGroup.cpp',
|
||||
'DocumentFragment.cpp',
|
||||
'DocumentOrShadowRoot.cpp',
|
||||
'DocumentType.cpp',
|
||||
'DOMCursor.cpp',
|
||||
'DOMError.cpp',
|
||||
@@ -373,12 +372,14 @@ SOURCES += [
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WEBRTC']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'nsDOMDataChannel.cpp',
|
||||
]
|
||||
|
||||
# these files couldn't be in UNIFIED_SOURCES for now for reasons given below:
|
||||
SOURCES += [
|
||||
# DocumentOrShadowRoot conflicts with mozilla::dom::ShadowRoot
|
||||
'DocumentOrShadowRoot.cpp',
|
||||
# Several conflicts with other bindings.
|
||||
'DOMIntersectionObserver.cpp',
|
||||
# Because of OS X headers.
|
||||
|
||||
+215
-8
@@ -2248,6 +2248,34 @@ nsContentUtils::ContentIsHostIncludingDescendantOf(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsContentUtils::ContentIsShadowIncludingDescendantOf(
|
||||
const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor)
|
||||
{
|
||||
MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
|
||||
MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
|
||||
|
||||
if (aPossibleAncestor == aPossibleDescendant->GetComposedDoc()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
do {
|
||||
if (aPossibleDescendant == aPossibleAncestor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aPossibleDescendant->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
|
||||
ShadowRoot* shadowRoot =
|
||||
ShadowRoot::FromNode(const_cast<nsINode*>(aPossibleDescendant));
|
||||
aPossibleDescendant = shadowRoot ? shadowRoot->GetHost() : nullptr;
|
||||
} else {
|
||||
aPossibleDescendant = aPossibleDescendant->GetParentNode();
|
||||
}
|
||||
} while (aPossibleDescendant);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
|
||||
@@ -2266,6 +2294,30 @@ nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
nsINode*
|
||||
nsContentUtils::Retarget(nsINode* aTargetA, nsINode* aTargetB)
|
||||
{
|
||||
while (true && aTargetA) {
|
||||
// If A's root is not a shadow root...
|
||||
nsINode* root = aTargetA->SubtreeRoot();
|
||||
// TODO: replace with IsShadowRoot() check once bug 1427511 lands.
|
||||
if (!ShadowRoot::FromNode(root)) {
|
||||
// ...then return A.
|
||||
return aTargetA;
|
||||
}
|
||||
|
||||
// or A's root is a shadow-including inclusive ancestor of B...
|
||||
if (nsContentUtils::ContentIsShadowIncludingDescendantOf(aTargetB, root)) {
|
||||
// ...then return A.
|
||||
return aTargetA;
|
||||
}
|
||||
|
||||
aTargetA = ShadowRoot::FromNode(root)->GetHost();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
@@ -2377,6 +2429,56 @@ nsContentUtils::GetCommonAncestor(nsINode* aNode1,
|
||||
return parent;
|
||||
}
|
||||
|
||||
// static
|
||||
nsINode*
|
||||
nsContentUtils::GetCommonAncestorUnderInteractiveContent(nsINode* aNode1,
|
||||
nsINode* aNode2)
|
||||
{
|
||||
if (!aNode1 || !aNode2) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aNode1 == aNode2) {
|
||||
return aNode1;
|
||||
}
|
||||
|
||||
// Build the chain of parents
|
||||
AutoTArray<nsINode*, 30> parents1;
|
||||
do {
|
||||
parents1.AppendElement(aNode1);
|
||||
if (aNode1->IsElement() &&
|
||||
aNode1->AsElement()->IsInteractiveHTMLContent(true)) {
|
||||
break;
|
||||
}
|
||||
aNode1 = aNode1->GetFlattenedTreeParentNode();
|
||||
} while (aNode1);
|
||||
|
||||
AutoTArray<nsINode*, 30> parents2;
|
||||
do {
|
||||
parents2.AppendElement(aNode2);
|
||||
if (aNode2->IsElement() &&
|
||||
aNode2->AsElement()->IsInteractiveHTMLContent(true)) {
|
||||
break;
|
||||
}
|
||||
aNode2 = aNode2->GetFlattenedTreeParentNode();
|
||||
} while (aNode2);
|
||||
|
||||
// Find where the parent chain differs
|
||||
uint32_t pos1 = parents1.Length();
|
||||
uint32_t pos2 = parents2.Length();
|
||||
nsINode* parent = nullptr;
|
||||
for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
|
||||
nsINode* child1 = parents1.ElementAt(--pos1);
|
||||
nsINode* child2 = parents2.ElementAt(--pos2);
|
||||
if (child1 != child2) {
|
||||
break;
|
||||
}
|
||||
parent = child1;
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2)
|
||||
@@ -6332,9 +6434,7 @@ nsContentUtils::IsFocusedContent(const nsIContent* aContent)
|
||||
bool
|
||||
nsContentUtils::IsSubDocumentTabbable(nsIContent* aContent)
|
||||
{
|
||||
//XXXsmaug Shadow DOM spec issue!
|
||||
// We may need to change this to GetComposedDoc().
|
||||
nsIDocument* doc = aContent->GetUncomposedDoc();
|
||||
nsIDocument* doc = aContent->GetComposedDoc();
|
||||
if (!doc) {
|
||||
return false;
|
||||
}
|
||||
@@ -7056,16 +7156,26 @@ nsContentUtils::HasDistributedChildren(nsIContent* aContent)
|
||||
|
||||
// static
|
||||
bool
|
||||
nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader)
|
||||
nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader,
|
||||
const nsACString& aValue)
|
||||
{
|
||||
if (IsForbiddenSystemRequestHeader(aHeader)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((nsContentUtils::IsOverrideMethodHeader(aHeader) &&
|
||||
nsContentUtils::ContainsForbiddenMethod(aValue))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return StringBeginsWith(aHeader, NS_LITERAL_CSTRING("proxy-"),
|
||||
nsCaseInsensitiveCStringComparator()) ||
|
||||
StringBeginsWith(aHeader, NS_LITERAL_CSTRING("sec-"),
|
||||
nsCaseInsensitiveCStringComparator());
|
||||
if (StringBeginsWith(aHeader, NS_LITERAL_CSTRING("proxy-"),
|
||||
nsCaseInsensitiveCStringComparator()) ||
|
||||
StringBeginsWith(aHeader, NS_LITERAL_CSTRING("sec-"),
|
||||
nsCaseInsensitiveCStringComparator())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
@@ -7094,6 +7204,64 @@ nsContentUtils::IsForbiddenResponseHeader(const nsACString& aHeader)
|
||||
aHeader.LowerCaseEqualsASCII("set-cookie2"));
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
nsContentUtils::IsOverrideMethodHeader(const nsACString& headerName) {
|
||||
return headerName.LowerCaseEqualsASCII("x-http-method-override") ||
|
||||
headerName.LowerCaseEqualsASCII("x-http-method") ||
|
||||
headerName.LowerCaseEqualsASCII("x-method-override");
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
nsContentUtils::ContainsForbiddenMethod(const nsACString& headerValue) {
|
||||
bool hasInsecureMethod = false;
|
||||
nsCCharSeparatedTokenizer tokenizer(headerValue, ',');
|
||||
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
const nsDependentCSubstring& value = tokenizer.nextToken();
|
||||
|
||||
if (value.LowerCaseEqualsASCII("connect") ||
|
||||
value.LowerCaseEqualsASCII("trace") ||
|
||||
value.LowerCaseEqualsASCII("track")) {
|
||||
hasInsecureMethod = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hasInsecureMethod;
|
||||
}
|
||||
|
||||
// static
|
||||
bool nsContentUtils::IsCorsUnsafeRequestHeaderValue(
|
||||
const nsACString& aHeaderValue) {
|
||||
const char* cur = aHeaderValue.BeginReading();
|
||||
const char* end = aHeaderValue.EndReading();
|
||||
|
||||
while (cur != end) {
|
||||
// Implementation of
|
||||
// https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte Is less
|
||||
// than a space but not a horizontal tab
|
||||
if ((*cur < ' ' && *cur != '\t') || *cur == '"' || *cur == '(' ||
|
||||
*cur == ')' || *cur == ':' || *cur == '<' || *cur == '>' ||
|
||||
*cur == '?' || *cur == '@' || *cur == '[' || *cur == '\\' ||
|
||||
*cur == ']' || *cur == '{' || *cur == '}' ||
|
||||
*cur == 0x7F) { // 0x75 is DEL
|
||||
return true;
|
||||
}
|
||||
cur++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
bool nsContentUtils::IsAllowedNonCorsAccept(const nsACString& aHeaderValue) {
|
||||
if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
nsContentUtils::IsAllowedNonCorsContentType(const nsACString& aHeaderValue)
|
||||
@@ -7101,6 +7269,10 @@ nsContentUtils::IsAllowedNonCorsContentType(const nsACString& aHeaderValue)
|
||||
nsAutoCString contentType;
|
||||
nsAutoCString unused;
|
||||
|
||||
if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = NS_ParseRequestContentType(aHeaderValue, contentType, unused);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
@@ -7111,6 +7283,41 @@ nsContentUtils::IsAllowedNonCorsContentType(const nsACString& aHeaderValue)
|
||||
contentType.LowerCaseEqualsLiteral("multipart/form-data");
|
||||
}
|
||||
|
||||
// static
|
||||
bool nsContentUtils::IsAllowedNonCorsLanguage(const nsACString& aHeaderValue) {
|
||||
const char* cur = aHeaderValue.BeginReading();
|
||||
const char* end = aHeaderValue.EndReading();
|
||||
|
||||
while (cur != end) {
|
||||
if ((*cur >= '0' && *cur <= '9') || (*cur >= 'A' && *cur <= 'Z') ||
|
||||
(*cur >= 'a' && *cur <= 'z') || *cur == ' ' || *cur == '*' ||
|
||||
*cur == ',' || *cur == '-' || *cur == '.' || *cur == ';' ||
|
||||
*cur == '=') {
|
||||
cur++;
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool nsContentUtils::IsCORSSafelistedRequestHeader(const nsACString& aName,
|
||||
const nsACString& aValue) {
|
||||
// See https://fetch.spec.whatwg.org/#cors-safelisted-request-header
|
||||
if (aValue.Length() > 128) {
|
||||
return false;
|
||||
}
|
||||
return (aName.LowerCaseEqualsLiteral("accept") &&
|
||||
nsContentUtils::IsAllowedNonCorsAccept(aValue)) ||
|
||||
(aName.LowerCaseEqualsLiteral("accept-language") &&
|
||||
nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
|
||||
(aName.LowerCaseEqualsLiteral("content-language") &&
|
||||
nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
|
||||
(aName.LowerCaseEqualsLiteral("content-type") &&
|
||||
nsContentUtils::IsAllowedNonCorsContentType(aValue));
|
||||
}
|
||||
|
||||
bool
|
||||
nsContentUtils::DOMWindowDumpEnabled()
|
||||
{
|
||||
|
||||
@@ -279,6 +279,13 @@ public:
|
||||
static bool ContentIsHostIncludingDescendantOf(
|
||||
const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor);
|
||||
|
||||
/**
|
||||
* Similar to above, but does special case only ShadowRoot,
|
||||
* not HTMLTemplateElement.
|
||||
*/
|
||||
static bool ContentIsShadowIncludingDescendantOf(
|
||||
const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor);
|
||||
|
||||
/**
|
||||
* Similar to ContentIsDescendantOf except it crosses document boundaries,
|
||||
* this function uses ancestor/descendant relations in the composed document
|
||||
@@ -287,6 +294,13 @@ public:
|
||||
static bool ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
|
||||
nsINode* aPossibleAncestor);
|
||||
|
||||
/**
|
||||
* Retarget an object A against an object B
|
||||
* @see https://dom.spec.whatwg.org/#retarget
|
||||
*/
|
||||
static nsINode*
|
||||
Retarget(nsINode* aTargetA, nsINode* aTargetB);
|
||||
|
||||
/*
|
||||
* This method fills the |aArray| with all ancestor nodes of |aNode|
|
||||
* including |aNode| at the zero index.
|
||||
@@ -324,6 +338,15 @@ public:
|
||||
static nsINode* GetCommonAncestor(nsINode* aNode1,
|
||||
nsINode* aNode2);
|
||||
|
||||
/**
|
||||
* Returns the common ancestor under interactive content, if any.
|
||||
* If neither one has interactive content as ancestor, common ancestor will be
|
||||
* returned. If only one has interactive content as ancestor, null will be
|
||||
* returned. If the nodes are the same, that node is returned.
|
||||
*/
|
||||
static nsINode* GetCommonAncestorUnderInteractiveContent(nsINode* aNode1,
|
||||
nsINode* aNode2);
|
||||
|
||||
/**
|
||||
* Returns true if aNode1 is before aNode2 in the same connected
|
||||
* tree.
|
||||
@@ -2425,7 +2448,8 @@ public:
|
||||
* Returns whether a given header is forbidden for an XHR or fetch
|
||||
* request.
|
||||
*/
|
||||
static bool IsForbiddenRequestHeader(const nsACString& aHeader);
|
||||
static bool IsForbiddenRequestHeader(const nsACString& aHeader,
|
||||
const nsACString& aValue);
|
||||
|
||||
/**
|
||||
* Returns whether a given header is forbidden for a system XHR
|
||||
@@ -2433,18 +2457,50 @@ public:
|
||||
*/
|
||||
static bool IsForbiddenSystemRequestHeader(const nsACString& aHeader);
|
||||
|
||||
/**
|
||||
* Returns whether a given header has characters that aren't permitted
|
||||
*/
|
||||
static bool IsCorsUnsafeRequestHeaderValue(const nsACString& aHeaderValue);
|
||||
|
||||
/**
|
||||
* Returns whether a given Accept header value is allowed
|
||||
* for a non-CORS XHR or fetch request.
|
||||
*/
|
||||
static bool IsAllowedNonCorsAccept(const nsACString& aHeaderValue);
|
||||
|
||||
/**
|
||||
* Returns whether a given Content-Type header value is allowed
|
||||
* for a non-CORS XHR or fetch request.
|
||||
*/
|
||||
static bool IsAllowedNonCorsContentType(const nsACString& aHeaderValue);
|
||||
|
||||
/**
|
||||
* Returns whether a given Content-Language or accept-language header value is
|
||||
* allowed for a non-CORS XHR or fetch request.
|
||||
*/
|
||||
static bool IsAllowedNonCorsLanguage(const nsACString& aHeaderValue);
|
||||
|
||||
/**
|
||||
* Returns whether a given header and value is a CORS-safelisted request
|
||||
* header per https://fetch.spec.whatwg.org/#cors-safelisted-request-header
|
||||
*/
|
||||
static bool IsCORSSafelistedRequestHeader(const nsACString& aName,
|
||||
const nsACString& aValue);
|
||||
|
||||
/**
|
||||
* Returns whether a given header is forbidden for an XHR or fetch
|
||||
* response.
|
||||
*/
|
||||
static bool IsForbiddenResponseHeader(const nsACString& aHeader);
|
||||
|
||||
/**
|
||||
* Checks whether the header overrides any http methods
|
||||
*/
|
||||
static bool IsOverrideMethodHeader(const nsACString& headerName);
|
||||
/**
|
||||
* Checks whether the header value contains any forbidden method
|
||||
*/
|
||||
static bool ContainsForbiddenMethod(const nsACString& headerValue);
|
||||
/**
|
||||
* Returns the inner window ID for the window associated with a request,
|
||||
*/
|
||||
|
||||
@@ -2367,75 +2367,6 @@ nsDOMWindowUtils::GetCurrentAudioBackend(nsAString& aBackend)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::StartFrameTimeRecording(uint32_t *startIndex)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(startIndex);
|
||||
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
LayerManager *mgr = widget->GetLayerManager();
|
||||
if (!mgr)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
const uint32_t kRecordingMinSize = 60 * 10; // 10 seconds @60 fps.
|
||||
const uint32_t kRecordingMaxSize = 60 * 60 * 60; // One hour
|
||||
uint32_t bufferSize = Preferences::GetUint("toolkit.framesRecording.bufferSize", uint32_t(0));
|
||||
bufferSize = std::min(bufferSize, kRecordingMaxSize);
|
||||
bufferSize = std::max(bufferSize, kRecordingMinSize);
|
||||
*startIndex = mgr->StartFrameTimeRecording(bufferSize);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::StopFrameTimeRecording(uint32_t startIndex,
|
||||
uint32_t *frameCount,
|
||||
float **frameIntervals)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(frameCount);
|
||||
NS_ENSURE_ARG_POINTER(frameIntervals);
|
||||
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
LayerManager *mgr = widget->GetLayerManager();
|
||||
if (!mgr)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsTArray<float> tmpFrameIntervals;
|
||||
mgr->StopFrameTimeRecording(startIndex, tmpFrameIntervals);
|
||||
*frameCount = tmpFrameIntervals.Length();
|
||||
|
||||
*frameIntervals = (float*)moz_xmalloc(*frameCount * sizeof(float));
|
||||
|
||||
/* copy over the frame intervals and paint times into the arrays we just allocated */
|
||||
for (uint32_t i = 0; i < *frameCount; i++) {
|
||||
(*frameIntervals)[i] = tmpFrameIntervals[i];
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::BeginTabSwitch()
|
||||
{
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
LayerManager *mgr = widget->GetLayerManager();
|
||||
if (!mgr)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
mgr->BeginTabSwitch();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool
|
||||
ComputeAnimationValue(nsCSSPropertyID aProperty,
|
||||
Element* aElement,
|
||||
@@ -2478,7 +2409,7 @@ nsDOMWindowUtils::AdvanceTimeAndRefresh(int64_t aMilliseconds)
|
||||
|
||||
nsPresContext* presContext = GetPresContext();
|
||||
if (presContext) {
|
||||
nsRefreshDriver* driver = presContext->RefreshDriver();
|
||||
RefPtr<nsRefreshDriver> driver = presContext->RefreshDriver();
|
||||
driver->AdvanceTimeAndRefresh(aMilliseconds);
|
||||
|
||||
RefPtr<LayerTransactionChild> transaction = GetLayerTransaction();
|
||||
|
||||
+31
-162
@@ -3120,106 +3120,6 @@ nsDocument::ElementFromPoint(float aX, float aY, nsIDOMElement** aReturn)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Element*
|
||||
nsIDocument::ElementFromPoint(float aX, float aY)
|
||||
{
|
||||
return ElementFromPointHelper(aX, aY, false, true);
|
||||
}
|
||||
|
||||
void
|
||||
nsIDocument::ElementsFromPoint(float aX, float aY,
|
||||
nsTArray<RefPtr<Element>>& aElements)
|
||||
{
|
||||
ElementsFromPointHelper(aX, aY, nsIDocument::FLUSH_LAYOUT, aElements);
|
||||
}
|
||||
|
||||
Element*
|
||||
nsDocument::ElementFromPointHelper(float aX, float aY,
|
||||
bool aIgnoreRootScrollFrame,
|
||||
bool aFlushLayout)
|
||||
{
|
||||
AutoTArray<RefPtr<Element>, 1> elementArray;
|
||||
ElementsFromPointHelper(aX, aY,
|
||||
((aIgnoreRootScrollFrame ? nsIDocument::IGNORE_ROOT_SCROLL_FRAME : 0) |
|
||||
(aFlushLayout ? nsIDocument::FLUSH_LAYOUT : 0) |
|
||||
nsIDocument::IS_ELEMENT_FROM_POINT),
|
||||
elementArray);
|
||||
if (elementArray.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return elementArray[0];
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::ElementsFromPointHelper(float aX, float aY,
|
||||
uint32_t aFlags,
|
||||
nsTArray<RefPtr<mozilla::dom::Element>>& aElements)
|
||||
{
|
||||
// As per the the spec, we return null if either coord is negative
|
||||
if (!(aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) && (aX < 0 || aY < 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
|
||||
nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
|
||||
nsPoint pt(x, y);
|
||||
|
||||
// Make sure the layout information we get is up-to-date, and
|
||||
// ensure we get a root frame (for everything but XUL)
|
||||
if (aFlags & nsIDocument::FLUSH_LAYOUT) {
|
||||
FlushPendingNotifications(Flush_Layout);
|
||||
}
|
||||
|
||||
nsIPresShell *ps = GetShell();
|
||||
if (!ps) {
|
||||
return;
|
||||
}
|
||||
nsIFrame *rootFrame = ps->GetRootFrame();
|
||||
|
||||
// XUL docs, unlike HTML, have no frame tree until everything's done loading
|
||||
if (!rootFrame) {
|
||||
return; // return null to premature XUL callers as a reminder to wait
|
||||
}
|
||||
|
||||
nsTArray<nsIFrame*> outFrames;
|
||||
// Emulate what GetFrameAtPoint does, since we want all the frames under our
|
||||
// point.
|
||||
nsLayoutUtils::GetFramesForArea(rootFrame, nsRect(pt, nsSize(1, 1)), outFrames,
|
||||
nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
|
||||
((aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
|
||||
|
||||
// Dunno when this would ever happen, as we should at least have a root frame under us?
|
||||
if (outFrames.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Used to filter out repeated elements in sequence.
|
||||
nsIContent* lastAdded = nullptr;
|
||||
|
||||
for (uint32_t i = 0; i < outFrames.Length(); i++) {
|
||||
nsIContent* node = GetContentInThisDocument(outFrames[i]);
|
||||
|
||||
if (!node || !node->IsElement()) {
|
||||
// If this helper is called via ElementsFromPoint, we need to make sure
|
||||
// our frame is an element. Otherwise return whatever the top frame is
|
||||
// even if it isn't the top-painted element.
|
||||
if (!(aFlags & nsIDocument::IS_ELEMENT_FROM_POINT)) {
|
||||
continue;
|
||||
}
|
||||
node = node->GetParent();
|
||||
}
|
||||
if (node && node != lastAdded) {
|
||||
aElements.AppendElement(node->AsElement());
|
||||
lastAdded = node;
|
||||
// If this helper is called via ElementFromPoint, just return the first
|
||||
// element we find.
|
||||
if (aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocument::NodesFromRectHelper(float aX, float aY,
|
||||
float aTopSize, float aRightSize,
|
||||
@@ -5923,14 +5823,9 @@ nsIDocument::ImportNode(nsINode& aNode, bool aDeep, ErrorResult& rv) const
|
||||
case nsIDOMNode::COMMENT_NODE:
|
||||
case nsIDOMNode::DOCUMENT_TYPE_NODE:
|
||||
{
|
||||
nsCOMPtr<nsINode> newNode;
|
||||
nsCOMArray<nsINode> nodesWithProperties;
|
||||
rv = nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager,
|
||||
nodesWithProperties, getter_AddRefs(newNode));
|
||||
if (rv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
return newNode.forget();
|
||||
return nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager,
|
||||
&nodesWithProperties, rv);
|
||||
}
|
||||
default:
|
||||
{
|
||||
@@ -7091,8 +6986,8 @@ nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
|
||||
}
|
||||
|
||||
nsCOMArray<nsINode> nodesWithProperties;
|
||||
rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
|
||||
newScope, nodesWithProperties);
|
||||
nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
|
||||
newScope, nodesWithProperties, rv);
|
||||
if (rv.Failed()) {
|
||||
// Disconnect all nodes from their parents, since some have the old document
|
||||
// as their ownerDocument and some have this as their ownerDocument.
|
||||
@@ -8387,7 +8282,7 @@ nsDocument::DoUnblockOnload()
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsDocument::GetContentInThisDocument(nsIFrame* aFrame) const
|
||||
nsIDocument::GetContentInThisDocument(nsIFrame* aFrame) const
|
||||
{
|
||||
for (nsIFrame* f = aFrame; f;
|
||||
f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
|
||||
@@ -8598,7 +8493,7 @@ nsDocument::OnPageHide(bool aPersisted,
|
||||
EnumerateActivityObservers(NotifyActivityChanged, nullptr);
|
||||
|
||||
ClearPendingFullscreenRequests(this);
|
||||
if (GetFullscreenElement()) {
|
||||
if (FullScreenStackTop()) {
|
||||
// If this document was fullscreen, we should exit fullscreen in this
|
||||
// doctree branch. This ensures that if the user navigates while in
|
||||
// fullscreen mode we don't leave its still visible ancestor documents
|
||||
@@ -10302,7 +10197,7 @@ nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc)
|
||||
static bool
|
||||
CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
|
||||
{
|
||||
if (aDoc->GetFullscreenElement()) {
|
||||
if (aDoc->FullScreenStackTop()) {
|
||||
uint32_t* count = static_cast<uint32_t*>(aData);
|
||||
(*count)++;
|
||||
}
|
||||
@@ -10322,7 +10217,7 @@ nsDocument::IsFullscreenLeaf()
|
||||
{
|
||||
// A fullscreen leaf document is fullscreen, and has no fullscreen
|
||||
// subdocuments.
|
||||
if (!GetFullscreenElement()) {
|
||||
if (!FullScreenStackTop()) {
|
||||
return false;
|
||||
}
|
||||
return CountFullscreenSubDocuments(this) == 0;
|
||||
@@ -10331,11 +10226,11 @@ nsDocument::IsFullscreenLeaf()
|
||||
static bool
|
||||
ResetFullScreen(nsIDocument* aDocument, void* aData)
|
||||
{
|
||||
if (aDocument->GetFullscreenElement()) {
|
||||
if (aDocument->FullScreenStackTop()) {
|
||||
NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
|
||||
"Should have at most 1 fullscreen subdocument.");
|
||||
static_cast<nsDocument*>(aDocument)->CleanupFullscreenState();
|
||||
NS_ASSERTION(!aDocument->GetFullscreenElement(),
|
||||
NS_ASSERTION(!aDocument->FullScreenStackTop(),
|
||||
"Should reset full-screen");
|
||||
auto changed = reinterpret_cast<nsCOMArray<nsIDocument>*>(aData);
|
||||
changed->AppendElement(aDocument);
|
||||
@@ -10383,7 +10278,7 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
|
||||
UnlockPointer();
|
||||
|
||||
nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
|
||||
if (!root || !root->GetFullscreenElement()) {
|
||||
if (!root || !root->FullScreenStackTop()) {
|
||||
// If a document was detached before exiting from fullscreen, it is
|
||||
// possible that the root had left fullscreen state. In this case,
|
||||
// we would not get anything from the ResetFullScreen() call. Root's
|
||||
@@ -10412,7 +10307,7 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
|
||||
DispatchFullScreenChange(changed[changed.Length() - i - 1]);
|
||||
}
|
||||
|
||||
NS_ASSERTION(!root->GetFullscreenElement(),
|
||||
NS_ASSERTION(!root->FullScreenStackTop(),
|
||||
"Fullscreen root should no longer be a fullscreen doc...");
|
||||
|
||||
// Move the top-level window out of fullscreen mode.
|
||||
@@ -10429,7 +10324,7 @@ GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
|
||||
nsIDocument** result = static_cast<nsIDocument**>(aData);
|
||||
*result = aDoc;
|
||||
return false;
|
||||
} else if (aDoc->GetFullscreenElement()) {
|
||||
} else if (aDoc->FullScreenStackTop()) {
|
||||
aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData);
|
||||
}
|
||||
return true;
|
||||
@@ -10448,7 +10343,7 @@ GetFullscreenLeaf(nsIDocument* aDoc)
|
||||
nsIDocument* root = nsContentUtils::GetRootDocument(aDoc);
|
||||
// Check that the root is actually fullscreen so we don't waste time walking
|
||||
// around its descendants.
|
||||
if (!root->GetFullscreenElement()) {
|
||||
if (!root->FullScreenStackTop()) {
|
||||
return nullptr;
|
||||
}
|
||||
GetFullscreenLeaf(root, &leaf);
|
||||
@@ -10458,10 +10353,10 @@ GetFullscreenLeaf(nsIDocument* aDoc)
|
||||
void
|
||||
nsDocument::RestorePreviousFullScreenState()
|
||||
{
|
||||
NS_ASSERTION(!GetFullscreenElement() || !FullscreenRoots::IsEmpty(),
|
||||
NS_ASSERTION(!FullScreenStackTop() || !FullscreenRoots::IsEmpty(),
|
||||
"Should have at least 1 fullscreen root when fullscreen!");
|
||||
|
||||
if (!GetFullscreenElement() || !GetWindow() || FullscreenRoots::IsEmpty()) {
|
||||
if (!FullScreenStackTop() || !GetWindow() || FullscreenRoots::IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10636,7 +10531,7 @@ nsDocument::FullScreenStackPush(Element* aElement)
|
||||
}
|
||||
EventStateManager::SetFullScreenState(aElement, true);
|
||||
mFullScreenStack.AppendElement(do_GetWeakReference(aElement));
|
||||
NS_ASSERTION(GetFullscreenElement() == aElement, "Should match");
|
||||
NS_ASSERTION(FullScreenStackTop() == aElement, "Should match");
|
||||
UpdateViewportScrollbarOverrideForFullscreen(this);
|
||||
return true;
|
||||
}
|
||||
@@ -10684,7 +10579,7 @@ nsDocument::FullScreenStackTop()
|
||||
uint32_t last = mFullScreenStack.Length() - 1;
|
||||
nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last]));
|
||||
NS_ASSERTION(element, "Should have full-screen element!");
|
||||
NS_ASSERTION(element->IsInUncomposedDoc(), "Full-screen element should be in doc");
|
||||
NS_ASSERTION(element->IsInComposedDoc(), "Full-screen element should be in doc");
|
||||
NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc");
|
||||
return element;
|
||||
}
|
||||
@@ -10762,7 +10657,7 @@ nsresult nsDocument::RemoteFrameFullscreenReverted()
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsDocument::IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject)
|
||||
nsIDocument::IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return nsContentUtils::IsCallerChrome() ||
|
||||
@@ -10810,10 +10705,10 @@ nsDocument::FullscreenElementReadyCheck(Element* aElement,
|
||||
{
|
||||
NS_ASSERTION(aElement,
|
||||
"Must pass non-null element to nsDocument::RequestFullScreen");
|
||||
if (!aElement || aElement == GetFullscreenElement()) {
|
||||
if (!aElement || aElement == FullScreenStackTop()) {
|
||||
return false;
|
||||
}
|
||||
if (!aElement->IsInUncomposedDoc()) {
|
||||
if (!aElement->IsInComposedDoc()) {
|
||||
DispatchFullscreenError("FullscreenDeniedNotInDocument");
|
||||
return false;
|
||||
}
|
||||
@@ -10837,8 +10732,11 @@ nsDocument::FullscreenElementReadyCheck(Element* aElement,
|
||||
DispatchFullscreenError("FullscreenDeniedSubDocFullScreen");
|
||||
return false;
|
||||
}
|
||||
if (GetFullscreenElement() &&
|
||||
!nsContentUtils::ContentIsDescendantOf(aElement, GetFullscreenElement())) {
|
||||
//XXXsmaug Note, we don't follow the latest fullscreen spec here.
|
||||
// This whole check could be probably removed.
|
||||
if (FullScreenStackTop() &&
|
||||
!nsContentUtils::ContentIsHostIncludingDescendantOf(aElement,
|
||||
FullScreenStackTop())) {
|
||||
// If this document is full-screen, only grant full-screen requests from
|
||||
// a descendant of the current full-screen element.
|
||||
DispatchFullscreenError("FullscreenDeniedNotDescendant");
|
||||
@@ -11198,16 +11096,6 @@ nsDocument::GetMozFullScreenElement(nsIDOMElement **aFullScreenElement)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Element*
|
||||
nsDocument::GetFullscreenElement()
|
||||
{
|
||||
Element* element = FullScreenStackTop();
|
||||
NS_ASSERTION(!element ||
|
||||
element->State().HasState(NS_EVENT_STATE_FULL_SCREEN),
|
||||
"Fullscreen element should have fullscreen styles applied");
|
||||
return element;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocument::GetMozFullScreen(bool *aFullScreen)
|
||||
{
|
||||
@@ -11326,7 +11214,7 @@ GetPointerLockError(Element* aElement, Element* aCurrentLock,
|
||||
return "PointerLockDeniedInUse";
|
||||
}
|
||||
|
||||
if (!aElement->IsInUncomposedDoc()) {
|
||||
if (!aElement->IsInComposedDoc()) {
|
||||
return "PointerLockDeniedNotInDocument";
|
||||
}
|
||||
|
||||
@@ -11377,11 +11265,11 @@ ChangePointerLockedElement(Element* aElement, nsIDocument* aDocument,
|
||||
MOZ_ASSERT(aDocument);
|
||||
MOZ_ASSERT(aElement != aPointerLockedElement);
|
||||
if (aPointerLockedElement) {
|
||||
MOZ_ASSERT(aPointerLockedElement->GetUncomposedDoc() == aDocument);
|
||||
MOZ_ASSERT(aPointerLockedElement->GetComposedDoc() == aDocument);
|
||||
aPointerLockedElement->ClearPointerLock();
|
||||
}
|
||||
if (aElement) {
|
||||
MOZ_ASSERT(aElement->GetUncomposedDoc() == aDocument);
|
||||
MOZ_ASSERT(aElement->GetComposedDoc() == aDocument);
|
||||
aElement->SetPointerLock();
|
||||
EventStateManager::sPointerLockedElement = do_GetWeakReference(aElement);
|
||||
EventStateManager::sPointerLockedDoc = do_GetWeakReference(aDocument);
|
||||
@@ -11405,9 +11293,9 @@ PointerLockRequest::Run()
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
|
||||
nsDocument* d = static_cast<nsDocument*>(doc.get());
|
||||
const char* error = nullptr;
|
||||
if (!e || !d || !e->GetUncomposedDoc()) {
|
||||
if (!e || !d || !e->GetComposedDoc()) {
|
||||
error = "PointerLockDeniedNotInDocument";
|
||||
} else if (e->GetUncomposedDoc() != d) {
|
||||
} else if (e->GetComposedDoc() != d) {
|
||||
error = "PointerLockDeniedMovedDocument";
|
||||
}
|
||||
if (!error) {
|
||||
@@ -11573,25 +11461,6 @@ nsDocument::GetMozPointerLockElement(nsIDOMElement** aPointerLockedElement)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Element*
|
||||
nsIDocument::GetPointerLockElement()
|
||||
{
|
||||
nsCOMPtr<Element> pointerLockedElement =
|
||||
do_QueryReferent(EventStateManager::sPointerLockedElement);
|
||||
if (!pointerLockedElement) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Make sure pointer locked element is in the same document.
|
||||
nsCOMPtr<nsIDocument> pointerLockedDoc =
|
||||
do_QueryReferent(EventStateManager::sPointerLockedDoc);
|
||||
if (pointerLockedDoc != this) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return pointerLockedElement;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocument::Observe(nsISupports *aSubject,
|
||||
const char *aTopic,
|
||||
|
||||
+1
-22
@@ -692,14 +692,6 @@ public:
|
||||
nsIAtom* aAttrName,
|
||||
const nsAString& aAttrValue) const override;
|
||||
|
||||
virtual Element* ElementFromPointHelper(float aX, float aY,
|
||||
bool aIgnoreRootScrollFrame,
|
||||
bool aFlushLayout) override;
|
||||
|
||||
virtual void ElementsFromPointHelper(float aX, float aY,
|
||||
uint32_t aFlags,
|
||||
nsTArray<RefPtr<mozilla::dom::Element>>& aElements) override;
|
||||
|
||||
virtual nsresult NodesFromRectHelper(float aX, float aY,
|
||||
float aTopSize, float aRightSize,
|
||||
float aBottomSize, float aLeftSize,
|
||||
@@ -896,8 +888,6 @@ public:
|
||||
//
|
||||
already_AddRefed<nsSimpleContentList> BlockedTrackingNodes() const;
|
||||
|
||||
static bool IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject);
|
||||
|
||||
// Do the "fullscreen element ready check" from the fullscreen spec.
|
||||
// It returns true if the given element is allowed to go into fullscreen.
|
||||
bool FullscreenElementReadyCheck(Element* aElement, bool aWasCallerChrome);
|
||||
@@ -921,11 +911,10 @@ public:
|
||||
void FullScreenStackPop();
|
||||
|
||||
// Returns the top element from the full-screen stack.
|
||||
Element* FullScreenStackTop();
|
||||
Element* FullScreenStackTop() override;
|
||||
|
||||
// DOM-exposed fullscreen API
|
||||
bool FullscreenEnabled() override;
|
||||
Element* GetFullscreenElement() override;
|
||||
|
||||
void RequestPointerLock(Element* aElement) override;
|
||||
bool SetPointerLock(Element* aElement, int aCursorStyle);
|
||||
@@ -1315,16 +1304,6 @@ private:
|
||||
|
||||
nsresult InitCSP(nsIChannel* aChannel);
|
||||
|
||||
/**
|
||||
* Find the (non-anonymous) content in this document for aFrame. It will
|
||||
* be aFrame's content node if that content is in this document and not
|
||||
* anonymous. Otherwise, when aFrame is in a subdocument, we use the frame
|
||||
* element containing the subdocument containing aFrame, and/or find the
|
||||
* nearest non-anonymous ancestor in this document.
|
||||
* Returns null if there is no such element.
|
||||
*/
|
||||
nsIContent* GetContentInThisDocument(nsIFrame* aFrame) const;
|
||||
|
||||
// Just like EnableStyleSheetsForSet, but doesn't check whether
|
||||
// aSheetSet is null and allows the caller to control whether to set
|
||||
// aSheetSet as the preferred set in the CSSLoader.
|
||||
|
||||
+639
-94
@@ -8,10 +8,12 @@
|
||||
#include "nsFocusManager.h"
|
||||
|
||||
#include "AccessibleCaretEventHub.h"
|
||||
#include "ChildIterator.h"
|
||||
#include "nsAttrValueInlines.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsDocument.h"
|
||||
#include "nsIEditor.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIDOMChromeWindow.h"
|
||||
@@ -46,7 +48,9 @@
|
||||
|
||||
#include "mozilla/ContentEvents.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
#include "mozilla/dom/HTMLInputElement.h"
|
||||
#include "mozilla/dom/HTMLSlotElement.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/EventStates.h"
|
||||
@@ -2972,6 +2976,378 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindowOuter* aWindow,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsHostOrSlot(nsIContent* aContent)
|
||||
{
|
||||
return aContent && (aContent->GetShadowRoot() ||
|
||||
aContent->IsHTMLElement(nsGkAtoms::slot));
|
||||
}
|
||||
|
||||
// Helper class to iterate contents in scope by traversing flattened tree
|
||||
// in tree order
|
||||
class MOZ_STACK_CLASS ScopedContentTraversal
|
||||
{
|
||||
public:
|
||||
ScopedContentTraversal(nsIContent* aStartContent, nsIContent* aOwner)
|
||||
: mCurrent(aStartContent)
|
||||
, mOwner(aOwner)
|
||||
{
|
||||
MOZ_ASSERT(aStartContent);
|
||||
}
|
||||
|
||||
void Next();
|
||||
void Prev();
|
||||
|
||||
void Reset()
|
||||
{
|
||||
SetCurrent(mOwner);
|
||||
}
|
||||
|
||||
nsIContent* GetCurrent()
|
||||
{
|
||||
return mCurrent;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetCurrent(nsIContent* aContent)
|
||||
{
|
||||
mCurrent = aContent;
|
||||
}
|
||||
|
||||
nsIContent* mCurrent;
|
||||
nsIContent* mOwner;
|
||||
};
|
||||
|
||||
void
|
||||
ScopedContentTraversal::Next()
|
||||
{
|
||||
MOZ_ASSERT(mCurrent);
|
||||
|
||||
// Get mCurrent's first child if it's in the same scope.
|
||||
if (!IsHostOrSlot(mCurrent) || mCurrent == mOwner) {
|
||||
StyleChildrenIterator iter(mCurrent);
|
||||
nsIContent* child = iter.GetNextChild();
|
||||
if (child) {
|
||||
SetCurrent(child);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If mOwner has no children, END traversal
|
||||
if (mCurrent == mOwner) {
|
||||
SetCurrent(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
nsIContent* current = mCurrent;
|
||||
while (1) {
|
||||
// Create parent's iterator and move to current
|
||||
nsIContent* parent = current->GetFlattenedTreeParent();
|
||||
StyleChildrenIterator parentIter(parent);
|
||||
parentIter.Seek(current);
|
||||
|
||||
// Get next sibling of current
|
||||
if (nsIContent* next = parentIter.GetNextChild()) {
|
||||
SetCurrent(next);
|
||||
return;
|
||||
}
|
||||
|
||||
// If no next sibling and parent is mOwner, END traversal
|
||||
if (parent == mOwner) {
|
||||
SetCurrent(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
current = parent;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ScopedContentTraversal::Prev()
|
||||
{
|
||||
MOZ_ASSERT(mCurrent);
|
||||
|
||||
nsIContent* parent;
|
||||
nsIContent* last;
|
||||
if (mCurrent == mOwner) {
|
||||
// Get last child of mOwner
|
||||
StyleChildrenIterator ownerIter(mOwner, false /* aStartAtBeginning */);
|
||||
last = ownerIter.GetPreviousChild();
|
||||
|
||||
parent = last;
|
||||
} else {
|
||||
// Create parent's iterator and move to mCurrent
|
||||
parent = mCurrent->GetFlattenedTreeParent();
|
||||
StyleChildrenIterator parentIter(parent);
|
||||
parentIter.Seek(mCurrent);
|
||||
|
||||
// Get previous sibling
|
||||
last = parentIter.GetPreviousChild();
|
||||
}
|
||||
|
||||
while (last) {
|
||||
parent = last;
|
||||
if (parent->GetShadowRoot() ||
|
||||
parent->IsHTMLElement(nsGkAtoms::slot)) {
|
||||
// Skip contents in other scopes
|
||||
break;
|
||||
}
|
||||
|
||||
// Find last child
|
||||
StyleChildrenIterator iter(parent, false /* aStartAtBeginning */);
|
||||
last = iter.GetPreviousChild();
|
||||
}
|
||||
|
||||
// If parent is mOwner and no previous sibling remains, END traversal
|
||||
SetCurrent(parent == mOwner ? nullptr : parent);
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::FindOwner(nsIContent* aContent)
|
||||
{
|
||||
nsIContent* currentContent = aContent;
|
||||
while (currentContent) {
|
||||
nsIContent* parent = currentContent->GetFlattenedTreeParent();
|
||||
|
||||
// Shadow host / Slot
|
||||
if (IsHostOrSlot(parent)) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
currentContent = parent;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Host and Slot elements need to be handled as if they had tabindex 0 even
|
||||
* when they don't have the attribute. This is a helper method to get the
|
||||
* right value for focus navigation. If aIsFocusable is passed, it is set to
|
||||
* true if the element itself is focusable.
|
||||
*/
|
||||
static int32_t
|
||||
HostOrSlotTabIndexValue(nsIContent* aContent,
|
||||
bool* aIsFocusable = nullptr)
|
||||
{
|
||||
MOZ_ASSERT(IsHostOrSlot(aContent));
|
||||
|
||||
if (aIsFocusable) {
|
||||
*aIsFocusable = false;
|
||||
nsIFrame* frame = aContent->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
int32_t tabIndex;
|
||||
frame->IsFocusable(&tabIndex, 0);
|
||||
*aIsFocusable = tabIndex >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
const nsAttrValue* attrVal =
|
||||
aContent->AsElement()->GetParsedAttr(nsGkAtoms::tabindex);
|
||||
if (!attrVal) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (attrVal->Type() == nsAttrValue::eInteger) {
|
||||
return attrVal->GetIntegerValue();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::GetNextTabbableContentInScope(nsIContent* aOwner,
|
||||
nsIContent* aStartContent,
|
||||
nsIContent* aOriginalStartContent,
|
||||
bool aForward,
|
||||
int32_t aCurrentTabIndex,
|
||||
bool aIgnoreTabIndex,
|
||||
bool aForDocumentNavigation,
|
||||
bool aSkipOwner)
|
||||
{
|
||||
MOZ_ASSERT(IsHostOrSlot(aOwner), "Scope owner should be host or slot");
|
||||
|
||||
if (!aSkipOwner && (aForward && aOwner == aStartContent)) {
|
||||
int32_t tabIndex = -1;
|
||||
nsIFrame* frame = aOwner->GetPrimaryFrame();
|
||||
if (frame && frame->IsFocusable(&tabIndex, false) && tabIndex >= 0) {
|
||||
return aOwner;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Iterate contents in scope
|
||||
//
|
||||
ScopedContentTraversal contentTraversal(aStartContent, aOwner);
|
||||
nsCOMPtr<nsIContent> iterContent;
|
||||
nsIContent* firstNonChromeOnly = aStartContent->IsInNativeAnonymousSubtree() ?
|
||||
aStartContent->FindFirstNonChromeOnlyAccessContent() : nullptr;
|
||||
while (1) {
|
||||
// Iterate tab index to find corresponding contents in scope
|
||||
|
||||
while (1) {
|
||||
// Iterate remaining contents in scope to find next content to focus
|
||||
|
||||
// Get next content
|
||||
aForward ? contentTraversal.Next() : contentTraversal.Prev();
|
||||
iterContent = contentTraversal.GetCurrent();
|
||||
|
||||
if (firstNonChromeOnly && firstNonChromeOnly == iterContent) {
|
||||
// We just broke out from the native anonymous content, so move
|
||||
// to the previous/next node of the native anonymous owner.
|
||||
if (aForward) {
|
||||
contentTraversal.Next();
|
||||
} else {
|
||||
contentTraversal.Prev();
|
||||
}
|
||||
iterContent = contentTraversal.GetCurrent();
|
||||
}
|
||||
|
||||
if (!iterContent) {
|
||||
// Reach the end
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the tab index of the next element. For NAC we rely on frames.
|
||||
//XXXsmaug we should probably use frames also for Shadow DOM and special
|
||||
// case only display:contents elements.
|
||||
int32_t tabIndex = 0;
|
||||
if (iterContent->IsInNativeAnonymousSubtree() &&
|
||||
iterContent->GetPrimaryFrame()) {
|
||||
iterContent->GetPrimaryFrame()->IsFocusable(&tabIndex);
|
||||
} else if (IsHostOrSlot(iterContent)) {
|
||||
tabIndex = HostOrSlotTabIndexValue(iterContent);
|
||||
} else {
|
||||
iterContent->IsFocusable(&tabIndex);
|
||||
}
|
||||
if (tabIndex < 0 || !(aIgnoreTabIndex || tabIndex == aCurrentTabIndex)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsHostOrSlot(iterContent)) {
|
||||
nsCOMPtr<nsIContent> elementInFrame;
|
||||
bool checkSubDocument = true;
|
||||
if (aForDocumentNavigation &&
|
||||
TryDocumentNavigation(iterContent, &checkSubDocument,
|
||||
getter_AddRefs(elementInFrame))) {
|
||||
return elementInFrame;
|
||||
}
|
||||
if (!checkSubDocument) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TryToMoveFocusToSubDocument(iterContent, aOriginalStartContent,
|
||||
aForward, aForDocumentNavigation,
|
||||
getter_AddRefs(elementInFrame))) {
|
||||
return elementInFrame;
|
||||
}
|
||||
|
||||
// Found content to focus
|
||||
return iterContent;
|
||||
}
|
||||
|
||||
// Search in scope owned by iterContent
|
||||
nsIContent* contentToFocus =
|
||||
GetNextTabbableContentInScope(iterContent, iterContent,
|
||||
aOriginalStartContent, aForward,
|
||||
aForward ? 1 : 0, aIgnoreTabIndex,
|
||||
aForDocumentNavigation,
|
||||
false /* aSkipOwner */);
|
||||
if (contentToFocus) {
|
||||
return contentToFocus;
|
||||
}
|
||||
};
|
||||
|
||||
// If already at lowest priority tab (0), end search completely.
|
||||
// A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
|
||||
if (aCurrentTabIndex == (aForward ? 0 : 1)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Continue looking for next highest priority tabindex
|
||||
aCurrentTabIndex = GetNextTabIndex(aOwner, aCurrentTabIndex, aForward);
|
||||
contentTraversal.Reset();
|
||||
}
|
||||
|
||||
// Return scope owner at last for backward navigation if its tabindex
|
||||
// is non-negative
|
||||
if (!aSkipOwner && !aForward) {
|
||||
int32_t tabIndex = -1;
|
||||
nsIFrame* frame = aOwner->GetPrimaryFrame();
|
||||
if (frame && frame->IsFocusable(&tabIndex, false) && tabIndex >= 0) {
|
||||
return aOwner;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::GetNextTabbableContentInAncestorScopes(
|
||||
nsIContent* aStartOwner,
|
||||
nsIContent** aStartContent,
|
||||
nsIContent* aOriginalStartContent,
|
||||
bool aForward,
|
||||
int32_t* aCurrentTabIndex,
|
||||
bool aIgnoreTabIndex,
|
||||
bool aForDocumentNavigation)
|
||||
{
|
||||
MOZ_ASSERT(aStartOwner == FindOwner(*aStartContent),
|
||||
"aStartOwner should be the scope owner of aStartContent");
|
||||
MOZ_ASSERT(IsHostOrSlot(aStartOwner), "scope owner should be host or slot");
|
||||
|
||||
nsIContent* owner = aStartOwner;
|
||||
nsIContent* startContent = *aStartContent;
|
||||
|
||||
while (IsHostOrSlot(owner)) {
|
||||
int32_t tabIndex = 0;
|
||||
if (IsHostOrSlot(startContent)) {
|
||||
tabIndex = HostOrSlotTabIndexValue(startContent);
|
||||
} else {
|
||||
startContent->IsFocusable(&tabIndex);
|
||||
}
|
||||
nsIContent* contentToFocus =
|
||||
GetNextTabbableContentInScope(owner, startContent, aOriginalStartContent,
|
||||
aForward, tabIndex, aIgnoreTabIndex,
|
||||
aForDocumentNavigation,
|
||||
false /* aSkipOwner */);
|
||||
if (contentToFocus) {
|
||||
return contentToFocus;
|
||||
}
|
||||
|
||||
startContent = owner;
|
||||
owner = FindOwner(startContent);
|
||||
}
|
||||
|
||||
// If not found in shadow DOM, search from the top level shadow host in light DOM
|
||||
*aStartContent = startContent;
|
||||
*aCurrentTabIndex = HostOrSlotTabIndexValue(startContent);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static nsIContent*
|
||||
GetTopLevelScopeOwner(nsIContent* aContent)
|
||||
{
|
||||
nsIContent* topLevelScopeOwner = nullptr;
|
||||
while (aContent) {
|
||||
if (HTMLSlotElement* slot = aContent->GetAssignedSlot()) {
|
||||
aContent = slot;
|
||||
} else if (ShadowRoot* shadowRoot = aContent->GetContainingShadow()) {
|
||||
aContent = shadowRoot->Host();
|
||||
topLevelScopeOwner = aContent;
|
||||
} else {
|
||||
// TODO: replaced with FromNode.
|
||||
if (HTMLSlotElement::FromContentOrNull(aContent)) {
|
||||
topLevelScopeOwner = aContent;
|
||||
}
|
||||
aContent = aContent->GetParent();
|
||||
}
|
||||
}
|
||||
|
||||
return topLevelScopeOwner;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
nsIContent* aRootContent,
|
||||
@@ -2989,70 +3365,163 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
if (!startContent)
|
||||
return NS_OK;
|
||||
|
||||
nsIContent* currentTopLevelScopeOwner = GetTopLevelScopeOwner(aStartContent);
|
||||
|
||||
LOGCONTENTNAVIGATION("GetNextTabbable: %s", aStartContent);
|
||||
LOGFOCUSNAVIGATION((" tabindex: %d", aCurrentTabIndex));
|
||||
|
||||
if (nsDocument::IsWebComponentsEnabled(aRootContent)) {
|
||||
// If aStartContent is a shadow host or slot in forward navigation,
|
||||
// search in scope owned by aStartContent.
|
||||
if (aForward && IsHostOrSlot(aStartContent)) {
|
||||
nsIContent* contentToFocus =
|
||||
GetNextTabbableContentInScope(aStartContent, aStartContent,
|
||||
aOriginalStartContent, aForward,
|
||||
aForward ? 1 : 0, aIgnoreTabIndex,
|
||||
aForDocumentNavigation,
|
||||
true /* aSkipOwner */);
|
||||
if (contentToFocus) {
|
||||
NS_ADDREF(*aResultContent = contentToFocus);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// If aStartContent is in a scope owned by Shadow DOM search from scope
|
||||
// including aStartContent.
|
||||
if (nsIContent* owner = FindOwner(aStartContent)) {
|
||||
nsIContent* contentToFocus =
|
||||
GetNextTabbableContentInAncestorScopes(owner,
|
||||
&aStartContent,
|
||||
aOriginalStartContent,
|
||||
aForward,
|
||||
&aCurrentTabIndex,
|
||||
aIgnoreTabIndex,
|
||||
aForDocumentNavigation);
|
||||
if (contentToFocus) {
|
||||
NS_ADDREF(*aResultContent = contentToFocus);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// If we reach here, it means no next tabbable content in shadow DOM.
|
||||
// We need to continue searching in light DOM, starting at the top level
|
||||
// shadow host in light DOM (updated aStartContent) and its tabindex
|
||||
// (updated aCurrentTabIndex).
|
||||
MOZ_ASSERT(!FindOwner(aStartContent),
|
||||
"aStartContent should not be owned by Shadow DOM at this point");
|
||||
}
|
||||
|
||||
nsPresContext* presContext = aPresShell->GetPresContext();
|
||||
|
||||
bool getNextFrame = true;
|
||||
nsCOMPtr<nsIContent> iterStartContent = aStartContent;
|
||||
// Iterate tab index to find corresponding contents
|
||||
while (1) {
|
||||
nsIFrame* startFrame = iterStartContent->GetPrimaryFrame();
|
||||
nsIFrame* frame = iterStartContent->GetPrimaryFrame();
|
||||
// if there is no frame, look for another content node that has a frame
|
||||
if (!startFrame) {
|
||||
while (!frame) {
|
||||
// if the root content doesn't have a frame, just return
|
||||
if (iterStartContent == aRootContent)
|
||||
if (iterStartContent == aRootContent) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// look for the next or previous content node in tree order
|
||||
iterStartContent = aForward ? iterStartContent->GetNextNode() : iterStartContent->GetPreviousContent();
|
||||
iterStartContent = aForward ? iterStartContent->GetNextNode()
|
||||
: iterStartContent->GetPreviousContent();
|
||||
if (!iterStartContent) {
|
||||
break;
|
||||
}
|
||||
|
||||
frame = iterStartContent->GetPrimaryFrame();
|
||||
// Host without frame, enter its scope.
|
||||
if (nsDocument::IsWebComponentsEnabled(aRootContent) &&
|
||||
(!frame && iterStartContent->GetShadowRoot())) {
|
||||
int32_t tabIndex = HostOrSlotTabIndexValue(iterStartContent);
|
||||
if (tabIndex >= 0 &&
|
||||
(aIgnoreTabIndex || aCurrentTabIndex == tabIndex)) {
|
||||
nsIContent* contentToFocus = GetNextTabbableContentInScope(
|
||||
iterStartContent, iterStartContent, aOriginalStartContent,
|
||||
aForward, aForward ? 1 : 0, aIgnoreTabIndex,
|
||||
aForDocumentNavigation, true /* aSkipOwner */);
|
||||
if (contentToFocus) {
|
||||
NS_ADDREF(*aResultContent = contentToFocus);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
// we've already skipped over the initial focused content, so we
|
||||
// don't want to traverse frames.
|
||||
getNextFrame = false;
|
||||
if (iterStartContent)
|
||||
continue;
|
||||
|
||||
// otherwise, as a last attempt, just look at the root content
|
||||
iterStartContent = aRootContent;
|
||||
continue;
|
||||
}
|
||||
|
||||
// For tab navigation, pass false for aSkipPopupChecks so that we don't
|
||||
// iterate into or out of a popup. For document naviation pass true to
|
||||
// ignore these boundaries.
|
||||
nsCOMPtr<nsIFrameEnumerator> frameTraversal;
|
||||
nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
|
||||
presContext, startFrame,
|
||||
ePreOrder,
|
||||
false, // aVisual
|
||||
false, // aLockInScrollView
|
||||
true, // aFollowOOFs
|
||||
aForDocumentNavigation // aSkipPopupChecks
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (frame) {
|
||||
// For tab navigation, pass false for aSkipPopupChecks so that we don't
|
||||
// iterate into or out of a popup. For document navigation, pass true to
|
||||
// ignore these boundaries.
|
||||
nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
|
||||
presContext,
|
||||
frame,
|
||||
ePreOrder,
|
||||
false, // aVisual
|
||||
false, // aLockInScrollView
|
||||
true, // aFollowOOFs
|
||||
aForDocumentNavigation // aSkipPopupChecks
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (iterStartContent == aRootContent) {
|
||||
if (!aForward) {
|
||||
frameTraversal->Last();
|
||||
} else if (aRootContent->IsFocusable()) {
|
||||
frameTraversal->Next();
|
||||
if (iterStartContent == aRootContent) {
|
||||
if (!aForward) {
|
||||
frameTraversal->Last();
|
||||
} else if (aRootContent->IsFocusable()) {
|
||||
frameTraversal->Next();
|
||||
}
|
||||
frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
||||
} else if (getNextFrame &&
|
||||
(!iterStartContent ||
|
||||
!iterStartContent->IsHTMLElement(nsGkAtoms::area))) {
|
||||
// Need to do special check in case we're in an imagemap which has multiple
|
||||
// content nodes per frame, so don't skip over the starting frame.
|
||||
if (aForward) {
|
||||
frameTraversal->Next();
|
||||
} else {
|
||||
frameTraversal->Prev();
|
||||
}
|
||||
|
||||
frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
||||
}
|
||||
}
|
||||
else if (getNextFrame &&
|
||||
(!iterStartContent ||
|
||||
!iterStartContent->IsHTMLElement(nsGkAtoms::area))) {
|
||||
// Need to do special check in case we're in an imagemap which has multiple
|
||||
// content nodes per frame, so don't skip over the starting frame.
|
||||
if (aForward)
|
||||
frameTraversal->Next();
|
||||
else
|
||||
frameTraversal->Prev();
|
||||
}
|
||||
|
||||
// Walk frames to find something tabbable matching mCurrentTabIndex
|
||||
nsIFrame* frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
||||
while (frame) {
|
||||
nsIContent* currentContent = frame->GetContent();
|
||||
if (nsDocument::IsWebComponentsEnabled(currentContent)) {
|
||||
// Try to find the topmost scope owner, since we want to skip the node
|
||||
// that is not owned by document in frame traversal.
|
||||
nsIContent* oldTopLevelScopeOwner = currentTopLevelScopeOwner;
|
||||
if (!aForward || oldTopLevelScopeOwner != currentContent) {
|
||||
currentTopLevelScopeOwner = GetTopLevelScopeOwner(currentContent);
|
||||
} else {
|
||||
currentTopLevelScopeOwner = currentContent;
|
||||
}
|
||||
if (currentTopLevelScopeOwner) {
|
||||
if (currentTopLevelScopeOwner == oldTopLevelScopeOwner) {
|
||||
// We're within non-document scope, continue.
|
||||
do {
|
||||
if (aForward) {
|
||||
frameTraversal->Next();
|
||||
} else {
|
||||
frameTraversal->Prev();
|
||||
}
|
||||
frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
||||
// For the usage of GetPrevContinuation, see the comment
|
||||
// at the end of while (frame) loop.
|
||||
} while (frame && frame->GetPrevContinuation());
|
||||
continue;
|
||||
}
|
||||
currentContent = currentTopLevelScopeOwner;
|
||||
}
|
||||
}
|
||||
|
||||
// For document navigation, check if this element is an open panel. Since
|
||||
// panels aren't focusable (tabIndex would be -1), we'll just assume that
|
||||
@@ -3086,7 +3555,7 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
// and root content, so that we only find content within the panel.
|
||||
// Note also that we pass false for aForDocumentNavigation since we
|
||||
// want to locate the first content, not the first document.
|
||||
rv = GetNextTabbableContent(aPresShell, currentContent,
|
||||
nsresult rv = GetNextTabbableContent(aPresShell, currentContent,
|
||||
nullptr, currentContent,
|
||||
true, 1, false, false,
|
||||
aResultContent);
|
||||
@@ -3097,6 +3566,34 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
}
|
||||
}
|
||||
|
||||
// As of now, 2018/04/12, sequential focus navigation is still
|
||||
// in the obsolete Shadow DOM specification.
|
||||
// http://w3c.github.io/webcomponents/spec/shadow/#sequential-focus-navigation
|
||||
// "if ELEMENT is focusable, a shadow host, or a slot element,
|
||||
// append ELEMENT to NAVIGATION-ORDER."
|
||||
// and later in "For each element ELEMENT in NAVIGATION-ORDER: "
|
||||
// hosts and slots are handled before other elements.
|
||||
if (nsDocument::IsWebComponentsEnabled(currentContent) &&
|
||||
IsHostOrSlot(currentContent)) {
|
||||
bool focusableHostSlot;
|
||||
int32_t tabIndex =
|
||||
HostOrSlotTabIndexValue(currentContent, &focusableHostSlot);
|
||||
// Host or slot itself isn't focusable or going backwards, enter its scope.
|
||||
if ((!aForward || !focusableHostSlot) && tabIndex >= 0 &&
|
||||
(aIgnoreTabIndex || aCurrentTabIndex == tabIndex)) {
|
||||
nsIContent* contentToFocus =
|
||||
GetNextTabbableContentInScope(currentContent, currentContent,
|
||||
aOriginalStartContent, aForward,
|
||||
aForward ? 1 : 0, aIgnoreTabIndex,
|
||||
aForDocumentNavigation,
|
||||
true /* aSkipOwner */);
|
||||
if (contentToFocus) {
|
||||
NS_ADDREF(*aResultContent = contentToFocus);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TabIndex not set defaults to 0 for form elements, anchors and other
|
||||
// elements that are normally focusable. Tabindex defaults to -1
|
||||
// for elements that are not normally focusable.
|
||||
@@ -3145,54 +3642,22 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
|
||||
// Next, for document navigation, check if this a non-remote child document.
|
||||
bool checkSubDocument = true;
|
||||
if (aForDocumentNavigation) {
|
||||
nsIContent* docRoot = GetRootForChildDocument(currentContent);
|
||||
if (docRoot) {
|
||||
// If GetRootForChildDocument returned something then call
|
||||
// FocusFirst to find the root or first element to focus within
|
||||
// the child document. If this is a frameset though, skip this and
|
||||
// fall through to the checkSubDocument block below to iterate into
|
||||
// the frameset's frames and locate the first focusable frame.
|
||||
if (!docRoot->IsHTMLElement(nsGkAtoms::frameset)) {
|
||||
return FocusFirst(docRoot, aResultContent);
|
||||
}
|
||||
} else {
|
||||
// Set checkSubDocument to false, as this was neither a frame
|
||||
// type element or a child document that was focusable.
|
||||
checkSubDocument = false;
|
||||
}
|
||||
if (aForDocumentNavigation &&
|
||||
TryDocumentNavigation(currentContent, &checkSubDocument,
|
||||
aResultContent)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (checkSubDocument) {
|
||||
// found a node with a matching tab index. Check if it is a child
|
||||
// frame. If so, navigate into the child frame instead.
|
||||
nsIDocument* doc = currentContent->GetComposedDoc();
|
||||
NS_ASSERTION(doc, "content not in document");
|
||||
nsIDocument* subdoc = doc->GetSubDocumentFor(currentContent);
|
||||
if (subdoc && !subdoc->EventHandlingSuppressed()) {
|
||||
if (aForward) {
|
||||
// when tabbing forward into a frame, return the root
|
||||
// frame so that the canvas becomes focused.
|
||||
nsCOMPtr<nsPIDOMWindowOuter> subframe = subdoc->GetWindow();
|
||||
if (subframe) {
|
||||
*aResultContent = GetRootForFocus(subframe, subdoc, false, true);
|
||||
if (*aResultContent) {
|
||||
NS_ADDREF(*aResultContent);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
Element* rootElement = subdoc->GetRootElement();
|
||||
nsIPresShell* subShell = subdoc->GetShell();
|
||||
if (rootElement && subShell) {
|
||||
rv = GetNextTabbableContent(subShell, rootElement,
|
||||
aOriginalStartContent, rootElement,
|
||||
aForward, (aForward ? 1 : 0),
|
||||
false, aForDocumentNavigation, aResultContent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (*aResultContent)
|
||||
return NS_OK;
|
||||
}
|
||||
if (TryToMoveFocusToSubDocument(currentContent,
|
||||
aOriginalStartContent,
|
||||
aForward,
|
||||
aForDocumentNavigation,
|
||||
aResultContent)) {
|
||||
MOZ_ASSERT(*aResultContent);
|
||||
return NS_OK;
|
||||
}
|
||||
// otherwise, use this as the next content node to tab to, unless
|
||||
// this was the element we started on. This would happen for
|
||||
@@ -3263,6 +3728,77 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsFocusManager::TryDocumentNavigation(nsIContent* aCurrentContent,
|
||||
bool* aCheckSubDocument,
|
||||
nsIContent** aResultContent)
|
||||
{
|
||||
*aCheckSubDocument = true;
|
||||
nsIContent* docRoot = GetRootForChildDocument(aCurrentContent);
|
||||
if (docRoot) {
|
||||
// If GetRootForChildDocument returned something then call
|
||||
// FocusFirst to find the root or first element to focus within
|
||||
// the child document. If this is a frameset though, skip this and
|
||||
// fall through to normal tab navigation to iterate into
|
||||
// the frameset's frames and locate the first focusable frame.
|
||||
if (!docRoot->IsHTMLElement(nsGkAtoms::frameset)) {
|
||||
*aCheckSubDocument = false;
|
||||
Unused << FocusFirst(docRoot, aResultContent);
|
||||
return *aResultContent != nullptr;
|
||||
}
|
||||
} else {
|
||||
// Set aCheckSubDocument to false, as this was neither a frame
|
||||
// type element or a child document that was focusable.
|
||||
*aCheckSubDocument = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsFocusManager::TryToMoveFocusToSubDocument(nsIContent* aCurrentContent,
|
||||
nsIContent* aOriginalStartContent,
|
||||
bool aForward,
|
||||
bool aForDocumentNavigation,
|
||||
nsIContent** aResultContent)
|
||||
{
|
||||
nsIDocument* doc = aCurrentContent->GetComposedDoc();
|
||||
NS_ASSERTION(doc, "content not in document");
|
||||
nsIDocument* subdoc = doc->GetSubDocumentFor(aCurrentContent);
|
||||
if (subdoc && !subdoc->EventHandlingSuppressed()) {
|
||||
if (aForward) {
|
||||
// when tabbing forward into a frame, return the root
|
||||
// frame so that the canvas becomes focused.
|
||||
nsCOMPtr<nsPIDOMWindowOuter> subframe = subdoc->GetWindow();
|
||||
if (subframe) {
|
||||
*aResultContent = GetRootForFocus(subframe, subdoc, false, true);
|
||||
if (*aResultContent) {
|
||||
NS_ADDREF(*aResultContent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Element* rootElement = subdoc->GetRootElement();
|
||||
nsIPresShell* subShell = subdoc->GetShell();
|
||||
if (rootElement && subShell) {
|
||||
nsresult rv = GetNextTabbableContent(subShell,
|
||||
rootElement,
|
||||
aOriginalStartContent,
|
||||
rootElement,
|
||||
aForward,
|
||||
(aForward ? 1 : 0),
|
||||
false,
|
||||
aForDocumentNavigation,
|
||||
aResultContent);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
if (*aResultContent) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::GetNextTabbableMapArea(bool aForward,
|
||||
int32_t aCurrentTabIndex,
|
||||
@@ -3309,15 +3845,20 @@ nsFocusManager::GetNextTabIndex(nsIContent* aParent,
|
||||
bool aForward)
|
||||
{
|
||||
int32_t tabIndex, childTabIndex;
|
||||
StyleChildrenIterator iter(aParent);
|
||||
|
||||
if (aForward) {
|
||||
tabIndex = 0;
|
||||
for (nsIContent* child = aParent->GetFirstChild();
|
||||
for (nsIContent* child = iter.GetNextChild();
|
||||
child;
|
||||
child = child->GetNextSibling()) {
|
||||
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
|
||||
if (childTabIndex > aCurrentTabIndex && childTabIndex != tabIndex) {
|
||||
tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex : tabIndex;
|
||||
child = iter.GetNextChild()) {
|
||||
// Skip child's descendants if child is a shadow host, as they are
|
||||
// in the focus navigation scope owned by child's shadow root
|
||||
if (!(nsDocument::IsWebComponentsEnabled(aParent) && IsHostOrSlot(child))) {
|
||||
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
|
||||
if (childTabIndex > aCurrentTabIndex && childTabIndex != tabIndex) {
|
||||
tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex : tabIndex;
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString tabIndexStr;
|
||||
@@ -3331,13 +3872,17 @@ nsFocusManager::GetNextTabIndex(nsIContent* aParent,
|
||||
}
|
||||
else { /* !aForward */
|
||||
tabIndex = 1;
|
||||
for (nsIContent* child = aParent->GetFirstChild();
|
||||
for (nsIContent* child = iter.GetNextChild();
|
||||
child;
|
||||
child = child->GetNextSibling()) {
|
||||
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
|
||||
if ((aCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
|
||||
(childTabIndex < aCurrentTabIndex && childTabIndex > tabIndex)) {
|
||||
tabIndex = childTabIndex;
|
||||
child = iter.GetNextChild()) {
|
||||
// Skip child's descendants if child is a shadow host, as they are
|
||||
// in the focus navigation scope owned by child's shadow root
|
||||
if (!(nsDocument::IsWebComponentsEnabled(aParent) && IsHostOrSlot(child))) {
|
||||
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
|
||||
if ((aCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
|
||||
(childTabIndex < aCurrentTabIndex && childTabIndex > tabIndex)) {
|
||||
tabIndex = childTabIndex;
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString tabIndexStr;
|
||||
|
||||
@@ -384,6 +384,91 @@ protected:
|
||||
int32_t aType, bool aNoParentTraversal,
|
||||
nsIContent** aNextContent);
|
||||
|
||||
/**
|
||||
* Returns scope owner of aContent.
|
||||
* A scope owner is either a document root, shadow host, or slot.
|
||||
*/
|
||||
nsIContent* FindOwner(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Retrieve the next tabbable element in scope owned by aOwner, using
|
||||
* focusability and tabindex to determine the tab order.
|
||||
*
|
||||
* aOwner is the owner of scope to search in.
|
||||
*
|
||||
* aStartContent is the starting point for this call of this method.
|
||||
*
|
||||
* aOriginalStartContent is the initial starting point for sequential
|
||||
* navigation.
|
||||
*
|
||||
* aForward should be true for forward navigation or false for backward
|
||||
* navigation.
|
||||
*
|
||||
* aCurrentTabIndex is the current tabindex.
|
||||
*
|
||||
* aIgnoreTabIndex to ignore the current tabindex and find the element
|
||||
* irrespective or the tab index.
|
||||
*
|
||||
* aForDocumentNavigation informs whether we're navigating only through
|
||||
* documents.
|
||||
*
|
||||
* aSkipOwner to skip owner while searching. The flag is set when caller is
|
||||
* |GetNextTabbableContent| in order to let caller handle owner.
|
||||
*
|
||||
* NOTE:
|
||||
* Consider the method searches downwards in flattened subtree
|
||||
* rooted at aOwner.
|
||||
*/
|
||||
nsIContent* GetNextTabbableContentInScope(nsIContent* aOwner,
|
||||
nsIContent* aStartContent,
|
||||
nsIContent* aOriginalStartContent,
|
||||
bool aForward,
|
||||
int32_t aCurrentTabIndex,
|
||||
bool aIgnoreTabIndex,
|
||||
bool aForDocumentNavigation,
|
||||
bool aSkipOwner);
|
||||
|
||||
/**
|
||||
* Retrieve the next tabbable element in scope including aStartContent
|
||||
* and the scope's ancestor scopes, using focusability and tabindex to
|
||||
* determine the tab order.
|
||||
*
|
||||
* aStartOwner is the scope owner of the aStartContent.
|
||||
*
|
||||
* aStartContent an in/out paremeter. It as input is the starting point
|
||||
* for this call of this method; as output it is the shadow host in
|
||||
* light DOM if the next tabbable element is not found in shadow DOM,
|
||||
* in order to continue searching in light DOM.
|
||||
*
|
||||
* aOriginalStartContent is the initial starting point for sequential
|
||||
* navigation.
|
||||
*
|
||||
* aForward should be true for forward navigation or false for backward
|
||||
* navigation.
|
||||
*
|
||||
* aCurrentTabIndex returns tab index of shadow host in light DOM if the
|
||||
* next tabbable element is not found in shadow DOM, in order to continue
|
||||
* searching in light DOM.
|
||||
*
|
||||
* aIgnoreTabIndex to ignore the current tabindex and find the element
|
||||
* irrespective or the tab index.
|
||||
*
|
||||
* aForDocumentNavigation informs whether we're navigating only through
|
||||
* documents.
|
||||
*
|
||||
* NOTE:
|
||||
* Consider the method searches upwards in all shadow host- or slot-rooted
|
||||
* flattened subtrees that contains aStartContent as non-root, except
|
||||
* the flattened subtree rooted at shadow host in light DOM.
|
||||
*/
|
||||
nsIContent* GetNextTabbableContentInAncestorScopes(nsIContent* aStartOwner,
|
||||
nsIContent** aStartContent,
|
||||
nsIContent* aOriginalStartContent,
|
||||
bool aForward,
|
||||
int32_t* aCurrentTabIndex,
|
||||
bool aIgnoreTabIndex,
|
||||
bool aForDocumentNavigation);
|
||||
|
||||
/**
|
||||
* Retrieve the next tabbable element within a document, using focusability
|
||||
* and tabindex to determine the tab order. The element is returned in
|
||||
@@ -503,6 +588,16 @@ private:
|
||||
|
||||
void SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow);
|
||||
|
||||
bool TryDocumentNavigation(nsIContent* aCurrentContent,
|
||||
bool* aCheckSubDocument,
|
||||
nsIContent** aResultContent);
|
||||
|
||||
bool TryToMoveFocusToSubDocument(nsIContent* aCurrentContent,
|
||||
nsIContent* aOriginalStartContent,
|
||||
bool aForward,
|
||||
bool aForDocumentNavigation,
|
||||
nsIContent** aResultContent);
|
||||
|
||||
// the currently active and front-most top-most window
|
||||
nsCOMPtr<nsPIDOMWindowOuter> mActiveWindow;
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ GK_ATOM(_moz_resizing, "_moz_resizing")
|
||||
GK_ATOM(mozallowfullscreen, "mozallowfullscreen")
|
||||
GK_ATOM(moztype, "_moz-type")
|
||||
GK_ATOM(mozdirty, "_moz_dirty")
|
||||
GK_ATOM(_is_cropped, "_is_cropped")
|
||||
GK_ATOM(mozdisallowselectionprint, "mozdisallowselectionprint")
|
||||
GK_ATOM(mozdonotsend, "moz-do-not-send")
|
||||
GK_ATOM(mozeditorbogusnode, "_moz_editor_bogus_node")
|
||||
|
||||
@@ -284,7 +284,7 @@ nsHTMLContentSerializer::AppendElementStart(Element* aElement,
|
||||
if (ns == kNameSpaceID_XHTML &&
|
||||
(name == nsGkAtoms::script ||
|
||||
name == nsGkAtoms::style ||
|
||||
name == nsGkAtoms::noscript ||
|
||||
(name == nsGkAtoms::noscript && aElement->OwnerDoc()->IsScriptEnabled()) ||
|
||||
name == nsGkAtoms::noframes)) {
|
||||
++mDisableEntityEncoding;
|
||||
}
|
||||
@@ -314,7 +314,7 @@ nsHTMLContentSerializer::AppendElementEnd(Element* aElement,
|
||||
if (ns == kNameSpaceID_XHTML &&
|
||||
(name == nsGkAtoms::script ||
|
||||
name == nsGkAtoms::style ||
|
||||
name == nsGkAtoms::noscript ||
|
||||
(name == nsGkAtoms::noscript && aElement->OwnerDoc()->IsScriptEnabled()) ||
|
||||
name == nsGkAtoms::noframes)) {
|
||||
--mDisableEntityEncoding;
|
||||
}
|
||||
|
||||
+12
-26
@@ -1811,26 +1811,6 @@ public:
|
||||
nsIAtom* aAttrName,
|
||||
const nsAString& aAttrValue) const = 0;
|
||||
|
||||
/**
|
||||
* Helper for nsIDOMDocument::elementFromPoint implementation that allows
|
||||
* ignoring the scroll frame and/or avoiding layout flushes.
|
||||
*
|
||||
* @see nsIDOMWindowUtils::elementFromPoint
|
||||
*/
|
||||
virtual Element* ElementFromPointHelper(float aX, float aY,
|
||||
bool aIgnoreRootScrollFrame,
|
||||
bool aFlushLayout) = 0;
|
||||
|
||||
enum ElementsFromPointFlags {
|
||||
IGNORE_ROOT_SCROLL_FRAME = 1,
|
||||
FLUSH_LAYOUT = 2,
|
||||
IS_ELEMENT_FROM_POINT = 4
|
||||
};
|
||||
|
||||
virtual void ElementsFromPointHelper(float aX, float aY,
|
||||
uint32_t aFlags,
|
||||
nsTArray<RefPtr<mozilla::dom::Element>>& aElements) = 0;
|
||||
|
||||
virtual nsresult NodesFromRectHelper(float aX, float aY,
|
||||
float aTopSize, float aRightSize,
|
||||
float aBottomSize, float aLeftSize,
|
||||
@@ -2637,17 +2617,17 @@ public:
|
||||
nsIURI* GetDocumentURIObject() const;
|
||||
// Not const because all the full-screen goop is not const
|
||||
virtual bool FullscreenEnabled() = 0;
|
||||
virtual Element* GetFullscreenElement() = 0;
|
||||
virtual Element* FullScreenStackTop() = 0;
|
||||
bool Fullscreen()
|
||||
{
|
||||
return !!GetFullscreenElement();
|
||||
}
|
||||
void ExitFullscreen();
|
||||
Element* GetPointerLockElement();
|
||||
void ExitPointerLock()
|
||||
{
|
||||
UnlockPointer(this);
|
||||
}
|
||||
static bool IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject);
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
bool Hidden() const
|
||||
{
|
||||
@@ -2664,10 +2644,6 @@ public:
|
||||
void GetPreferredStyleSheetSet(nsAString& aSheetSet);
|
||||
virtual mozilla::dom::DOMStringList* StyleSheetSets() = 0;
|
||||
virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) = 0;
|
||||
Element* ElementFromPoint(float aX, float aY);
|
||||
void ElementsFromPoint(float aX,
|
||||
float aY,
|
||||
nsTArray<RefPtr<mozilla::dom::Element>>& aElements);
|
||||
|
||||
/**
|
||||
* Retrieve the location of the caret position (DOM node and character
|
||||
@@ -2835,6 +2811,16 @@ public:
|
||||
|
||||
bool ModuleScriptsEnabled();
|
||||
|
||||
/**
|
||||
* Find the (non-anonymous) content in this document for aFrame. It will
|
||||
* be aFrame's content node if that content is in this document and not
|
||||
* anonymous. Otherwise, when aFrame is in a subdocument, we use the frame
|
||||
* element containing the subdocument containing aFrame, and/or find the
|
||||
* nearest non-anonymous ancestor in this document.
|
||||
* Returns null if there is no such element.
|
||||
*/
|
||||
nsIContent* GetContentInThisDocument(nsIFrame* aFrame) const;
|
||||
|
||||
virtual void AddResizeObserver(mozilla::dom::ResizeObserver* aResizeObserver) = 0;
|
||||
|
||||
virtual void ScheduleResizeObserversNotification() const = 0;
|
||||
|
||||
+47
-36
@@ -273,6 +273,18 @@ nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions)
|
||||
return SubtreeRoot();
|
||||
}
|
||||
|
||||
// TODO: was marked as constant.
|
||||
nsINode*
|
||||
nsINode::GetParentOrHostNode()
|
||||
{
|
||||
if (mParent) {
|
||||
return mParent;
|
||||
}
|
||||
|
||||
ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
|
||||
return shadowRoot ? shadowRoot->GetHost() : nullptr;
|
||||
}
|
||||
|
||||
nsINode*
|
||||
nsINode::SubtreeRoot() const
|
||||
{
|
||||
@@ -1543,32 +1555,25 @@ nsINode::SetExplicitBaseURI(nsIURI* aURI)
|
||||
return rv;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode)
|
||||
static void
|
||||
AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode, ErrorResult& aError)
|
||||
{
|
||||
NS_ASSERTION(!aNode->GetParentNode(),
|
||||
"Should have removed from parent already");
|
||||
|
||||
nsIDocument *doc = aParent->OwnerDoc();
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
DebugOnly<nsINode*> adoptedNode = doc->AdoptNode(*aNode, aError);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> adoptedNode;
|
||||
rv = domDoc->AdoptNode(node, getter_AddRefs(adoptedNode));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ASSERTION(aParent->OwnerDoc() == doc,
|
||||
"ownerDoc chainged while adopting");
|
||||
NS_ASSERTION(adoptedNode == node, "Uh, adopt node changed nodes?");
|
||||
NS_ASSERTION(aParent->OwnerDoc() == aNode->OwnerDoc(),
|
||||
#ifdef DEBUG
|
||||
if (!aError.Failed()) {
|
||||
MOZ_ASSERT(aParent->OwnerDoc() == doc,
|
||||
"ownerDoc changed while adopting");
|
||||
MOZ_ASSERT(adoptedNode == aNode, "Uh, adopt node changed nodes?");
|
||||
MOZ_ASSERT(aParent->OwnerDoc() == aNode->OwnerDoc(),
|
||||
"ownerDocument changed again after adopting!");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
static nsresult
|
||||
@@ -1593,19 +1598,20 @@ ReparentWrappersInSubtree(nsIContent* aRoot)
|
||||
|
||||
rootedGlobal = xpc::GetXBLScope(cx, rootedGlobal);
|
||||
|
||||
nsresult rv;
|
||||
ErrorResult rv;
|
||||
JS::Rooted<JSObject*> reflector(cx);
|
||||
for (nsIContent* cur = aRoot; cur; cur = cur->GetNextNode(aRoot)) {
|
||||
if ((reflector = cur->GetWrapper())) {
|
||||
JSAutoCompartment ac(cx, reflector);
|
||||
rv = ReparentWrapper(cx, reflector);
|
||||
if NS_FAILED(rv) {
|
||||
ReparentWrapper(cx, reflector, rv);
|
||||
rv.WouldReportJSException();
|
||||
if (rv.Failed()) {
|
||||
// We _could_ consider BlastSubtreeToPieces here, but it's not really
|
||||
// needed. Having some nodes in here accessible to content while others
|
||||
// are not is probably OK. We just need to fail out of the actual
|
||||
// insertion, so they're not in the DOM. Returning a failure here will
|
||||
// do that.
|
||||
return rv;
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1619,7 +1625,6 @@ nsINode::doInsertChildAt(nsIContent* aKid, uint32_t aIndex,
|
||||
{
|
||||
NS_PRECONDITION(!aKid->GetParentNode(),
|
||||
"Inserting node that already has parent");
|
||||
nsresult rv;
|
||||
|
||||
// The id-handling code, and in the future possibly other code, need to
|
||||
// react to unexpected attribute changes.
|
||||
@@ -1630,15 +1635,23 @@ nsINode::doInsertChildAt(nsIContent* aKid, uint32_t aIndex,
|
||||
mozAutoDocUpdate updateBatch(GetComposedDoc(), UPDATE_CONTENT_MODEL, aNotify);
|
||||
|
||||
if (OwnerDoc() != aKid->OwnerDoc()) {
|
||||
rv = AdoptNodeIntoOwnerDoc(this, aKid);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
ErrorResult error;
|
||||
AdoptNodeIntoOwnerDoc(this, aKid, error);
|
||||
|
||||
// Need to WouldReportJSException() if our callee can throw a JS
|
||||
// exception (which it can) and we're neither propagating the
|
||||
// error out nor unconditionally suppressing it.
|
||||
error.WouldReportJSException();
|
||||
if (NS_WARN_IF(error.Failed())) {
|
||||
return error.StealNSResult();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t childCount = aChildArray.ChildCount();
|
||||
NS_ENSURE_TRUE(aIndex <= childCount, NS_ERROR_ILLEGAL_VALUE);
|
||||
bool isAppend = (aIndex == childCount);
|
||||
|
||||
rv = aChildArray.InsertChildAt(aKid, aIndex);
|
||||
nsresult rv = aChildArray.InsertChildAt(aKid, aIndex);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (aIndex == 0) {
|
||||
mFirstChild = aKid;
|
||||
@@ -2475,7 +2488,7 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
|
||||
// inserting them w/o calling AdoptNode().
|
||||
nsIDocument* doc = OwnerDoc();
|
||||
if (doc != newContent->OwnerDoc()) {
|
||||
aError = AdoptNodeIntoOwnerDoc(this, aNewChild);
|
||||
AdoptNodeIntoOwnerDoc(this, aNewChild, aError);
|
||||
if (aError.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -2879,9 +2892,9 @@ FindMatchingElementsWithId(const nsAString& aId, nsINode* aRoot,
|
||||
// We have an element with the right id and it's a strict descendant
|
||||
// of aRoot. Make sure it really matches the selector.
|
||||
if (!aMatchInfo ||
|
||||
nsCSSRuleProcessor::SelectorListMatches(element,
|
||||
aMatchInfo->mMatchContext,
|
||||
aMatchInfo->mSelectorList)) {
|
||||
nsCSSRuleProcessor::RestrictedSelectorListMatches(element,
|
||||
aMatchInfo->mMatchContext,
|
||||
aMatchInfo->mSelectorList)) {
|
||||
aList.AppendElement(element);
|
||||
if (onlyFirstMatch) {
|
||||
return;
|
||||
@@ -2932,9 +2945,9 @@ FindMatchingElements(nsINode* aRoot, nsCSSSelectorList* aSelectorList, T &aList,
|
||||
cur;
|
||||
cur = cur->GetNextNode(aRoot)) {
|
||||
if (cur->IsElement() &&
|
||||
nsCSSRuleProcessor::SelectorListMatches(cur->AsElement(),
|
||||
matchingContext,
|
||||
aSelectorList)) {
|
||||
nsCSSRuleProcessor::RestrictedSelectorListMatches(cur->AsElement(),
|
||||
matchingContext,
|
||||
aSelectorList)) {
|
||||
if (onlyFirstMatch) {
|
||||
aList.AppendElement(cur->AsElement());
|
||||
return;
|
||||
@@ -3072,9 +3085,7 @@ nsINode::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
already_AddRefed<nsINode>
|
||||
nsINode::CloneNode(bool aDeep, ErrorResult& aError)
|
||||
{
|
||||
nsCOMPtr<nsINode> result;
|
||||
aError = nsNodeUtils::CloneNodeImpl(this, aDeep, getter_AddRefs(result));
|
||||
return result.forget();
|
||||
return nsNodeUtils::CloneNodeImpl(this, aDeep, aError);
|
||||
}
|
||||
|
||||
nsDOMAttributeMap*
|
||||
|
||||
@@ -942,6 +942,12 @@ public:
|
||||
return mParent;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is similar to above, but in case 'this' is ShadowRoot, we return its
|
||||
* host element.
|
||||
*/
|
||||
nsINode* GetParentOrHostNode();
|
||||
|
||||
/**
|
||||
* Returns the node that is the parent of this node in the flattened
|
||||
* tree. This differs from the normal parent if the node is filtered
|
||||
|
||||
@@ -1412,16 +1412,16 @@ nsImageLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
bool aCompileEventHandlers)
|
||||
{
|
||||
// We may be entering the document, so if our image should be tracked,
|
||||
// track it.
|
||||
if (!aDocument)
|
||||
// We may be getting connected and our image should be tracked.
|
||||
nsCOMPtr<nsIDocument> doc = GetOurCurrentDoc();
|
||||
if (!doc)
|
||||
return;
|
||||
|
||||
TrackImage(mCurrentRequest);
|
||||
TrackImage(mPendingRequest);
|
||||
|
||||
if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
|
||||
aDocument->BlockOnload();
|
||||
doc->BlockOnload();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -224,16 +224,23 @@ ProcessNameForCollectorLog()
|
||||
|
||||
namespace xpc {
|
||||
|
||||
// This handles JS Exceptions (via ExceptionStackOrNull), as well as DOM and XPC
|
||||
// Exceptions.
|
||||
// This handles JS Exceptions (via ExceptionStackOrNull), DOM and XPC
|
||||
// Exceptions, and arbitrary values that were associated with a stack by the
|
||||
// JS engine when they were thrown, as specified by exceptionStack.
|
||||
//
|
||||
// Note that the returned object is _not_ wrapped into the compartment of
|
||||
// exceptionValue.
|
||||
JSObject*
|
||||
FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
|
||||
JS::HandleValue exceptionValue)
|
||||
JS::HandleValue exceptionValue,
|
||||
JS::HandleObject exceptionStack)
|
||||
{
|
||||
if (!exceptionValue.isObject()) {
|
||||
// Use the stack provided by the JS engine, if available. This will not be
|
||||
// a wrapper.
|
||||
if (exceptionStack) {
|
||||
return exceptionStack;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -257,6 +264,10 @@ FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
|
||||
// Not a DOM Exception, try XPC Exception.
|
||||
UNWRAP_OBJECT(Exception, exceptionObject, exception);
|
||||
if (!exception) {
|
||||
// As above, use the stack provided by the JS engine, if available.
|
||||
if (exceptionStack) {
|
||||
return exceptionStack;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -421,10 +432,12 @@ public:
|
||||
ScriptErrorEvent(nsPIDOMWindowInner* aWindow,
|
||||
JS::RootingContext* aRootingCx,
|
||||
xpc::ErrorReport* aReport,
|
||||
JS::Handle<JS::Value> aError)
|
||||
JS::Handle<JS::Value> aError,
|
||||
JS::Handle<JSObject*> aErrorStack)
|
||||
: mWindow(aWindow)
|
||||
, mReport(aReport)
|
||||
, mError(aRootingCx, aError)
|
||||
, mErrorStack(aRootingCx, aErrorStack)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
@@ -471,7 +484,7 @@ public:
|
||||
|
||||
if (status != nsEventStatus_eConsumeNoDefault) {
|
||||
JS::Rooted<JSObject*> stack(rootingCx,
|
||||
xpc::FindExceptionStackForConsoleReport(win, mError));
|
||||
xpc::FindExceptionStackForConsoleReport(win, mError, mErrorStack));
|
||||
mReport->LogToConsoleWithStack(stack);
|
||||
}
|
||||
|
||||
@@ -481,7 +494,8 @@ public:
|
||||
private:
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
RefPtr<xpc::ErrorReport> mReport;
|
||||
JS::PersistentRootedValue mError;
|
||||
JS::PersistentRootedValue mError;
|
||||
JS::PersistentRootedObject mErrorStack;
|
||||
|
||||
static bool sHandlingScriptError;
|
||||
};
|
||||
@@ -494,9 +508,10 @@ namespace xpc {
|
||||
|
||||
void
|
||||
DispatchScriptErrorEvent(nsPIDOMWindowInner *win, JS::RootingContext* rootingCx,
|
||||
xpc::ErrorReport *xpcReport, JS::Handle<JS::Value> exception)
|
||||
xpc::ErrorReport *xpcReport, JS::Handle<JS::Value> exception,
|
||||
JS::Handle<JSObject*> exceptionStack)
|
||||
{
|
||||
nsContentUtils::AddScriptRunner(new ScriptErrorEvent(win, rootingCx, xpcReport, exception));
|
||||
nsContentUtils::AddScriptRunner(new ScriptErrorEvent(win, rootingCx, xpcReport, exception, exceptionStack));
|
||||
}
|
||||
|
||||
} /* namespace xpc */
|
||||
|
||||
+79
-42
@@ -402,28 +402,20 @@ nsNodeUtils::TraverseUserData(nsINode* aNode,
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
nsNodeUtils::CloneNodeImpl(nsINode *aNode, bool aDeep, nsINode **aResult)
|
||||
already_AddRefed<nsINode>
|
||||
nsNodeUtils::CloneNodeImpl(nsINode *aNode, bool aDeep, ErrorResult& aError)
|
||||
{
|
||||
*aResult = nullptr;
|
||||
|
||||
nsCOMPtr<nsINode> newNode;
|
||||
nsCOMArray<nsINode> nodesWithProperties;
|
||||
nsresult rv = Clone(aNode, aDeep, nullptr, nodesWithProperties,
|
||||
getter_AddRefs(newNode));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
newNode.forget(aResult);
|
||||
return NS_OK;
|
||||
return Clone(aNode, aDeep, nullptr, &nodesWithProperties, aError);
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
already_AddRefed<nsINode>
|
||||
nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
nsNodeInfoManager *aNewNodeInfoManager,
|
||||
JS::Handle<JSObject*> aReparentScope,
|
||||
nsCOMArray<nsINode> &aNodesWithProperties,
|
||||
nsINode *aParent, nsINode **aResult)
|
||||
nsCOMArray<nsINode> *aNodesWithProperties,
|
||||
nsINode *aParent, ErrorResult& aError)
|
||||
{
|
||||
NS_PRECONDITION((!aClone && aNewNodeInfoManager) || !aReparentScope,
|
||||
"If cloning or not getting a new nodeinfo we shouldn't "
|
||||
@@ -431,14 +423,11 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
NS_PRECONDITION(!aParent || aNode->IsNodeOfType(nsINode::eCONTENT),
|
||||
"Can't insert document or attribute nodes into a parent");
|
||||
|
||||
*aResult = nullptr;
|
||||
|
||||
// First deal with aNode and walk its attributes (and their children). Then,
|
||||
// if aDeep is true, deal with aNode's children (and recurse into their
|
||||
// attributes and children).
|
||||
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
nsresult rv;
|
||||
|
||||
nsNodeInfoManager *nodeInfoManager = aNewNodeInfoManager;
|
||||
|
||||
@@ -450,14 +439,20 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
// Don't allow importing/adopting nodes from non-privileged "scriptable"
|
||||
// documents to "non-scriptable" documents.
|
||||
nsIDocument* newDoc = nodeInfoManager->GetDocument();
|
||||
NS_ENSURE_STATE(newDoc);
|
||||
if (NS_WARN_IF(!newDoc)) {
|
||||
aError.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
bool hasHadScriptHandlingObject = false;
|
||||
if (!newDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
|
||||
!hasHadScriptHandlingObject) {
|
||||
nsIDocument* currentDoc = aNode->OwnerDoc();
|
||||
NS_ENSURE_STATE((nsContentUtils::IsChromeDoc(currentDoc) ||
|
||||
(!currentDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
|
||||
!hasHadScriptHandlingObject)));
|
||||
if (NS_WARN_IF(!nsContentUtils::IsChromeDoc(currentDoc) &&
|
||||
(currentDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) ||
|
||||
hasHadScriptHandlingObject))) {
|
||||
aError.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
newNodeInfo = nodeInfoManager->GetNodeInfo(nodeInfo->NameAtom(),
|
||||
@@ -473,8 +468,11 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
|
||||
nsCOMPtr<nsINode> clone;
|
||||
if (aClone) {
|
||||
rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsresult rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aError.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (CustomElementRegistry::IsCustomElementEnabled() &&
|
||||
clone->IsHTMLElement()) {
|
||||
@@ -512,7 +510,10 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
// parent.
|
||||
rv = aParent->AppendChildTo(static_cast<nsIContent*>(clone.get()),
|
||||
false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aError.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else if (aDeep && clone->IsNodeOfType(nsINode::eDOCUMENT)) {
|
||||
// After cloning the document itself, we want to clone the children into
|
||||
@@ -611,8 +612,8 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
if ((wrapper = aNode->GetWrapper())) {
|
||||
MOZ_ASSERT(IsDOMObject(wrapper));
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
rv = ReparentWrapper(cx, wrapper);
|
||||
if (NS_FAILED(rv)) {
|
||||
ReparentWrapper(cx, wrapper, aError);
|
||||
if (aError.Failed()) {
|
||||
if (wasRegistered) {
|
||||
aNode->OwnerDoc()->UnregisterActivityObserver(aNode->AsElement());
|
||||
}
|
||||
@@ -620,17 +621,17 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
if (wasRegistered) {
|
||||
aNode->OwnerDoc()->RegisterActivityObserver(aNode->AsElement());
|
||||
}
|
||||
return rv;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aNode->HasProperties()) {
|
||||
bool ok = aNodesWithProperties.AppendObject(aNode);
|
||||
bool ok = aNodesWithProperties->AppendObject(aNode);
|
||||
MOZ_RELEASE_ASSERT(ok, "Out of memory");
|
||||
if (aClone) {
|
||||
ok = aNodesWithProperties.AppendObject(clone);
|
||||
ok = aNodesWithProperties->AppendObject(clone);
|
||||
MOZ_RELEASE_ASSERT(ok, "Out of memory");
|
||||
}
|
||||
}
|
||||
@@ -640,11 +641,47 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
for (nsIContent* cloneChild = aNode->GetFirstChild();
|
||||
cloneChild;
|
||||
cloneChild = cloneChild->GetNextSibling()) {
|
||||
nsCOMPtr<nsINode> child;
|
||||
rv = CloneAndAdopt(cloneChild, aClone, true, nodeInfoManager,
|
||||
aReparentScope, aNodesWithProperties, clone,
|
||||
getter_AddRefs(child));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsINode> child =
|
||||
CloneAndAdopt(cloneChild, aClone, true, nodeInfoManager,
|
||||
aReparentScope, aNodesWithProperties, clone,
|
||||
aError);
|
||||
if (NS_WARN_IF(aError.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aDeep && aNode->IsElement()) {
|
||||
ShadowRoot* shadowRoot = aNode->AsElement()->GetShadowRoot();
|
||||
if (!shadowRoot) {
|
||||
// Nothing to do here.
|
||||
} else if (aClone && clone->OwnerDoc()->IsStaticDocument()) {
|
||||
ShadowRootInit init;
|
||||
init.mMode = shadowRoot->Mode();
|
||||
RefPtr<ShadowRoot> newShadowRoot =
|
||||
clone->AsElement()->AttachShadow(init, aError);
|
||||
if (NS_WARN_IF(aError.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
newShadowRoot->CloneInternalDataFrom(shadowRoot);
|
||||
for (nsIContent* originalChild = shadowRoot->GetFirstChild();
|
||||
originalChild;
|
||||
originalChild = originalChild->GetNextSibling()) {
|
||||
nsCOMPtr<nsINode> child = CloneAndAdopt(originalChild, aClone, aDeep, nodeInfoManager,
|
||||
aReparentScope, aNodesWithProperties, newShadowRoot,
|
||||
aError);
|
||||
if (NS_WARN_IF(aError.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} else if (!aClone) {
|
||||
nsCOMPtr<nsINode> child = CloneAndAdopt(shadowRoot, aClone, aDeep, nodeInfoManager,
|
||||
aReparentScope, aNodesWithProperties, clone,
|
||||
aError);
|
||||
if (NS_WARN_IF(aError.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,11 +700,13 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
for (nsIContent* cloneChild = origContent->GetFirstChild();
|
||||
cloneChild;
|
||||
cloneChild = cloneChild->GetNextSibling()) {
|
||||
nsCOMPtr<nsINode> child;
|
||||
rv = CloneAndAdopt(cloneChild, aClone, aDeep, ownerNodeInfoManager,
|
||||
aReparentScope, aNodesWithProperties, cloneContent,
|
||||
getter_AddRefs(child));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsINode> child =
|
||||
CloneAndAdopt(cloneChild, aClone, aDeep, ownerNodeInfoManager,
|
||||
aReparentScope, aNodesWithProperties, cloneContent,
|
||||
aError);
|
||||
if (NS_WARN_IF(aError.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,9 +727,7 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
}
|
||||
}
|
||||
|
||||
clone.forget(aResult);
|
||||
|
||||
return NS_OK;
|
||||
return clone.forget();
|
||||
}
|
||||
|
||||
|
||||
|
||||
+36
-35
@@ -17,6 +17,7 @@ template<class E> class nsCOMArray;
|
||||
class nsCycleCollectionTraversalCallback;
|
||||
namespace mozilla {
|
||||
struct NonOwningAnimationTarget;
|
||||
class ErrorResult;
|
||||
namespace dom {
|
||||
class Animation;
|
||||
} // namespace dom
|
||||
@@ -181,25 +182,17 @@ public:
|
||||
* @param aNodesWithProperties All nodes (from amongst aNode and its
|
||||
* descendants) with properties. Every node will
|
||||
* be followed by its clone.
|
||||
* @param aResult *aResult will contain the cloned node.
|
||||
* @param aError The error, if any.
|
||||
*
|
||||
* @return The newly created node. Null in error conditions.
|
||||
*/
|
||||
static nsresult Clone(nsINode *aNode, bool aDeep,
|
||||
nsNodeInfoManager *aNewNodeInfoManager,
|
||||
nsCOMArray<nsINode> &aNodesWithProperties,
|
||||
nsINode **aResult)
|
||||
static already_AddRefed<nsINode> Clone(nsINode *aNode, bool aDeep,
|
||||
nsNodeInfoManager *aNewNodeInfoManager,
|
||||
nsCOMArray<nsINode> *aNodesWithProperties,
|
||||
mozilla::ErrorResult& aError)
|
||||
{
|
||||
return CloneAndAdopt(aNode, true, aDeep, aNewNodeInfoManager,
|
||||
nullptr, aNodesWithProperties, nullptr, aResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones aNode, its attributes and, if aDeep is true, its descendant nodes
|
||||
*/
|
||||
static nsresult Clone(nsINode *aNode, bool aDeep, nsINode **aResult)
|
||||
{
|
||||
nsCOMArray<nsINode> dummyNodeWithProperties;
|
||||
return CloneAndAdopt(aNode, true, aDeep, nullptr, nullptr,
|
||||
dummyNodeWithProperties, aNode->GetParent(), aResult);
|
||||
nullptr, aNodesWithProperties, nullptr, aError);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,19 +211,20 @@ public:
|
||||
* should be done.
|
||||
* @param aNodesWithProperties All nodes (from amongst aNode and its
|
||||
* descendants) with properties.
|
||||
* @param aError The error, if any.
|
||||
*/
|
||||
static nsresult Adopt(nsINode *aNode, nsNodeInfoManager *aNewNodeInfoManager,
|
||||
JS::Handle<JSObject*> aReparentScope,
|
||||
nsCOMArray<nsINode> &aNodesWithProperties)
|
||||
static void Adopt(nsINode *aNode, nsNodeInfoManager *aNewNodeInfoManager,
|
||||
JS::Handle<JSObject*> aReparentScope,
|
||||
nsCOMArray<nsINode>& aNodesWithProperties,
|
||||
mozilla::ErrorResult& aError)
|
||||
{
|
||||
nsCOMPtr<nsINode> node;
|
||||
nsresult rv = CloneAndAdopt(aNode, false, true, aNewNodeInfoManager,
|
||||
aReparentScope, aNodesWithProperties,
|
||||
nullptr, getter_AddRefs(node));
|
||||
// Just need to store the return value of CloneAndAdopt in a
|
||||
// temporary nsCOMPtr to make sure we release it.
|
||||
nsCOMPtr<nsINode> node = CloneAndAdopt(aNode, false, true, aNewNodeInfoManager,
|
||||
aReparentScope, &aNodesWithProperties,
|
||||
nullptr, aError);
|
||||
|
||||
nsMutationGuard::DidMutate();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,9 +242,12 @@ public:
|
||||
*
|
||||
* @param aNode the node to clone
|
||||
* @param aDeep if true all descendants will be cloned too
|
||||
* @param aResult the clone
|
||||
* @param aError the error, if any.
|
||||
*
|
||||
* @return the clone, or null if an error occurs.
|
||||
*/
|
||||
static nsresult CloneNodeImpl(nsINode *aNode, bool aDeep, nsINode **aResult);
|
||||
static already_AddRefed<nsINode> CloneNodeImpl(nsINode *aNode, bool aDeep,
|
||||
mozilla::ErrorResult& aError);
|
||||
|
||||
/**
|
||||
* Release the UserData for aNode.
|
||||
@@ -286,7 +283,7 @@ private:
|
||||
*
|
||||
* @param aNode Node to adopt/clone.
|
||||
* @param aClone If true the node will be cloned and the cloned node will
|
||||
* be in aResult.
|
||||
* be returned.
|
||||
* @param aDeep If true the function will be called recursively on
|
||||
* descendants of the node
|
||||
* @param aNewNodeInfoManager The nodeinfo manager to use to create new
|
||||
@@ -302,14 +299,18 @@ private:
|
||||
* @param aParent If aClone is true the cloned node will be appended to
|
||||
* aParent's children. May be null. If not null then aNode
|
||||
* must be an nsIContent.
|
||||
* @param aResult If aClone is true then *aResult will contain the cloned
|
||||
* node.
|
||||
* @param aError The error, if any.
|
||||
*
|
||||
* @return If aClone is true then the cloned node will be returned,
|
||||
* unless an error occurred. In error conditions, null
|
||||
* will be returned.
|
||||
*/
|
||||
static nsresult CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
nsNodeInfoManager *aNewNodeInfoManager,
|
||||
JS::Handle<JSObject*> aReparentScope,
|
||||
nsCOMArray<nsINode> &aNodesWithProperties,
|
||||
nsINode *aParent, nsINode **aResult);
|
||||
static already_AddRefed<nsINode>
|
||||
CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
nsNodeInfoManager* aNewNodeInfoManager,
|
||||
JS::Handle<JSObject*> aReparentScope,
|
||||
nsCOMArray<nsINode> *aNodesWithProperties,
|
||||
nsINode *aParent, mozilla::ErrorResult& aError);
|
||||
|
||||
enum class AnimationMutationType
|
||||
{
|
||||
|
||||
@@ -86,7 +86,6 @@
|
||||
#include "mozilla/EventStates.h"
|
||||
#include "mozilla/dom/HTMLObjectElementBinding.h"
|
||||
#include "mozilla/dom/HTMLSharedObjectElement.h"
|
||||
#include "nsChannelClassifier.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
// Thanks so much, Microsoft! :(
|
||||
|
||||
@@ -304,14 +304,6 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Check for a ShadowRoot because link elements are inert in a
|
||||
// ShadowRoot.
|
||||
ShadowRoot* containingShadow = thisContent->GetContainingShadow();
|
||||
if (thisContent->IsHTMLElement(nsGkAtoms::link) &&
|
||||
(aOldShadowRoot || containingShadow)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// XXXheycam ServoStyleSheets do not support <style scoped>.
|
||||
Element* oldScopeElement = nullptr;
|
||||
if (mStyleSheet) {
|
||||
@@ -346,15 +338,16 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
||||
}
|
||||
}
|
||||
|
||||
// When static documents are created, stylesheets are cloned manually.
|
||||
if (mDontLoadStyle || !mUpdatesEnabled ||
|
||||
thisContent->OwnerDoc()->IsStaticDocument()) {
|
||||
nsCOMPtr<nsIDocument> doc = thisContent->IsInShadowTree() ?
|
||||
thisContent->OwnerDoc() : thisContent->GetUncomposedDoc();
|
||||
|
||||
// Loader could be null during unlink, see bug 1425866.
|
||||
if (!doc || !doc->CSSLoader() || !doc->CSSLoader()->GetEnabled()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = thisContent->IsInShadowTree() ?
|
||||
thisContent->OwnerDoc() : thisContent->GetUncomposedDoc();
|
||||
if (!doc || !doc->CSSLoader()->GetEnabled()) {
|
||||
// When static documents are created, stylesheets are cloned manually.
|
||||
if (mDontLoadStyle || !mUpdatesEnabled || doc->IsStaticDocument()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -426,8 +419,7 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
||||
rv = doc->CSSLoader()->
|
||||
LoadInlineStyle(thisContent, text, mLineNumber, title, media,
|
||||
scopeElement, aObserver, &doneLoading, &isAlternate, &isExplicitlyEnabled);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
nsAutoString integrity;
|
||||
thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity, integrity);
|
||||
if (!integrity.IsEmpty()) {
|
||||
|
||||
@@ -0,0 +1,903 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Bug 1453693</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script>
|
||||
|
||||
class TestNode extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
const styles = "<style>:focus{background-color:yellow;}</style>";
|
||||
this.attachShadow({ mode: 'open' });
|
||||
this.shadowRoot.innerHTML =
|
||||
`${styles}<div tabindex='-1'>test node</div> <slot></slot>`;
|
||||
}}
|
||||
|
||||
window.customElements.define('test-node', TestNode);
|
||||
|
||||
var lastFocusTarget;
|
||||
function focusLogger(event) {
|
||||
lastFocusTarget = event.target;
|
||||
console.log(event.target + " under " + event.target.parentNode);
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
function testTabbingThroughShadowDOMWithTabIndexes() {
|
||||
var anchor = document.createElement("a");
|
||||
anchor.onfocus = focusLogger;
|
||||
anchor.href = "#";
|
||||
anchor.textContent = "in light DOM";
|
||||
document.body.appendChild(anchor);
|
||||
|
||||
var host = document.createElement("div");
|
||||
document.body.appendChild(host);
|
||||
|
||||
var sr = host.attachShadow({mode: "open"});
|
||||
var shadowAnchor = anchor.cloneNode(false);
|
||||
shadowAnchor.onfocus = focusLogger;
|
||||
shadowAnchor.textContent = "in shadow DOM";
|
||||
sr.appendChild(shadowAnchor);
|
||||
var shadowInput = document.createElement("input");
|
||||
shadowInput.onfocus = focusLogger;
|
||||
shadowInput.tabIndex = 1;
|
||||
sr.appendChild(shadowInput);
|
||||
|
||||
var shadowDate = document.createElement("input");
|
||||
shadowDate.type = "date";
|
||||
shadowDate.onfocus = focusLogger;
|
||||
shadowDate.tabIndex = 1;
|
||||
sr.appendChild(shadowDate);
|
||||
|
||||
var shadowIframe = document.createElement("iframe");
|
||||
shadowIframe.tabIndex = 1;
|
||||
sr.appendChild(shadowIframe);
|
||||
shadowIframe.contentDocument.body.innerHTML = "<input>";
|
||||
|
||||
var input = document.createElement("input");
|
||||
input.onfocus = focusLogger;
|
||||
input.tabIndex = 1;
|
||||
document.body.appendChild(input);
|
||||
|
||||
var input2 = document.createElement("input");
|
||||
input2.onfocus = focusLogger;
|
||||
document.body.appendChild(input2);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input, "Should have focused input element. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(shadowIframe.contentDocument.activeElement,
|
||||
shadowIframe.contentDocument.documentElement,
|
||||
"Should have focused document element in shadow iframe. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(shadowIframe.contentDocument.activeElement,
|
||||
shadowIframe.contentDocument.body.firstChild,
|
||||
"Should have focused input element in shadow iframe. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (3)");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input2, "Should have focused input[2] element. (3)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(shadowIframe.contentDocument.activeElement,
|
||||
shadowIframe.contentDocument.body.firstChild,
|
||||
"Should have focused input element in shadow iframe. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(shadowIframe.contentDocument.activeElement,
|
||||
shadowIframe.contentDocument.documentElement,
|
||||
"Should have focused document element in shadow iframe. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (4)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input, "Should have focused input element. (4)");
|
||||
|
||||
document.body.innerHTML = null;
|
||||
}
|
||||
|
||||
function testTabbingThroughSimpleShadowDOM() {
|
||||
var anchor = document.createElement("a");
|
||||
anchor.onfocus = focusLogger;
|
||||
anchor.href = "#";
|
||||
anchor.textContent = "in light DOM";
|
||||
document.body.appendChild(anchor);
|
||||
anchor.focus();
|
||||
|
||||
var host = document.createElement("div");
|
||||
document.body.appendChild(host);
|
||||
|
||||
var sr = host.attachShadow({mode: "open"});
|
||||
var shadowAnchor = anchor.cloneNode(false);
|
||||
shadowAnchor.onfocus = focusLogger;
|
||||
shadowAnchor.textContent = "in shadow DOM";
|
||||
sr.appendChild(shadowAnchor);
|
||||
var shadowInput = document.createElement("input");
|
||||
shadowInput.onfocus = focusLogger;
|
||||
sr.appendChild(shadowInput);
|
||||
|
||||
var hiddenShadowButton = document.createElement("button");
|
||||
hiddenShadowButton.setAttribute("style", "display: none;");
|
||||
sr.appendChild(hiddenShadowButton);
|
||||
|
||||
var input = document.createElement("input");
|
||||
input.onfocus = focusLogger;
|
||||
document.body.appendChild(input);
|
||||
|
||||
var input2 = document.createElement("input");
|
||||
input2.onfocus = focusLogger;
|
||||
document.body.appendChild(input2);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM.");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM.");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input, "Should have focused input element.");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input2, "Should have focused input[2] element.");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input, "Should have focused input element. (2)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (2)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (2)");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (2)");
|
||||
|
||||
host.remove();
|
||||
input.remove();
|
||||
input2.remove();
|
||||
}
|
||||
|
||||
function testTabbingThroughNestedShadowDOM() {
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (1)");
|
||||
|
||||
var host = document.createElement("div");
|
||||
host.id = "host";
|
||||
document.body.appendChild(host);
|
||||
|
||||
var sr0 = host.attachShadow({mode: "open"});
|
||||
sr0.innerHTML = "<button id='button'>X</button><br id='br'><div id='h1'></div><div id='h2'></div>";
|
||||
var button = sr0.getElementById("button");
|
||||
button.onfocus = focusLogger;
|
||||
|
||||
var h1 = sr0.getElementById("h1");
|
||||
var sr1 = h1.attachShadow({mode: "open"});
|
||||
sr1.innerHTML = "h1 <input id='h11' placeholder='click me and press tab'><input id='h12' placeholder='and then tab again'>";
|
||||
var input11 = sr1.getElementById("h11");
|
||||
input11.onfocus = focusLogger;
|
||||
var input12 = sr1.getElementById("h12");
|
||||
input12.onfocus = focusLogger;
|
||||
|
||||
var h2 = sr0.getElementById("h2");
|
||||
var sr2 = h2.attachShadow({mode: "open"});
|
||||
sr2.innerHTML = "h2 <input id='h21'><input id='h22'>";
|
||||
var input21 = sr2.getElementById("h21");
|
||||
input21.onfocus = focusLogger;
|
||||
var input22 = sr2.getElementById("h22");
|
||||
input22.onfocus = focusLogger;
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, button, "[nested shadow] Should have focused button element. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input11, "[nested shadow] Should have focused input element. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input12, "[nested shadow] Should have focused input element. (2)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input21, "[nested shadow] Should have focused input element. (3)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input22, "[nested shadow] Should have focused input element. (4)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input21, "[nested shadow] Should have focused input element. (5)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input12, "[nested shadow] Should have focused input element. (6)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input11, "[nested shadow] Should have focused input element. (7)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, button, "[nested shadow] Should have focused button element. (8)");
|
||||
|
||||
// Back to beginning, outside of Shadow DOM.
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (2)");
|
||||
|
||||
host.remove();
|
||||
}
|
||||
|
||||
function testTabbingThroughDisplayContentsHost() {
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (1)");
|
||||
|
||||
var host = document.createElement("div");
|
||||
host.id = "host";
|
||||
host.setAttribute("style", "display: contents; border: 1px solid black;");
|
||||
document.body.appendChild(host);
|
||||
|
||||
var sr0 = host.attachShadow({mode: "open"});
|
||||
sr0.innerHTML = "<input id='shadowInput1'><input id='shadowInput2'>";
|
||||
var shadowInput1 = sr0.getElementById("shadowInput1");
|
||||
shadowInput1.onfocus = focusLogger;
|
||||
var shadowInput2 = sr0.getElementById("shadowInput2");
|
||||
shadowInput2.onfocus = focusLogger;
|
||||
|
||||
var host1 = document.createElement("div");
|
||||
host1.id = "host";
|
||||
host1.tabIndex = 0;
|
||||
host1.setAttribute("style", "display: contents; border: 1px solid black;");
|
||||
document.body.appendChild(host1);
|
||||
|
||||
var sr1 = host1.attachShadow({mode: "open"});
|
||||
sr1.innerHTML = "<input id='shadowInput1'><input id='shadowInput2'>";
|
||||
var shadowInput3 = sr1.getElementById("shadowInput1");
|
||||
shadowInput3.onfocus = focusLogger;
|
||||
var shadowInput4 = sr1.getElementById("shadowInput2");
|
||||
shadowInput4.onfocus = focusLogger;
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowInput1, "Should have focused input element. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowInput2, "Should have focused input element. (2)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowInput3, "Should have focused input element. (3)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowInput4, "Should have focused input element. (4)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowInput3, "Should have focused input element. (5)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowInput2, "Should have focused input element. (6)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowInput1, "Should have focused input element. (7)");
|
||||
|
||||
// Back to beginning, outside of Shadow DOM.
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (2)");
|
||||
|
||||
host.remove();
|
||||
host1.remove();
|
||||
}
|
||||
|
||||
function testTabbingThroughLightDOMShadowDOMLightDOM() {
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
var host = document.createElement("span");
|
||||
host.innerHTML = "\n";
|
||||
host.id = "host";
|
||||
document.body.appendChild(host);
|
||||
|
||||
var sr0 = host.attachShadow({mode: "open"});
|
||||
sr0.innerHTML = document.getElementById("template").innerHTML;
|
||||
var p1 = sr0.getElementById("p1");
|
||||
p1.onfocus = focusLogger;
|
||||
var p2 = sr0.getElementById("p2");
|
||||
p2.onfocus = focusLogger;
|
||||
|
||||
var p = document.createElement("p");
|
||||
p.innerHTML = " <a href='#p'>link 1</a> ";
|
||||
var a = p.firstElementChild;
|
||||
a.onfocus = focusLogger;
|
||||
document.body.appendChild(p);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, p1, "Should have focused p1.");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, p2, "Should have focused p2.");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, a, "Should have focused a.");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, p2, "Should have focused p2.");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, p1, "Should have focused p1.");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
host.remove();
|
||||
p.remove();
|
||||
}
|
||||
|
||||
function testFocusableHost() {
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
var host = document.createElement("div");
|
||||
host.id = "host";
|
||||
host.tabIndex = 0;
|
||||
host.onfocus = focusLogger;
|
||||
document.body.appendChild(host);
|
||||
|
||||
var slotted = document.createElement("div");
|
||||
slotted.tabIndex = 0;
|
||||
slotted.onfocus = focusLogger;
|
||||
host.appendChild(slotted);
|
||||
|
||||
var sr0 = host.attachShadow({mode: "open"});
|
||||
sr0.appendChild(document.createElement("slot"));
|
||||
|
||||
var p = document.createElement("p");
|
||||
p.innerHTML = " <a href='#p'>link 1</a> ";
|
||||
var a = p.firstElementChild;
|
||||
a.onfocus = focusLogger;
|
||||
document.body.appendChild(p);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, host, "Should have focused host.");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, slotted, "Should have focused slotted.");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, a, "Should have focused a.");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, slotted, "Should have focused slotted.");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, host, "Should have focused host.");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
host.remove();
|
||||
p.remove();
|
||||
}
|
||||
|
||||
function testShiftTabbingThroughFocusableHost() {
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
var host = document.createElement("div");
|
||||
host.id = "host";
|
||||
host.tabIndex = 0;
|
||||
host.onfocus = focusLogger;
|
||||
document.body.appendChild(host);
|
||||
|
||||
var sr = host.attachShadow({mode: "open"});
|
||||
var shadowButton = document.createElement("button");
|
||||
shadowButton.innerText = "X";
|
||||
shadowButton.onfocus = focusLogger;
|
||||
sr.appendChild(shadowButton);
|
||||
|
||||
var shadowInput = document.createElement("input");
|
||||
shadowInput.onfocus = focusLogger;
|
||||
sr.appendChild(shadowInput);
|
||||
sr.appendChild(document.createElement("br"));
|
||||
|
||||
var input = document.createElement("input");
|
||||
input.onfocus = focusLogger;
|
||||
document.body.appendChild(input);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, host, "Should have focused host element. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowButton, "Should have focused button element in shadow DOM. (2)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (3)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input, "Should have focused input element. (4)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (5)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, shadowButton, "Should have focused button element in shadow DOM. (6)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
// focus is already on host
|
||||
opener.is(sr.activeElement, null,
|
||||
"Focus should have left button element in shadow DOM. (7)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
host.remove();
|
||||
input.remove();
|
||||
}
|
||||
|
||||
function testTabbingThroughNestedSlot() {
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
|
||||
|
||||
var host0 = document.createElement("div");
|
||||
var sr0 = host0.attachShadow({mode: "open"});
|
||||
sr0.innerHTML = "<slot></slot>";
|
||||
document.body.appendChild(host0);
|
||||
|
||||
// focusable
|
||||
var host00 = document.createElement("div");
|
||||
var sr00 = host00.attachShadow({mode: "open"});
|
||||
var div00 = document.createElement("div");
|
||||
div00.tabIndex = 0;
|
||||
div00.onfocus = focusLogger;
|
||||
sr00.appendChild(div00);
|
||||
host0.appendChild(host00);
|
||||
|
||||
// not focusable
|
||||
var host01 = document.createElement("div");
|
||||
var sr01 = host01.attachShadow({mode: "open"});
|
||||
sr01.innerHTML = "<div></div>";
|
||||
host0.appendChild(host01);
|
||||
|
||||
// focusable
|
||||
var host02 = document.createElement("div");
|
||||
var sr02 = host02.attachShadow({mode: "open"});
|
||||
var div02 = document.createElement("div");
|
||||
div02.tabIndex = 0;
|
||||
div02.onfocus = focusLogger;
|
||||
sr02.appendChild(div02);
|
||||
host0.appendChild(host02);
|
||||
|
||||
var host1 = document.createElement("div");
|
||||
var sr1 = host1.attachShadow({mode: "open"});
|
||||
sr1.innerHTML = "<slot></slot>";
|
||||
document.body.appendChild(host1);
|
||||
|
||||
var host10 = document.createElement("div");
|
||||
var sr10 = host10.attachShadow({mode: "open"});
|
||||
sr10.innerHTML = "<slot></slot>";
|
||||
host1.appendChild(host10);
|
||||
|
||||
var input10 = document.createElement("input");
|
||||
input10.onfocus = focusLogger;
|
||||
host10.appendChild(input10);
|
||||
|
||||
var host11 = document.createElement("div");
|
||||
var sr11 = host11.attachShadow({mode: "open"});
|
||||
sr11.innerHTML = "<slot></slot>";
|
||||
host1.appendChild(host11);
|
||||
|
||||
var input11 = document.createElement("input");
|
||||
input11.onfocus = focusLogger;
|
||||
host11.appendChild(input11);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, div00, "Should have focused div element in shadow DOM. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, div02, "Should have focused div element in shadow DOM. (2)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input10, "Should have focused input element in shadow DOM. (3)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input11, "Should have focused button element in shadow DOM. (4)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input10, "Should have focused input element in shadow DOM. (5)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, div02, "Should have focused input element in shadow DOM. (6)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, div00, "Should have focused input element in shadow DOM. (7)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
host0.remove();
|
||||
host1.remove();
|
||||
}
|
||||
|
||||
function testTabbingThroughSlotInLightDOM() {
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
|
||||
|
||||
var input0 = document.createElement("input");
|
||||
input0.onfocus = focusLogger;
|
||||
document.body.appendChild(input0);
|
||||
|
||||
var slot1 = document.createElement("slot");
|
||||
document.body.appendChild(slot1);
|
||||
|
||||
var input10 = document.createElement("input");
|
||||
input10.onfocus = focusLogger;
|
||||
slot1.appendChild(input10);
|
||||
|
||||
var input11 = document.createElement("input");
|
||||
input11.onfocus = focusLogger;
|
||||
slot1.appendChild(input11);
|
||||
|
||||
var input2 = document.createElement("input");
|
||||
input2.onfocus = focusLogger;
|
||||
document.body.appendChild(input2);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input0, "Should have focused input element. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input10, "Should have focused input element in slot. (2)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input11, "Should have focused input element in slot. (3)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input2, "Should have focused input element. (4)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input11, "Should have focused input element in slot. (5)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input10, "Should have focused input element in slot. (6)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input0, "Should have focused input element. (7)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
input0.remove();
|
||||
slot1.remove();
|
||||
input2.remove();
|
||||
}
|
||||
|
||||
function testTabbingThroughFocusableSlotInLightDOM() {
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
|
||||
|
||||
var slot0 = document.createElement("slot");
|
||||
slot0.tabIndex = 0;
|
||||
slot0.setAttribute("style", "display: inline;");
|
||||
slot0.onfocus = focusLogger;
|
||||
document.body.appendChild(slot0);
|
||||
|
||||
var slot00 = document.createElement("slot");
|
||||
slot00.tabIndex = 0;
|
||||
slot00.setAttribute("style", "display: inline;");
|
||||
slot00.onfocus = focusLogger;
|
||||
slot0.appendChild(slot00);
|
||||
|
||||
var input000 = document.createElement("input");
|
||||
input000.onfocus = focusLogger;
|
||||
slot00.appendChild(input000);
|
||||
|
||||
var input01 = document.createElement("input");
|
||||
input01.onfocus = focusLogger;
|
||||
slot0.appendChild(input01);
|
||||
|
||||
var input1 = document.createElement("input");
|
||||
input1.onfocus = focusLogger;
|
||||
document.body.appendChild(input1);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, slot0, "Should have focused slot element. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, slot00, "Should have focused slot element. (2)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input000, "Should have focused input element in slot. (3)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input01, "Should have focused input element in slot. (4)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input1, "Should have focused input element. (5)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input01, "Should have focused input element in slot. (6)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input000, "Should have focused input element in slot. (7)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, slot00, "Should have focused slot element. (8)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, slot0, "Should have focused slot element. (9)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
slot0.remove();
|
||||
input1.remove();
|
||||
}
|
||||
|
||||
function testTabbingThroughScrollableShadowDOM() {
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
|
||||
|
||||
var host0 = document.createElement("div");
|
||||
host0.setAttribute("style", "height: 50px; overflow: auto;");
|
||||
host0.onfocus = focusLogger;
|
||||
document.body.appendChild(host0);
|
||||
|
||||
var sr0 = host0.attachShadow({mode: "open"});
|
||||
sr0.innerHTML = `
|
||||
<style>
|
||||
div,slot {
|
||||
height: 30px;
|
||||
display: block;
|
||||
overflow: auto;
|
||||
}
|
||||
input {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
|
||||
var input00 = document.createElement("input");
|
||||
input00.setAttribute("style", "background-color: red;");
|
||||
input00.onfocus = focusLogger;
|
||||
sr0.appendChild(input00);
|
||||
|
||||
var container01 = document.createElement("div");
|
||||
container01.onfocus = focusLogger;
|
||||
sr0.appendChild(container01);
|
||||
|
||||
var input010 = document.createElement("input");
|
||||
input010.onfocus = focusLogger;
|
||||
container01.appendChild(input010);
|
||||
|
||||
var input011 = document.createElement("input");
|
||||
input011.onfocus = focusLogger;
|
||||
container01.appendChild(input011);
|
||||
|
||||
var slot02 = document.createElement("slot");
|
||||
slot02.onfocus = focusLogger;
|
||||
sr0.appendChild(slot02);
|
||||
|
||||
var input020 = document.createElement("input");
|
||||
input020.setAttribute("style", "display: block;");
|
||||
input020.onfocus = focusLogger;
|
||||
host0.appendChild(input020);
|
||||
|
||||
var input021 = document.createElement("input");
|
||||
input021.setAttribute("style", "display: block;");
|
||||
input021.onfocus = focusLogger;
|
||||
host0.appendChild(input021);
|
||||
|
||||
var input1 = document.createElement("input");
|
||||
input1.onfocus = focusLogger;
|
||||
document.body.appendChild(input1);
|
||||
|
||||
document.body.offsetLeft;
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, host0, "Should have focused shadow host element. (1)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input00, "Should have focused input element in shadow dom. (2)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, container01, "Should have focused scrollable element in shadow dom. (3)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input010, "Should have focused input element in shadow dom. (4)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input011, "Should have focused input element in shadow dom. (5)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, slot02, "Should have focused slot element in shadow dom. (6)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input020, "Should have focused input element in slot. (7)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input021, "Should have focused input element in slot. (8)");
|
||||
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(lastFocusTarget, input1, "Should have focused input element in light dom. (9)");
|
||||
|
||||
// Backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input021, "Should have focused input element in slot. (10)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input020, "Should have focused input element in slot. (11)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, slot02, "Should have focused slot element in shadow dom. (12)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input011, "Should have focused input element in shadow dom. (13)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input010, "Should have focused input element in shadow dom. (14)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, container01, "Should have focused scrollable element in shadow dom. (15)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(lastFocusTarget, input00, "Should have focused input element in shadow dom. (16)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
// focus is already on host
|
||||
opener.is(sr0.activeElement, null,
|
||||
"Focus should have left input element in shadow DOM. (7)");
|
||||
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild,
|
||||
"body's first child should have focus.");
|
||||
|
||||
host0.remove();
|
||||
input1.remove();
|
||||
}
|
||||
|
||||
function testDeeplyNestedShadowTree() {
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
|
||||
var host1 = document.createElement("test-node");
|
||||
var lastHost = host1;
|
||||
for (var i = 0; i < 20; ++i) {
|
||||
lastHost.appendChild(document.createElement("test-node"));
|
||||
lastHost = lastHost.firstChild;
|
||||
}
|
||||
|
||||
var input = document.createElement("input");
|
||||
document.body.appendChild(host1);
|
||||
document.body.appendChild(input);
|
||||
document.body.offsetLeft;
|
||||
|
||||
// Test shadow tree which doesn't have anything tab-focusable.
|
||||
host1.shadowRoot.querySelector("div").focus();
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(document.activeElement, input, "Should have focused input element.");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
|
||||
|
||||
// Same test but with focusable elements in the tree...
|
||||
var input2 = document.createElement("input");
|
||||
var host2 = host1.firstChild;
|
||||
var host3 = host2.firstChild;
|
||||
host2.insertBefore(input2, host3);
|
||||
var input3 = document.createElement("input");
|
||||
lastHost.appendChild(input3);
|
||||
document.body.offsetLeft;
|
||||
host3.shadowRoot.querySelector("div").focus();
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(document.activeElement, input3, "Should have focused input3 element.");
|
||||
|
||||
// ...and backwards
|
||||
host3.shadowRoot.querySelector("div").focus();
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
is(document.activeElement, input2, "Should have focused input2 element.");
|
||||
|
||||
// Remove elements added to body element.
|
||||
host1.remove();
|
||||
input.remove();
|
||||
|
||||
// Tests expect body.firstChild to have focus.
|
||||
document.body.firstChild.focus();
|
||||
}
|
||||
|
||||
// Bug 1558393
|
||||
function testBackwardsTabbingWithSlotsWithoutFocusableContent() {
|
||||
let first = document.createElement("div");
|
||||
first.tabIndex = 0;
|
||||
let host = document.createElement("div");
|
||||
host.tabIndex = 0;
|
||||
let second = document.createElement("div");
|
||||
second.tabIndex = 0;
|
||||
host.appendChild(document.createTextNode("foo"));
|
||||
host.attachShadow({ mode: "open" }).innerHTML = `<slot></slot>`;
|
||||
|
||||
document.body.appendChild(first);
|
||||
document.body.appendChild(host);
|
||||
document.body.appendChild(second);
|
||||
document.body.offsetLeft;
|
||||
|
||||
first.focus();
|
||||
opener.is(document.activeElement, first, "First light div should have focus");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(document.activeElement, host, "Host should be focused");
|
||||
synthesizeKey("KEY_Tab");
|
||||
opener.is(document.activeElement, second, "Second light div should be focused");
|
||||
|
||||
// Now backwards
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, host, "Focus should return to host");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
opener.is(document.activeElement, first, "Focus should return to first light div");
|
||||
|
||||
second.remove();
|
||||
host.remove();
|
||||
first.remove();
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
|
||||
testTabbingThroughShadowDOMWithTabIndexes();
|
||||
testTabbingThroughSimpleShadowDOM();
|
||||
testTabbingThroughNestedShadowDOM();
|
||||
testTabbingThroughDisplayContentsHost();
|
||||
testTabbingThroughLightDOMShadowDOMLightDOM();
|
||||
testFocusableHost();
|
||||
testShiftTabbingThroughFocusableHost();
|
||||
testTabbingThroughNestedSlot();
|
||||
testTabbingThroughSlotInLightDOM();
|
||||
testTabbingThroughFocusableSlotInLightDOM();
|
||||
testTabbingThroughScrollableShadowDOM();
|
||||
testDeeplyNestedShadowTree();
|
||||
testBackwardsTabbingWithSlotsWithoutFocusableContent();
|
||||
|
||||
opener.didRunTests();
|
||||
window.close();
|
||||
}
|
||||
|
||||
function init() {
|
||||
SimpleTest.waitForFocus(runTest);
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
||||
<template id="template">
|
||||
<div style="overflow: hidden">
|
||||
<p tabindex="0" id="p1">component</p>
|
||||
<p tabindex="0" id="p2">/component</p>
|
||||
</div>
|
||||
</template>
|
||||
</head>
|
||||
<body onload="init()">
|
||||
</body>
|
||||
</html>
|
||||
@@ -129,6 +129,7 @@ support-files =
|
||||
file_bug1263696_frame_pass.html
|
||||
file_bug1263696_frame_fail.html
|
||||
file_bug1274806.html
|
||||
file_bug1453693.html
|
||||
file_general_document.html
|
||||
file_htmlserializer_1.html
|
||||
file_htmlserializer_1_bodyonly.html
|
||||
@@ -609,6 +610,8 @@ skip-if = toolkit == 'android'
|
||||
[test_bug1308069.html]
|
||||
[test_bug1314032.html]
|
||||
[test_bug1375050.html]
|
||||
[test_bug1453693.html]
|
||||
skip-if = os == "mac" # Different tab focus behavior on mac
|
||||
[test_caretPositionFromPoint.html]
|
||||
[test_change_policy.html]
|
||||
[test_classList.html]
|
||||
@@ -648,6 +651,8 @@ skip-if = toolkit == 'android' #bug 904183
|
||||
[test_fileapi.html]
|
||||
[test_fileapi_slice.html]
|
||||
skip-if = (toolkit == 'android') # Android: Bug 775227
|
||||
[test_focus_shadow_dom_root.html]
|
||||
skip-if = os == "mac" # Different tab focus behavior on mac
|
||||
[test_getAttribute_after_createAttribute.html]
|
||||
[test_getElementById.html]
|
||||
[test_getTranslationNodes.html]
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1453693
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1453693</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 1453693 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTests() {
|
||||
win = window.open("file_bug1453693.html", "", "width=300, height=300");
|
||||
}
|
||||
|
||||
function didRunTests() {
|
||||
setTimeout("SimpleTest.finish()");
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="SimpleTest.waitForFocus(runTests);">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1453693">Mozilla Bug 1453693</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<!doctype html>
|
||||
<title>Test for bug 1544826</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<div id="host"><a href="#" id="slotted">This is focusable too</a></div>
|
||||
<script>
|
||||
const host = document.getElementById("host");
|
||||
const shadow = host.attachShadow({ mode: "open" });
|
||||
shadow.innerHTML = `
|
||||
<a id="shadow-1" href="#">This is focusable</a>
|
||||
<slot></slot>
|
||||
<a id="shadow-2" href="#">So is this</a>
|
||||
`;
|
||||
document.documentElement.remove();
|
||||
document.appendChild(host);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(function() {
|
||||
is(document.documentElement, host, "Host is the document element");
|
||||
host.offsetTop;
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(shadow.activeElement.id, "shadow-1", "First link in Shadow DOM is focused");
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(document.activeElement.id, "slotted", "Slotted link is focused");
|
||||
synthesizeKey("KEY_Tab");
|
||||
is(shadow.activeElement.id, "shadow-2", "Second link in Shadow DOM is focused");
|
||||
|
||||
// Now backwards.
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
is(document.activeElement.id, "slotted", "Backwards: Slotted link is focused");
|
||||
synthesizeKey("KEY_Tab", {shiftKey: true});
|
||||
is(shadow.activeElement.id, "shadow-1", "Backwards: First slotted link is focused");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
@@ -2106,16 +2106,19 @@ DictionaryBase::AppendJSONToString(const char16_t* aJSONData,
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
||||
void
|
||||
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg, ErrorResult& aError)
|
||||
{
|
||||
js::AssertSameCompartment(aCx, aObjArg);
|
||||
|
||||
aError.MightThrowJSException();
|
||||
|
||||
// Check if we're anywhere near the stack limit before we reach the
|
||||
// transplanting code, since it has no good way to handle errors. This uses
|
||||
// the untrusted script limit, which is not strictly necessary since no
|
||||
// actual script should run.
|
||||
JS_CHECK_RECURSION_CONSERVATIVE(aCx, return NS_ERROR_FAILURE);
|
||||
// TODO: Make sure to retain 'onerror' if bug 1342439 lands.
|
||||
JS_CHECK_RECURSION_CONSERVATIVE(aCx, aError.StealExceptionFromJSContext(aCx); return);
|
||||
|
||||
JS::Rooted<JSObject*> aObj(aCx, aObjArg);
|
||||
const DOMJSClass* domClass = GetDOMClass(aObj);
|
||||
@@ -2135,12 +2138,12 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
||||
JSCompartment* newCompartment = js::GetObjectCompartment(newParent);
|
||||
if (oldCompartment == newCompartment) {
|
||||
MOZ_ASSERT(oldParent == newParent);
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
nsISupports* native = UnwrapDOMObjectToISupports(aObj);
|
||||
if (!native) {
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
bool isProxy = js::IsProxy(aObj);
|
||||
@@ -2156,12 +2159,14 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
||||
|
||||
JS::Handle<JSObject*> proto = (domClass->mGetProto)(aCx);
|
||||
if (!proto) {
|
||||
return NS_ERROR_FAILURE;
|
||||
aError.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> newobj(aCx, JS_CloneObject(aCx, aObj, proto));
|
||||
if (!newobj) {
|
||||
return NS_ERROR_FAILURE;
|
||||
aError.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> propertyHolder(aCx);
|
||||
@@ -2169,11 +2174,13 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
||||
if (copyFrom) {
|
||||
propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, nullptr);
|
||||
if (!propertyHolder) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
aError.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!JS_CopyPropertiesFrom(aCx, propertyHolder, copyFrom)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
aError.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
propertyHolder = nullptr;
|
||||
@@ -2189,7 +2196,8 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
||||
// if expandos are present then the wrapper will already have been preserved
|
||||
// for this native.
|
||||
if (!xpc::XrayUtils::CloneExpandoChain(aCx, newobj, aObj)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
aError.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
// We've set up |newobj|, so we make it own the native by setting its reserved
|
||||
@@ -2244,9 +2252,6 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
||||
if (htmlobject) {
|
||||
htmlobject->SetupProtoChain(aCx, aObj);
|
||||
}
|
||||
|
||||
// Now we can just return the wrapper
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
GlobalObject::GlobalObject(JSContext* aCx, JSObject* aObject)
|
||||
|
||||
@@ -2758,8 +2758,8 @@ const nsAString& NonNullHelper(const binding_detail::FakeString& aArg)
|
||||
|
||||
// Reparent the wrapper of aObj to whatever its native now thinks its
|
||||
// parent should be.
|
||||
nsresult
|
||||
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObj);
|
||||
void
|
||||
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObj, ErrorResult& aError);
|
||||
|
||||
/**
|
||||
* Used to implement the Symbol.hasInstance property of an interface object.
|
||||
|
||||
Vendored
+1
-1
@@ -1770,7 +1770,7 @@ Manager::~Manager()
|
||||
|
||||
// Don't spin the event loop in the destructor waiting for the thread to
|
||||
// shutdown. Defer this to the main thread, instead.
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewRunnableMethod(ioThread, &nsIThread::Shutdown)));
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewRunnableMethod(ioThread, &nsIThread::AsyncShutdown)));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -3758,7 +3758,11 @@ CanvasRenderingContext2D::SetFontInternal(const nsAString& aFont,
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
|
||||
if (!presShell) {
|
||||
aError.Throw(NS_ERROR_FAILURE);
|
||||
// Do not throw here. We may be in a situation where we're loading in an iframe
|
||||
// that is sandboxed, and/or initially hidden with display:none, in which case
|
||||
// we don't want to throw an error but silently fail.
|
||||
// If we don't do this, JS trying to set context.font to something will abort,
|
||||
// breaking e.g. third party serviced graphs.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,37 +6,34 @@
|
||||
|
||||
#include "ImageBitmapColorUtils.h"
|
||||
|
||||
#include "js/LegacyIntTypes.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/*
|
||||
* Utility function form libyuv source files.
|
||||
*/
|
||||
static __inline int32 clamp0(int32 v) {
|
||||
static __inline int32_t clamp0(int32_t v) {
|
||||
return ((-(v) >> 31) & (v));
|
||||
}
|
||||
|
||||
static __inline int32 clamp255(int32 v) {
|
||||
static __inline int32_t clamp255(int32_t v) {
|
||||
return (((255 - (v)) >> 31) | (v)) & 255;
|
||||
}
|
||||
|
||||
static __inline uint32 Clamp(int32 val) {
|
||||
static __inline uint32_t Clamp(int32_t val) {
|
||||
int v = clamp0(val);
|
||||
return (uint32)(clamp255(v));
|
||||
return (uint32_t)(clamp255(v));
|
||||
}
|
||||
|
||||
#define YG 74 /* (int8)(1.164 * 64 + 0.5) */
|
||||
#define YG 74 /* (int8_t)(1.164 * 64 + 0.5) */
|
||||
|
||||
#define UB 127 /* min(63,(int8)(2.018 * 64)) */
|
||||
#define UG -25 /* (int8)(-0.391 * 64 - 0.5) */
|
||||
#define UB 127 /* min(63,(int8_t)(2.018 * 64)) */
|
||||
#define UG -25 /* (int8_t)(-0.391 * 64 - 0.5) */
|
||||
#define UR 0
|
||||
|
||||
#define VB 0
|
||||
#define VG -52 /* (int8)(-0.813 * 64 - 0.5) */
|
||||
#define VR 102 /* (int8)(1.596 * 64 + 0.5) */
|
||||
#define VG -52 /* (int8_t)(-0.813 * 64 - 0.5) */
|
||||
#define VR 102 /* (int8_t)(1.596 * 64 + 0.5) */
|
||||
|
||||
// Bias
|
||||
#define BB UB * 128 + VB * 128
|
||||
@@ -44,28 +41,28 @@ static __inline uint32 Clamp(int32 val) {
|
||||
#define BR UR * 128 + VR * 128
|
||||
|
||||
static __inline void
|
||||
YuvPixel(uint8 y, uint8 u, uint8 v, uint8* b, uint8* g, uint8* r)
|
||||
YuvPixel(uint8_t y, uint8_t u, uint8_t v, uint8_t* b, uint8_t* g, uint8_t* r)
|
||||
{
|
||||
int32 y1 = ((int32)(y) - 16) * YG;
|
||||
*b = Clamp((int32)((u * UB + v * VB) - (BB) + y1) >> 6);
|
||||
*g = Clamp((int32)((u * UG + v * VG) - (BG) + y1) >> 6);
|
||||
*r = Clamp((int32)((u * UR + v * VR) - (BR) + y1) >> 6);
|
||||
int32_t y1 = ((int32_t)(y) - 16) * YG;
|
||||
*b = Clamp((int32_t)((u * UB + v * VB) - (BB) + y1) >> 6);
|
||||
*g = Clamp((int32_t)((u * UG + v * VG) - (BG) + y1) >> 6);
|
||||
*r = Clamp((int32_t)((u * UR + v * VR) - (BR) + y1) >> 6);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
RGBToY(uint8 r, uint8 g, uint8 b)
|
||||
RGBToY(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
return (66 * r + 129 * g + 25 * b + 0x1080) >> 8;
|
||||
}
|
||||
|
||||
static __inline int
|
||||
RGBToU(uint8 r, uint8 g, uint8 b)
|
||||
RGBToU(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
return (112 * b - 74 * g - 38 * r + 0x8080) >> 8;
|
||||
}
|
||||
|
||||
static __inline int
|
||||
RGBToV(uint8 r, uint8 g, uint8 b)
|
||||
RGBToV(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
return (112 * r - 94 * g - 18 * b + 0x8080) >> 8;
|
||||
}
|
||||
|
||||
@@ -303,13 +303,13 @@ WebGLContext::FromDomElem(const char* funcName, TexImageTarget target, uint32_t
|
||||
uint32_t height, uint32_t depth, const dom::Element& elem,
|
||||
ErrorResult* const out_error)
|
||||
{
|
||||
if (elem.IsHTMLElement(nsGkAtoms::canvas)) {
|
||||
const dom::HTMLCanvasElement* canvas = static_cast<const dom::HTMLCanvasElement*>(&elem);
|
||||
if (canvas->IsWriteOnly()) {
|
||||
out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (elem.IsHTMLElement(nsGkAtoms::canvas)) {
|
||||
const dom::HTMLCanvasElement* canvas = static_cast<const dom::HTMLCanvasElement*>(&elem);
|
||||
if (canvas->IsWriteOnly()) {
|
||||
out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t flags = nsLayoutUtils::SFE_WANT_IMAGE_SURFACE |
|
||||
nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR;
|
||||
@@ -1032,9 +1032,27 @@ ValidateCompressedTexImageRestrictions(const char* funcName, WebGLContext* webgl
|
||||
}
|
||||
|
||||
static bool
|
||||
ValidateTargetForFormat(const char* funcName, WebGLContext* webgl, TexImageTarget target,
|
||||
const webgl::FormatInfo* format)
|
||||
{
|
||||
ValidateFormatAndSize(const char* funcName,
|
||||
WebGLContext* webgl,
|
||||
TexImageTarget target,
|
||||
const webgl::FormatInfo* format,
|
||||
const uint32_t width,
|
||||
const uint32_t height,
|
||||
const uint32_t depth) {
|
||||
// Check if texture size will likely be rejected by the driver and give a more
|
||||
// meaningful error message.
|
||||
auto baseImageSize = CheckedInt<uint64_t>(format->estimatedBytesPerPixel) *
|
||||
width * height * depth;
|
||||
if (target == LOCAL_GL_TEXTURE_CUBE_MAP) {
|
||||
baseImageSize *= 6;
|
||||
}
|
||||
|
||||
if (!baseImageSize.isValid() ||
|
||||
baseImageSize.value() > (uint64_t)gfxPrefs::WebGLMaxSizePerTextureMB * (1024 * 1024)) {
|
||||
webgl->ErrorOutOfMemory("Texture size too large; base image MB > webgl.max-size-per-texture-mb");
|
||||
return false;
|
||||
}
|
||||
|
||||
// GLES 3.0.4 p127:
|
||||
// "Textures with a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL are
|
||||
// supported by texture image specification commands only if `target` is TEXTURE_2D,
|
||||
@@ -1137,7 +1155,8 @@ WebGLTexture::TexStorage(const char* funcName, TexTarget target, GLsizei levels,
|
||||
}
|
||||
auto dstFormat = dstUsage->format;
|
||||
|
||||
if (!ValidateTargetForFormat(funcName, mContext, testTarget, dstFormat))
|
||||
if (!ValidateFormatAndSize(funcName, mContext, testTarget, dstFormat,
|
||||
width, height, depth))
|
||||
return;
|
||||
|
||||
if (dstFormat->compression) {
|
||||
@@ -1264,7 +1283,8 @@ WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level,
|
||||
// Check that source and dest info are compatible
|
||||
auto dstFormat = dstUsage->format;
|
||||
|
||||
if (!ValidateTargetForFormat(funcName, mContext, target, dstFormat))
|
||||
if (!ValidateFormatAndSize(funcName, mContext, target, dstFormat,
|
||||
blob->mWidth, blob->mHeight, blob->mDepth))
|
||||
return;
|
||||
|
||||
if (!mContext->IsWebGL2() && dstFormat->d) {
|
||||
@@ -1484,7 +1504,8 @@ WebGLTexture::CompressedTexImage(const char* funcName, TexImageTarget target, GL
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ValidateTargetForFormat(funcName, mContext, target, format))
|
||||
if (!ValidateFormatAndSize(funcName, mContext, target, format,
|
||||
blob->mWidth, blob->mHeight, blob->mDepth))
|
||||
return;
|
||||
|
||||
////////////////////////////////////
|
||||
@@ -2143,7 +2164,8 @@ WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internal
|
||||
return;
|
||||
|
||||
const auto& dstFormat = dstUsage->format;
|
||||
if (!ValidateTargetForFormat(funcName, mContext, target, dstFormat))
|
||||
if (!ValidateFormatAndSize(funcName, mContext, target, dstFormat,
|
||||
width, height, depth))
|
||||
return;
|
||||
|
||||
if (!mContext->IsWebGL2() && dstFormat->d) {
|
||||
|
||||
@@ -41,7 +41,7 @@ EXPORTS.mozilla.dom += [
|
||||
]
|
||||
|
||||
# Canvas 2D and common sources
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'CanvasImageCache.cpp',
|
||||
'CanvasRenderingContext2D.cpp',
|
||||
'CanvasRenderingContextHelper.cpp',
|
||||
@@ -59,7 +59,7 @@ SOURCES += [
|
||||
]
|
||||
|
||||
# WebGL Sources
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'TexUnpackBlob.cpp',
|
||||
'WebGL1Context.cpp',
|
||||
'WebGL1ContextUniforms.cpp',
|
||||
|
||||
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
@@ -1741,6 +1741,10 @@ private:
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
}
|
||||
|
||||
if (pubKey->keyType != rsaKey) {
|
||||
return NS_ERROR_DOM_DATA_ERR;
|
||||
}
|
||||
|
||||
// Extract relevant information from the public key
|
||||
mModulusLength = 8 * pubKey->u.rsa.modulus.len;
|
||||
if (!mPublicExponent.Assign(&pubKey->u.rsa.publicExponent)) {
|
||||
@@ -1874,6 +1878,10 @@ private:
|
||||
}
|
||||
|
||||
if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
|
||||
if (pubKey->keyType != ecKey) {
|
||||
return NS_ERROR_DOM_DATA_ERR;
|
||||
}
|
||||
|
||||
if (!CheckEncodedECParameters(&pubKey->u.ec.DEREncodedParams)) {
|
||||
return NS_ERROR_DOM_OPERATION_ERR;
|
||||
}
|
||||
|
||||
@@ -116,10 +116,9 @@ ContentEventHandler::InitBasic()
|
||||
|
||||
// If text frame which has overflowing selection underline is dirty,
|
||||
// we need to flush the pending reflow here.
|
||||
mPresShell->FlushPendingNotifications(Flush_Layout);
|
||||
|
||||
// Flushing notifications can cause mPresShell to be destroyed (bug 577963).
|
||||
NS_ENSURE_TRUE(!mPresShell->IsDestroying(), NS_ERROR_FAILURE);
|
||||
if (nsIDocument* doc = mPresShell->GetDocument()) {
|
||||
doc->FlushPendingNotifications(Flush_Layout);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -170,11 +169,11 @@ ContentEventHandler::InitRootContent(Selection* aNormalSelection)
|
||||
}
|
||||
|
||||
// See bug 537041 comment 5, the range could have removed node.
|
||||
if (NS_WARN_IF(startNode->GetUncomposedDoc() != mPresShell->GetDocument())) {
|
||||
if (NS_WARN_IF(startNode->GetComposedDoc() != mPresShell->GetDocument())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_ASSERTION(startNode->GetUncomposedDoc() == endNode->GetUncomposedDoc(),
|
||||
NS_ASSERTION(startNode->GetComposedDoc() == endNode->GetComposedDoc(),
|
||||
"firstNormalSelectionRange crosses the document boundary");
|
||||
|
||||
mRootContent = startNode->GetSelectionRootContent(mPresShell);
|
||||
@@ -2882,31 +2881,6 @@ ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aRange)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ContentEventHandler::GetStartFrameAndOffset(const nsRange* aRange,
|
||||
nsIFrame*& aFrame,
|
||||
int32_t& aOffsetInFrame)
|
||||
{
|
||||
MOZ_ASSERT(aRange);
|
||||
|
||||
aFrame = nullptr;
|
||||
aOffsetInFrame = -1;
|
||||
|
||||
nsINode* node = aRange->GetStartParent();
|
||||
if (NS_WARN_IF(!node) ||
|
||||
NS_WARN_IF(!node->IsNodeOfType(nsINode::eCONTENT))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsIContent* content = static_cast<nsIContent*>(node);
|
||||
RefPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
|
||||
aFrame = fs->GetFrameForNodeOffset(content, aRange->StartOffset(),
|
||||
fs->GetHint(), &aOffsetInFrame);
|
||||
if (NS_WARN_IF(!aFrame)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ContentEventHandler::ConvertToRootRelativeOffset(nsIFrame* aFrame,
|
||||
nsRect& aRect)
|
||||
|
||||
@@ -287,10 +287,6 @@ protected:
|
||||
// If the aRange isn't in text node but next to a text node, this method
|
||||
// modifies it in the text node. Otherwise, not modified.
|
||||
nsresult AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aCollapsedRange);
|
||||
// Find the first frame for the range and get the start offset in it.
|
||||
nsresult GetStartFrameAndOffset(const nsRange* aRange,
|
||||
nsIFrame*& aFrame,
|
||||
int32_t& aOffsetInFrame);
|
||||
// Convert the frame relative offset to be relative to the root frame of the
|
||||
// root presContext (but still measured in appUnits of aFrame's presContext).
|
||||
nsresult ConvertToRootRelativeOffset(nsIFrame* aFrame,
|
||||
|
||||
+27
-38
@@ -148,6 +148,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Event)
|
||||
tmp->mEvent->mCurrentTarget = nullptr;
|
||||
tmp->mEvent->mOriginalTarget = nullptr;
|
||||
tmp->mEvent->mRelatedTarget = nullptr;
|
||||
tmp->mEvent->mOriginalRelatedTarget = nullptr;
|
||||
switch (tmp->mEvent->mClass) {
|
||||
case eDragEventClass: {
|
||||
WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
|
||||
@@ -176,6 +177,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mCurrentTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mRelatedTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalRelatedTarget)
|
||||
switch (tmp->mEvent->mClass) {
|
||||
case eDragEventClass: {
|
||||
WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
|
||||
@@ -194,6 +196,11 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (WidgetMouseEvent* mouseEvent = tmp->mEvent->AsMouseEvent()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mClickTarget");
|
||||
cb.NoteXPCOMChild(mouseEvent->mClickTarget);
|
||||
}
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExplicitOriginalTarget)
|
||||
@@ -550,6 +557,26 @@ Event::SetEventType(const nsAString& aEventTypeArg)
|
||||
mEvent->SetDefaultComposedInNativeAnonymousContent();
|
||||
}
|
||||
|
||||
already_AddRefed<EventTarget>
|
||||
Event::EnsureWebAccessibleRelatedTarget(EventTarget* aRelatedTarget)
|
||||
{
|
||||
nsCOMPtr<EventTarget> relatedTarget = aRelatedTarget;
|
||||
if (relatedTarget) {
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(relatedTarget);
|
||||
|
||||
if (content && content->ChromeOnlyAccess() &&
|
||||
!nsContentUtils::CanAccessNativeAnon()) {
|
||||
content = content->FindFirstNonChromeOnlyAccessContent();
|
||||
relatedTarget = do_QueryInterface(content);
|
||||
}
|
||||
|
||||
if (relatedTarget) {
|
||||
relatedTarget = relatedTarget->GetTargetForDOMEvent();
|
||||
}
|
||||
}
|
||||
return relatedTarget.forget();
|
||||
}
|
||||
|
||||
void
|
||||
Event::InitEvent(const nsAString& aEventTypeArg,
|
||||
bool aCanBubbleArg,
|
||||
@@ -1222,44 +1249,6 @@ Event::SetOwner(EventTarget* aOwner)
|
||||
#endif
|
||||
}
|
||||
|
||||
// static
|
||||
nsIContent*
|
||||
Event::GetShadowRelatedTarget(nsIContent* aCurrentTarget,
|
||||
nsIContent* aRelatedTarget)
|
||||
{
|
||||
if (!aCurrentTarget || !aRelatedTarget) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Walk up the ancestor node trees of the related target until
|
||||
// we encounter the node tree of the current target in order
|
||||
// to find the adjusted related target. Walking up the tree may
|
||||
// not find a common ancestor node tree if the related target is in
|
||||
// an ancestor tree, but in that case it does not need to be adjusted.
|
||||
ShadowRoot* currentTargetShadow = aCurrentTarget->GetContainingShadow();
|
||||
if (!currentTargetShadow) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent* relatedTarget = aCurrentTarget;
|
||||
while (relatedTarget) {
|
||||
ShadowRoot* ancestorShadow = relatedTarget->GetContainingShadow();
|
||||
if (currentTargetShadow == ancestorShadow) {
|
||||
return relatedTarget;
|
||||
}
|
||||
|
||||
// Didn't find the ancestor tree, thus related target does not have to
|
||||
// adjusted.
|
||||
if (!ancestorShadow) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
relatedTarget = ancestorShadow->GetHost();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Event::GetCancelBubble(bool* aCancelBubble)
|
||||
{
|
||||
|
||||
+3
-8
@@ -251,14 +251,6 @@ public:
|
||||
return mIsMainThreadEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given current target, returns the related target adjusted with
|
||||
* shadow DOM retargeting rules. Returns nullptr if related target
|
||||
* is not adjusted.
|
||||
*/
|
||||
static nsIContent* GetShadowRelatedTarget(nsIContent* aCurrentTarget,
|
||||
nsIContent* aRelatedTarget);
|
||||
|
||||
void MarkUninitialized()
|
||||
{
|
||||
mEvent->mMessage = eVoidEvent;
|
||||
@@ -295,6 +287,9 @@ protected:
|
||||
mEvent->SetComposed(aComposed);
|
||||
}
|
||||
|
||||
already_AddRefed<EventTarget>
|
||||
EnsureWebAccessibleRelatedTarget(EventTarget* aRelatedTarget);
|
||||
|
||||
mozilla::WidgetEvent* mEvent;
|
||||
RefPtr<nsPresContext> mPresContext;
|
||||
nsCOMPtr<EventTarget> mExplicitOriginalTarget;
|
||||
|
||||
@@ -204,6 +204,16 @@ public:
|
||||
mNewTarget = aNewTarget;
|
||||
}
|
||||
|
||||
EventTarget* GetRetargetedRelatedTarget()
|
||||
{
|
||||
return mRetargetedRelatedTarget;
|
||||
}
|
||||
|
||||
void SetRetargetedRelatedTarget(EventTarget* aTarget)
|
||||
{
|
||||
mRetargetedRelatedTarget = aTarget;
|
||||
}
|
||||
|
||||
void SetForceContentDispatch(bool aForce)
|
||||
{
|
||||
mFlags.mForceContentDispatch = aForce;
|
||||
@@ -354,6 +364,7 @@ public:
|
||||
|
||||
private:
|
||||
nsCOMPtr<EventTarget> mTarget;
|
||||
nsCOMPtr<EventTarget> mRetargetedRelatedTarget;
|
||||
|
||||
class EventTargetChainFlags
|
||||
{
|
||||
@@ -422,6 +433,7 @@ EventTargetChainItem::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
||||
SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent);
|
||||
SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle);
|
||||
SetRootOfClosedTree(aVisitor.mRootOfClosedTree);
|
||||
SetRetargetedRelatedTarget(aVisitor.mRetargetedRelatedTarget);
|
||||
mItemFlags = aVisitor.mItemFlags;
|
||||
mItemData = aVisitor.mItemData;
|
||||
}
|
||||
@@ -454,6 +466,7 @@ EventTargetChainItem::HandleEventTargetChain(
|
||||
{
|
||||
// Save the target so that it can be restored later.
|
||||
nsCOMPtr<EventTarget> firstTarget = aVisitor.mEvent->mTarget;
|
||||
nsCOMPtr<EventTarget> firstRelatedTarget = aVisitor.mEvent->mRelatedTarget;
|
||||
uint32_t chainLength = aChain.Length();
|
||||
uint32_t firstCanHandleEventTargetIdx =
|
||||
EventTargetChainItem::GetFirstCanHandleEventTargetIdx(aChain);
|
||||
@@ -483,6 +496,30 @@ EventTargetChainItem::HandleEventTargetChain(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dispatching-events
|
||||
// Step 14.2
|
||||
// "Set event's relatedTarget to tuple's relatedTarget."
|
||||
// Note, the initial retargeting was done already when creating
|
||||
// event target chain, so we need to do this only after calling
|
||||
// HandleEvent, not before, like in the specification.
|
||||
if (item.GetRetargetedRelatedTarget()) {
|
||||
bool found = false;
|
||||
for (uint32_t j = i; j > 0; --j) {
|
||||
uint32_t childIndex = j - 1;
|
||||
EventTarget* relatedTarget =
|
||||
aChain[childIndex].GetRetargetedRelatedTarget();
|
||||
if (relatedTarget) {
|
||||
found = true;
|
||||
aVisitor.mEvent->mRelatedTarget = relatedTarget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
aVisitor.mEvent->mRelatedTarget =
|
||||
aVisitor.mEvent->mOriginalRelatedTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Target
|
||||
@@ -511,6 +548,14 @@ EventTargetChainItem::HandleEventTargetChain(
|
||||
aVisitor.mEvent->mTarget = newTarget;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dispatching-events
|
||||
// Step 15.2
|
||||
// "Set event's relatedTarget to tuple's relatedTarget."
|
||||
EventTarget* relatedTarget = item.GetRetargetedRelatedTarget();
|
||||
if (relatedTarget) {
|
||||
aVisitor.mEvent->mRelatedTarget = relatedTarget;
|
||||
}
|
||||
|
||||
if (aVisitor.mEvent->mFlags.mBubbles || newTarget) {
|
||||
if ((!aVisitor.mEvent->mFlags.mNoContentDispatch ||
|
||||
item.ForceContentDispatch()) &&
|
||||
@@ -542,6 +587,7 @@ EventTargetChainItem::HandleEventTargetChain(
|
||||
// Retarget for system event group (which does the default handling too).
|
||||
// Setting back the target which was used also for default event group.
|
||||
aVisitor.mEvent->mTarget = firstTarget;
|
||||
aVisitor.mEvent->mRelatedTarget = aVisitor.mEvent->mOriginalRelatedTarget;
|
||||
aVisitor.mEvent->mFlags.mInSystemGroup = true;
|
||||
HandleEventTargetChain(aChain,
|
||||
aVisitor,
|
||||
@@ -599,6 +645,7 @@ MayRetargetToChromeIfCanNotHandleEvent(
|
||||
EventTargetChainItem::DestroyLast(aChain, aTargetEtci);
|
||||
}
|
||||
if (aPreVisitor.mAutomaticChromeDispatch && aContent) {
|
||||
aPreVisitor.mRelatedTargetRetargetedInCurrentScope = false;
|
||||
// Event target couldn't handle the event. Try to propagate to chrome.
|
||||
EventTargetChainItem* chromeTargetEtci =
|
||||
EventTargetChainItemForChromeTarget(aChain, aContent, aChildEtci);
|
||||
@@ -762,9 +809,10 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
||||
aEvent->mOriginalTarget = aEvent->mTarget;
|
||||
}
|
||||
|
||||
aEvent->mOriginalRelatedTarget = aEvent->mRelatedTarget;
|
||||
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->mOriginalTarget);
|
||||
bool isInAnon = (content && (content->IsInAnonymousSubtree() ||
|
||||
content->IsInShadowTree()));
|
||||
bool isInAnon = (content && content->IsInAnonymousSubtree());
|
||||
|
||||
aEvent->mFlags.mIsBeingDispatched = true;
|
||||
|
||||
@@ -772,7 +820,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
||||
// GetEventTargetParent for the original target.
|
||||
nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore;
|
||||
EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status,
|
||||
isInAnon);
|
||||
isInAnon, aEvent->mTarget);
|
||||
targetEtci->GetEventTargetParent(preVisitor);
|
||||
|
||||
if (!preVisitor.mCanHandle) {
|
||||
@@ -810,14 +858,21 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
||||
if (preVisitor.mEventTargetAtParent) {
|
||||
// Need to set the target of the event
|
||||
// so that also the next retargeting works.
|
||||
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
|
||||
preVisitor.mEvent->mTarget = preVisitor.mEventTargetAtParent;
|
||||
parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent);
|
||||
}
|
||||
|
||||
if (preVisitor.mRetargetedRelatedTarget) {
|
||||
preVisitor.mEvent->mRelatedTarget = preVisitor.mRetargetedRelatedTarget;
|
||||
}
|
||||
|
||||
parentEtci->GetEventTargetParent(preVisitor);
|
||||
if (preVisitor.mCanHandle) {
|
||||
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
|
||||
topEtci = parentEtci;
|
||||
} else {
|
||||
bool ignoreBecauseOfShadowDOM = preVisitor.mIgnoreBecauseOfShadowDOM;
|
||||
nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget);
|
||||
parentEtci = MayRetargetToChromeIfCanNotHandleEvent(chain,
|
||||
preVisitor,
|
||||
@@ -825,9 +880,14 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
||||
topEtci,
|
||||
disabledTarget);
|
||||
if (parentEtci && preVisitor.mCanHandle) {
|
||||
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
|
||||
EventTargetChainItem* item =
|
||||
EventTargetChainItem::GetFirstCanHandleEventTarget(chain);
|
||||
item->SetNewTarget(parentTarget);
|
||||
if (!ignoreBecauseOfShadowDOM) {
|
||||
// If we ignored the target because of Shadow DOM retargeting, we
|
||||
// shouldn't treat the target to be in the event path at all.
|
||||
item->SetNewTarget(parentTarget);
|
||||
}
|
||||
topEtci = parentEtci;
|
||||
continue;
|
||||
}
|
||||
@@ -871,6 +931,19 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
||||
aEvent->mFlags.mIsBeingDispatched = false;
|
||||
aEvent->mFlags.mDispatchedAtLeastOnce = true;
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-event-dispatch
|
||||
// Step 18
|
||||
// "If target's root is a shadow root, then set event's target attribute and
|
||||
// event's relatedTarget to null."
|
||||
nsCOMPtr<nsIContent> finalTarget = do_QueryInterface(aEvent->mTarget);
|
||||
// TODO: replace with IsShadowRoot() check once bug 1427511 lands.
|
||||
if (finalTarget && ShadowRoot::FromNode(finalTarget->SubtreeRoot())) {
|
||||
aEvent->mTarget = nullptr;
|
||||
aEvent->mOriginalTarget = nullptr;
|
||||
aEvent->mRelatedTarget = nullptr;
|
||||
aEvent->mOriginalRelatedTarget = nullptr;
|
||||
}
|
||||
|
||||
if (!externalDOMEvent && preVisitor.mDOMEvent) {
|
||||
// An dom::Event was created while dispatching the event.
|
||||
// Duplicate private data if someone holds a pointer to it.
|
||||
|
||||
@@ -113,7 +113,8 @@ public:
|
||||
WidgetEvent* aEvent,
|
||||
nsIDOMEvent* aDOMEvent,
|
||||
nsEventStatus aEventStatus,
|
||||
bool aIsInAnon)
|
||||
bool aIsInAnon,
|
||||
dom::EventTarget* aTargetInKnownToBeHandledScope)
|
||||
: EventChainVisitor(aPresContext, aEvent, aDOMEvent, aEventStatus)
|
||||
, mCanHandle(true)
|
||||
, mAutomaticChromeDispatch(true)
|
||||
@@ -126,8 +127,12 @@ public:
|
||||
, mRootOfClosedTree(false)
|
||||
, mParentIsSlotInClosedTree(false)
|
||||
, mParentIsChromeHandler(false)
|
||||
, mRelatedTargetRetargetedInCurrentScope(false)
|
||||
, mIgnoreBecauseOfShadowDOM(false)
|
||||
, mParentTarget(nullptr)
|
||||
, mEventTargetAtParent(nullptr)
|
||||
, mRetargetedRelatedTarget(nullptr)
|
||||
, mTargetInKnownToBeHandledScope(aTargetInKnownToBeHandledScope)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -144,8 +149,13 @@ public:
|
||||
mRootOfClosedTree = false;
|
||||
mParentIsSlotInClosedTree = false;
|
||||
mParentIsChromeHandler = false;
|
||||
// Note, we don't clear mRelatedTargetRetargetedInCurrentScope explicitly,
|
||||
// since it is used during event path creation to indicate whether
|
||||
// relatedTarget may need to be retargeted.
|
||||
mIgnoreBecauseOfShadowDOM = false;
|
||||
mParentTarget = nullptr;
|
||||
mEventTargetAtParent = nullptr;
|
||||
mRetargetedRelatedTarget = nullptr;
|
||||
}
|
||||
|
||||
dom::EventTarget* GetParentTarget()
|
||||
@@ -161,6 +171,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void IgnoreCurrentTargetBecauseOfShadowDOMRetargeting()
|
||||
{
|
||||
mCanHandle = false;
|
||||
mIgnoreBecauseOfShadowDOM = true;
|
||||
SetParentTarget(nullptr, false);
|
||||
mEventTargetAtParent = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Member that must be set in GetEventTargetParent by event targets. If set to
|
||||
* false, indicates that this event target will not be handling the event and
|
||||
@@ -229,6 +247,19 @@ public:
|
||||
*/
|
||||
bool mParentIsChromeHandler;
|
||||
|
||||
/**
|
||||
* True if event's related target has been already retargeted in the
|
||||
* current 'scope'. This should be set to false initially and whenever
|
||||
* event path creation crosses shadow boundary.
|
||||
*/
|
||||
bool mRelatedTargetRetargetedInCurrentScope;
|
||||
|
||||
/**
|
||||
* True if Shadow DOM relatedTarget retargeting causes the current item
|
||||
* to not show up in the event path.
|
||||
*/
|
||||
bool mIgnoreBecauseOfShadowDOM;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Parent item in the event target chain.
|
||||
@@ -241,6 +272,19 @@ public:
|
||||
* which should be used when the event is handled at mParentTarget.
|
||||
*/
|
||||
dom::EventTarget* mEventTargetAtParent;
|
||||
|
||||
/**
|
||||
* If the related target of the event needs to be retargeted, set this
|
||||
* to a new EventTarget.
|
||||
*/
|
||||
dom::EventTarget* mRetargetedRelatedTarget;
|
||||
|
||||
/**
|
||||
* Set to the value of mEvent->mTarget of the previous scope in case of
|
||||
* Shadow DOM or such, and if there is no anonymous content this just points
|
||||
* to the initial target.
|
||||
*/
|
||||
dom::EventTarget* mTargetInKnownToBeHandledScope;
|
||||
};
|
||||
|
||||
class EventChainPostVisitor : public mozilla::EventChainVisitor
|
||||
|
||||
+155
-105
@@ -432,11 +432,8 @@ NS_IMPL_CYCLE_COLLECTION(EventStateManager,
|
||||
mGestureDownContent,
|
||||
mGestureDownFrameOwner,
|
||||
mLastLeftMouseDownContent,
|
||||
mLastLeftMouseDownContentParent,
|
||||
mLastMiddleMouseDownContent,
|
||||
mLastMiddleMouseDownContentParent,
|
||||
mLastRightMouseDownContent,
|
||||
mLastRightMouseDownContentParent,
|
||||
mActiveContent,
|
||||
mHoverContent,
|
||||
mURLTargetContent,
|
||||
@@ -3171,14 +3168,11 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
|
||||
case eMouseUp:
|
||||
{
|
||||
ClearGlobalActiveContent(this);
|
||||
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
|
||||
if (mouseEvent && mouseEvent->IsReal()) {
|
||||
if (!mCurrentTarget) {
|
||||
GetEventTarget();
|
||||
}
|
||||
WidgetMouseEvent* mouseUpEvent = aEvent->AsMouseEvent();
|
||||
if (mouseUpEvent && EventCausesClickEvents(*mouseUpEvent)) {
|
||||
// Make sure to dispatch the click even if there is no frame for
|
||||
// the current target element. This is required for Web compatibility.
|
||||
ret = CheckForAndDispatchClick(mouseEvent, aStatus);
|
||||
ret = PostHandleMouseUp(mouseUpEvent, aStatus);
|
||||
}
|
||||
|
||||
nsIPresShell *shell = presContext->GetPresShell();
|
||||
@@ -4573,16 +4567,13 @@ EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
|
||||
nsEventStatus* aStatus)
|
||||
{
|
||||
nsCOMPtr<nsIContent> mouseContent;
|
||||
nsIContent* mouseContentParent = nullptr;
|
||||
if (mCurrentTarget) {
|
||||
mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(mouseContent));
|
||||
}
|
||||
if (mouseContent) {
|
||||
if (mouseContent->IsNodeOfType(nsINode::eTEXT)) {
|
||||
mouseContent = mouseContent->GetParent();
|
||||
}
|
||||
if (mouseContent && mouseContent->IsRootOfNativeAnonymousSubtree()) {
|
||||
mouseContentParent = mouseContent->GetParent();
|
||||
if (mouseContent && mouseContent->IsNodeOfType(nsINode::eTEXT)) {
|
||||
nsINode* parent = mouseContent->GetFlattenedTreeParentNode();
|
||||
if (parent && parent->IsContent()) {
|
||||
mouseContent = parent->AsContent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4590,54 +4581,51 @@ EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
|
||||
case WidgetMouseEvent::eLeftButton:
|
||||
if (aEvent->mMessage == eMouseDown) {
|
||||
mLastLeftMouseDownContent = mouseContent;
|
||||
mLastLeftMouseDownContentParent = mouseContentParent;
|
||||
} else if (aEvent->mMessage == eMouseUp) {
|
||||
if (mLastLeftMouseDownContent == mouseContent ||
|
||||
mLastLeftMouseDownContentParent == mouseContent ||
|
||||
mLastLeftMouseDownContent == mouseContentParent) {
|
||||
aEvent->mClickTarget =
|
||||
nsContentUtils::GetCommonAncestorUnderInteractiveContent(
|
||||
mouseContent, mLastLeftMouseDownContent);
|
||||
if (aEvent->mClickTarget) {
|
||||
aEvent->mClickCount = mLClickCount;
|
||||
mLClickCount = 0;
|
||||
} else {
|
||||
aEvent->mClickCount = 0;
|
||||
}
|
||||
mLastLeftMouseDownContent = nullptr;
|
||||
mLastLeftMouseDownContentParent = nullptr;
|
||||
}
|
||||
break;
|
||||
|
||||
case WidgetMouseEvent::eMiddleButton:
|
||||
if (aEvent->mMessage == eMouseDown) {
|
||||
mLastMiddleMouseDownContent = mouseContent;
|
||||
mLastMiddleMouseDownContentParent = mouseContentParent;
|
||||
} else if (aEvent->mMessage == eMouseUp) {
|
||||
if (mLastMiddleMouseDownContent == mouseContent ||
|
||||
mLastMiddleMouseDownContentParent == mouseContent ||
|
||||
mLastMiddleMouseDownContent == mouseContentParent) {
|
||||
aEvent->mClickTarget =
|
||||
nsContentUtils::GetCommonAncestorUnderInteractiveContent(
|
||||
mouseContent, mLastMiddleMouseDownContent);
|
||||
if (aEvent->mClickTarget) {
|
||||
aEvent->mClickCount = mMClickCount;
|
||||
mMClickCount = 0;
|
||||
} else {
|
||||
aEvent->mClickCount = 0;
|
||||
}
|
||||
mLastMiddleMouseDownContent = nullptr;
|
||||
mLastMiddleMouseDownContentParent = nullptr;
|
||||
}
|
||||
break;
|
||||
|
||||
case WidgetMouseEvent::eRightButton:
|
||||
if (aEvent->mMessage == eMouseDown) {
|
||||
mLastRightMouseDownContent = mouseContent;
|
||||
mLastRightMouseDownContentParent = mouseContentParent;
|
||||
} else if (aEvent->mMessage == eMouseUp) {
|
||||
if (mLastRightMouseDownContent == mouseContent ||
|
||||
mLastRightMouseDownContentParent == mouseContent ||
|
||||
mLastRightMouseDownContent == mouseContentParent) {
|
||||
aEvent->mClickTarget =
|
||||
nsContentUtils::GetCommonAncestorUnderInteractiveContent(
|
||||
mouseContent, mLastRightMouseDownContent);
|
||||
if (aEvent->mClickTarget) {
|
||||
aEvent->mClickCount = mRClickCount;
|
||||
mRClickCount = 0;
|
||||
} else {
|
||||
aEvent->mClickCount = 0;
|
||||
}
|
||||
mLastRightMouseDownContent = nullptr;
|
||||
mLastRightMouseDownContentParent = nullptr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -4645,89 +4633,151 @@ EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
|
||||
nsEventStatus* aStatus,
|
||||
EventMessage aMessage,
|
||||
nsIPresShell* aPresShell,
|
||||
nsIContent* aMouseTarget,
|
||||
nsWeakFrame aCurrentTarget,
|
||||
bool aNoContentDispatch)
|
||||
// static
|
||||
bool
|
||||
EventStateManager::EventCausesClickEvents(const WidgetMouseEvent& aMouseEvent)
|
||||
{
|
||||
WidgetMouseEvent event(aEvent->IsTrusted(), aMessage,
|
||||
aEvent->mWidget, WidgetMouseEvent::eReal);
|
||||
|
||||
event.mRefPoint = aEvent->mRefPoint;
|
||||
event.mClickCount = aEvent->mClickCount;
|
||||
event.mModifiers = aEvent->mModifiers;
|
||||
event.buttons = aEvent->buttons;
|
||||
event.mTime = aEvent->mTime;
|
||||
event.mTimeStamp = aEvent->mTimeStamp;
|
||||
event.mFlags.mNoContentDispatch = aNoContentDispatch;
|
||||
event.button = aEvent->button;
|
||||
event.inputSource = aEvent->inputSource;
|
||||
|
||||
return aPresShell->HandleEventWithTarget(&event, aCurrentTarget,
|
||||
aMouseTarget, aStatus);
|
||||
if (NS_WARN_IF(aMouseEvent.mMessage != eMouseUp)) {
|
||||
return false;
|
||||
}
|
||||
// If the mouseup event is synthesized event, we don't need to dispatch
|
||||
// click events.
|
||||
if (!aMouseEvent.IsReal()) {
|
||||
return false;
|
||||
}
|
||||
// If mouse is still over same element, clickcount will be > 1.
|
||||
// If it has moved it will be zero, so no click.
|
||||
if (!aMouseEvent.mClickCount || !aMouseEvent.mClickTarget) {
|
||||
return false;
|
||||
}
|
||||
// Check that the window isn't disabled before firing a click
|
||||
// (see bug 366544).
|
||||
return !(aMouseEvent.mWidget && !aMouseEvent.mWidget->IsEnabled());
|
||||
}
|
||||
|
||||
nsresult
|
||||
EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
|
||||
nsEventStatus* aStatus)
|
||||
EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aMouseUpEvent,
|
||||
nsEventStatus* aStatus,
|
||||
EventMessage aMessage,
|
||||
nsIPresShell* aPresShell,
|
||||
nsIContent* aMouseUpContent,
|
||||
nsWeakFrame aCurrentTarget,
|
||||
bool aNoContentDispatch)
|
||||
{
|
||||
nsresult ret = NS_OK;
|
||||
MOZ_ASSERT(aMouseUpEvent);
|
||||
MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
|
||||
MOZ_ASSERT(aMouseUpContent || aCurrentTarget);
|
||||
|
||||
//If mouse is still over same element, clickcount will be > 1.
|
||||
//If it has moved it will be zero, so no click.
|
||||
if (aEvent->mClickCount) {
|
||||
//Check that the window isn't disabled before firing a click
|
||||
//(see bug 366544).
|
||||
if (aEvent->mWidget && !aEvent->mWidget->IsEnabled()) {
|
||||
WidgetMouseEvent event(aMouseUpEvent->IsTrusted(), aMessage,
|
||||
aMouseUpEvent->mWidget, WidgetMouseEvent::eReal);
|
||||
event.mRefPoint = aMouseUpEvent->mRefPoint;
|
||||
event.mClickCount = aMouseUpEvent->mClickCount;
|
||||
event.mModifiers = aMouseUpEvent->mModifiers;
|
||||
event.buttons = aMouseUpEvent->buttons;
|
||||
event.mTime = aMouseUpEvent->mTime;
|
||||
event.mTimeStamp = aMouseUpEvent->mTimeStamp;
|
||||
event.mFlags.mNoContentDispatch = aNoContentDispatch;
|
||||
event.button = aMouseUpEvent->button;
|
||||
event.inputSource = aMouseUpEvent->inputSource;
|
||||
|
||||
if (!aMouseUpContent->IsInComposedDoc()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Use local event status for each click event dispatching since it'll be
|
||||
// cleared by EventStateManager::PreHandleEvent(). Therefore, dispatching
|
||||
// an event means that previous event status will be ignored.
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
nsresult rv = aPresShell->HandleEventWithTarget(&event, aCurrentTarget,
|
||||
aMouseUpContent, &status);
|
||||
// If current status is nsEventStatus_eConsumeNoDefault, we don't need to
|
||||
// overwrite it.
|
||||
if (*aStatus == nsEventStatus_eConsumeNoDefault) {
|
||||
return rv;
|
||||
}
|
||||
// If new status is nsEventStatus_eConsumeNoDefault or
|
||||
// nsEventStatus_eConsumeDoDefault, use it.
|
||||
if (status == nsEventStatus_eConsumeNoDefault ||
|
||||
status == nsEventStatus_eConsumeDoDefault) {
|
||||
*aStatus = status;
|
||||
return rv;
|
||||
}
|
||||
// Otherwise, keep the original status.
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EventStateManager::PostHandleMouseUp(WidgetMouseEvent* aMouseUpEvent,
|
||||
nsEventStatus* aStatus)
|
||||
{
|
||||
MOZ_ASSERT(aMouseUpEvent);
|
||||
MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
|
||||
MOZ_ASSERT(aStatus);
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
|
||||
if (!presShell) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> clickTarget = do_QueryInterface(aMouseUpEvent->mClickTarget);
|
||||
NS_ENSURE_STATE(clickTarget);
|
||||
|
||||
// Fire click events if the event target is still available.
|
||||
nsresult rv = DispatchClickEvents(presShell, aMouseUpEvent, aStatus,
|
||||
clickTarget);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EventStateManager::DispatchClickEvents(nsIPresShell* aPresShell,
|
||||
WidgetMouseEvent* aMouseUpEvent,
|
||||
nsEventStatus* aStatus,
|
||||
nsIContent* aClickTarget)
|
||||
{
|
||||
MOZ_ASSERT(aPresShell);
|
||||
MOZ_ASSERT(aMouseUpEvent);
|
||||
MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
|
||||
MOZ_ASSERT(aStatus);
|
||||
MOZ_ASSERT(aClickTarget);
|
||||
|
||||
bool notDispatchToContents =
|
||||
(aMouseUpEvent->button == WidgetMouseEvent::eMiddleButton ||
|
||||
aMouseUpEvent->button == WidgetMouseEvent::eRightButton);
|
||||
|
||||
bool fireAuxClick = notDispatchToContents;
|
||||
|
||||
nsWeakFrame currentTarget = aClickTarget->GetPrimaryFrame();
|
||||
nsresult ret =
|
||||
InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseClick,
|
||||
aPresShell, aClickTarget, currentTarget,
|
||||
notDispatchToContents);
|
||||
if (NS_WARN_IF(NS_FAILED(ret))) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Fire double click event if click count is 2.
|
||||
if (aMouseUpEvent->mClickCount == 2 &&
|
||||
aClickTarget && aClickTarget->IsInComposedDoc()) {
|
||||
ret = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseDoubleClick,
|
||||
aPresShell, aClickTarget, currentTarget,
|
||||
notDispatchToContents);
|
||||
if (NS_WARN_IF(NS_FAILED(ret))) {
|
||||
return ret;
|
||||
}
|
||||
//fire click
|
||||
bool notDispatchToContents =
|
||||
(aEvent->button == WidgetMouseEvent::eMiddleButton ||
|
||||
aEvent->button == WidgetMouseEvent::eRightButton);
|
||||
|
||||
bool fireAuxClick = notDispatchToContents;
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
|
||||
if (presShell) {
|
||||
nsCOMPtr<nsIContent> mouseContent = GetEventTargetContent(aEvent);
|
||||
// Click events apply to *elements* not nodes. At this point the target
|
||||
// content may have been reset to some non-element content, and so we need
|
||||
// to walk up the closest ancestor element, just like we do in
|
||||
// nsPresShell::HandlePositionedEvent.
|
||||
while (mouseContent && !mouseContent->IsElement()) {
|
||||
mouseContent = mouseContent->GetParent();
|
||||
}
|
||||
|
||||
if (!mouseContent && !mCurrentTarget) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// HandleEvent clears out mCurrentTarget which we might need again
|
||||
nsWeakFrame currentTarget = mCurrentTarget;
|
||||
ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseClick,
|
||||
presShell, mouseContent, currentTarget,
|
||||
notDispatchToContents);
|
||||
|
||||
if (NS_SUCCEEDED(ret) && aEvent->mClickCount == 2 &&
|
||||
mouseContent && mouseContent->IsInComposedDoc()) {
|
||||
//fire double click
|
||||
ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseDoubleClick,
|
||||
presShell, mouseContent, currentTarget,
|
||||
notDispatchToContents);
|
||||
}
|
||||
if (NS_SUCCEEDED(ret) && mouseContent && fireAuxClick &&
|
||||
mouseContent->IsInComposedDoc()) {
|
||||
ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseAuxClick,
|
||||
presShell, mouseContent, currentTarget,
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fire auxclick even if necessary.
|
||||
if (fireAuxClick &&
|
||||
aClickTarget && aClickTarget->IsInComposedDoc()) {
|
||||
ret = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseAuxClick,
|
||||
aPresShell, aClickTarget, currentTarget,
|
||||
false);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(ret), "Failed to dispatch eMouseAuxClick");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -414,16 +414,79 @@ protected:
|
||||
*/
|
||||
void UpdateDragDataTransfer(WidgetDragEvent* dragEvent);
|
||||
|
||||
static nsresult InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
|
||||
/**
|
||||
* InitAndDispatchClickEvent() dispatches a click event.
|
||||
*
|
||||
* @param aMouseUpEvent eMouseUp event which causes the click event.
|
||||
* EventCausesClickEvents() must return true
|
||||
* if this event is set to it.
|
||||
* @param aStatus Returns the result of click event.
|
||||
* If the status indicates consumed, the
|
||||
* value won't be overwritten with
|
||||
* nsEventStatus_eIgnore.
|
||||
* @param aMessage Should be eMouseClick, eMouseDoubleClick or
|
||||
* eMouseAuxClick.
|
||||
* @param aPresShell The PresShell.
|
||||
* @param aMouseUpContent The event target of aMouseUpEvent.
|
||||
* @param aCurrentTarget Current target of the caller.
|
||||
* @param aNoContentDispatch true if the event shouldn't be exposed to
|
||||
* web contents (although will be fired on
|
||||
* document and window).
|
||||
* @param aOverrideClickTarget Preferred click event target. If this is
|
||||
* not nullptr, aMouseUpContent and
|
||||
* aCurrentTarget are ignored.
|
||||
*/
|
||||
static nsresult InitAndDispatchClickEvent(WidgetMouseEvent* aMouseUpEvent,
|
||||
nsEventStatus* aStatus,
|
||||
EventMessage aMessage,
|
||||
nsIPresShell* aPresShell,
|
||||
nsIContent* aMouseTarget,
|
||||
nsIContent* aMouseUpContent,
|
||||
nsWeakFrame aCurrentTarget,
|
||||
bool aNoContentDispatch);
|
||||
nsresult SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus);
|
||||
nsresult CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
|
||||
nsEventStatus* aStatus);
|
||||
|
||||
/**
|
||||
* EventCausesClickEvents() returns true when aMouseEvent is an eMouseUp
|
||||
* event and it should cause eMouseClick, eMouseDoubleClick and/or
|
||||
* eMouseAuxClick events. Note that this method assumes that
|
||||
* aMouseEvent.mClickCount has already been initialized with SetClickCount().
|
||||
*/
|
||||
static bool EventCausesClickEvents(const WidgetMouseEvent& aMouseEvent);
|
||||
|
||||
/**
|
||||
* PostHandleMouseUp() handles default actions of eMouseUp event.
|
||||
*
|
||||
* @param aMouseUpEvent eMouseUp event which causes the click event.
|
||||
* EventCausesClickEvents() must return true
|
||||
* if this event is set to it.
|
||||
* @param aStatus Returns the result of event status.
|
||||
* If one of dispatching event is consumed or
|
||||
* this does something as default action,
|
||||
* returns nsEventStatus_eConsumeNoDefault.
|
||||
*/
|
||||
nsresult PostHandleMouseUp(WidgetMouseEvent* aMouseUpEvent,
|
||||
nsEventStatus* aStatus);
|
||||
|
||||
/**
|
||||
* DispatchClickEvents() dispatches eMouseClick, eMouseDoubleClick and
|
||||
* eMouseAuxClick events for aMouseUpEvent. aMouseUpEvent should cause
|
||||
* click event.
|
||||
*
|
||||
* @param aPresShell The PresShell.
|
||||
* @param aMouseUpEvent eMouseUp event which causes the click event.
|
||||
* EventCausesClickEvents() must return true
|
||||
* if this event is set to it.
|
||||
* @param aStatus Returns the result of event status.
|
||||
* If one of dispatching click event is
|
||||
* consumed, returns
|
||||
* nsEventStatus_eConsumeNoDefault.
|
||||
* @param aMouseUpContent The event target of aMouseUpEvent.
|
||||
*/
|
||||
nsresult DispatchClickEvents(nsIPresShell* aPresShell,
|
||||
WidgetMouseEvent* aMouseUpEvent,
|
||||
nsEventStatus* aStatus,
|
||||
nsIContent* aMouseUpContent);
|
||||
|
||||
void EnsureDocument(nsPresContext* aPresContext);
|
||||
void FlushPendingEvents(nsPresContext* aPresContext);
|
||||
|
||||
@@ -951,11 +1014,8 @@ private:
|
||||
uint16_t mGestureDownButtons;
|
||||
|
||||
nsCOMPtr<nsIContent> mLastLeftMouseDownContent;
|
||||
nsCOMPtr<nsIContent> mLastLeftMouseDownContentParent;
|
||||
nsCOMPtr<nsIContent> mLastMiddleMouseDownContent;
|
||||
nsCOMPtr<nsIContent> mLastMiddleMouseDownContentParent;
|
||||
nsCOMPtr<nsIContent> mLastRightMouseDownContent;
|
||||
nsCOMPtr<nsIContent> mLastRightMouseDownContentParent;
|
||||
|
||||
nsCOMPtr<nsIContent> mActiveContent;
|
||||
nsCOMPtr<nsIContent> mHoverContent;
|
||||
|
||||
@@ -30,14 +30,14 @@ NS_IMETHODIMP
|
||||
FocusEvent::GetRelatedTarget(nsIDOMEventTarget** aRelatedTarget)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aRelatedTarget);
|
||||
NS_IF_ADDREF(*aRelatedTarget = GetRelatedTarget());
|
||||
*aRelatedTarget = GetRelatedTarget().take();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
EventTarget*
|
||||
already_AddRefed<EventTarget>
|
||||
FocusEvent::GetRelatedTarget()
|
||||
{
|
||||
return mEvent->AsFocusEvent()->mRelatedTarget;
|
||||
return EnsureWebAccessibleRelatedTarget(mEvent->AsFocusEvent()->mRelatedTarget);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
nsPresContext* aPresContext,
|
||||
InternalFocusEvent* aEvent);
|
||||
|
||||
EventTarget* GetRelatedTarget();
|
||||
already_AddRefed<EventTarget> GetRelatedTarget();
|
||||
|
||||
static already_AddRefed<FocusEvent> Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aType,
|
||||
|
||||
@@ -300,27 +300,7 @@ MouseEvent::GetRelatedTarget()
|
||||
break;
|
||||
}
|
||||
|
||||
if (relatedTarget) {
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(relatedTarget);
|
||||
nsCOMPtr<nsIContent> currentTarget =
|
||||
do_QueryInterface(mEvent->mCurrentTarget);
|
||||
|
||||
nsIContent* shadowRelatedTarget = GetShadowRelatedTarget(currentTarget, content);
|
||||
if (shadowRelatedTarget) {
|
||||
relatedTarget = shadowRelatedTarget;
|
||||
}
|
||||
|
||||
if (content && content->ChromeOnlyAccess() &&
|
||||
!nsContentUtils::CanAccessNativeAnon()) {
|
||||
relatedTarget = do_QueryInterface(content->FindFirstNonChromeOnlyAccessContent());
|
||||
}
|
||||
|
||||
if (relatedTarget) {
|
||||
relatedTarget = relatedTarget->GetTargetForDOMEvent();
|
||||
}
|
||||
return relatedTarget.forget();
|
||||
}
|
||||
return nullptr;
|
||||
return EnsureWebAccessibleRelatedTarget(relatedTarget);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+17
-22
@@ -228,26 +228,20 @@ UIEvent::GetWhich(uint32_t* aWhich)
|
||||
already_AddRefed<nsINode>
|
||||
UIEvent::GetRangeParent()
|
||||
{
|
||||
nsIFrame* targetFrame = nullptr;
|
||||
|
||||
if (mPresContext) {
|
||||
targetFrame = mPresContext->EventStateManager()->GetEventTarget();
|
||||
if (NS_WARN_IF(!mPresContext)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (targetFrame) {
|
||||
nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent,
|
||||
targetFrame);
|
||||
nsCOMPtr<nsIContent> parent = targetFrame->GetContentOffsetsFromPoint(pt).content;
|
||||
if (parent) {
|
||||
if (parent->ChromeOnlyAccess() &&
|
||||
!nsContentUtils::CanAccessNativeAnon()) {
|
||||
return nullptr;
|
||||
}
|
||||
return parent.forget();
|
||||
}
|
||||
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
|
||||
if (NS_WARN_IF(!presShell)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
nsCOMPtr<nsIContent> container;
|
||||
nsLayoutUtils::GetContainerAndOffsetAtEvent(presShell, mEvent,
|
||||
getter_AddRefs(container),
|
||||
nullptr);
|
||||
return container.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -273,18 +267,19 @@ UIEvent::GetRangeOffset(int32_t* aRangeOffset)
|
||||
int32_t
|
||||
UIEvent::RangeOffset() const
|
||||
{
|
||||
if (!mPresContext) {
|
||||
if (NS_WARN_IF(!mPresContext)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget();
|
||||
if (!targetFrame) {
|
||||
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
|
||||
if (NS_WARN_IF(!presShell)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent,
|
||||
targetFrame);
|
||||
return targetFrame->GetContentOffsetsFromPoint(pt).offset;
|
||||
int32_t offset = 0;
|
||||
nsLayoutUtils::GetContainerAndOffsetAtEvent(presShell, mEvent,
|
||||
nullptr, &offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
nsIntPoint
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<p><input type="text" id="input"></p>
|
||||
<p><input type="text" id="input_readonly" readonly></p>
|
||||
<p><textarea id="textarea"></textarea></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
@@ -293,10 +296,163 @@ function testSynthesizedKeyLocation()
|
||||
window.removeEventListener("keyup", handler, true);
|
||||
}
|
||||
|
||||
// We're using TextEventDispatcher to decide if we should keypress event
|
||||
// on content in the default event group. So, we can test if keypress
|
||||
// event is NOT fired unexpectedly with synthesizeKey().
|
||||
function testEnterKeyPressEvent()
|
||||
{
|
||||
let keydownFired, keypressFired, beforeinputFired;
|
||||
function onEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "keydown":
|
||||
keydownFired = true;
|
||||
return;
|
||||
case "keypress":
|
||||
keypressFired = true;
|
||||
return;
|
||||
case "beforeinput":
|
||||
beforeinputFired = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (let targetId of ["input", "textarea", "input_readonly"]) {
|
||||
let target = document.getElementById(targetId);
|
||||
|
||||
function reset() {
|
||||
keydownFired = keypressFired = beforeinputFired = false;
|
||||
target.value = "";
|
||||
}
|
||||
|
||||
target.addEventListener("keydown", onEvent);
|
||||
target.addEventListener("keypress", onEvent);
|
||||
target.addEventListener("beforeinput", onEvent);
|
||||
|
||||
const kDescription = "<" + targetId.replace("_", " ") + ">: ";
|
||||
let isEditable = kDescription.includes("readonly");
|
||||
let isTextarea = kDescription.includes("textarea");
|
||||
|
||||
target.focus();
|
||||
|
||||
reset();
|
||||
synthesizeKey("KEY_Enter", {});
|
||||
is(keydownFired, true,
|
||||
kDescription + "keydown event should be fired when Enter key is pressed");
|
||||
is(keypressFired, true,
|
||||
kDescription + "keypress event should be fired when Enter key is pressed");
|
||||
if (isEditable) {
|
||||
todo_is(beforeinputFired, true,
|
||||
kDescription + "beforeinput event should be fired when Enter key is pressed");
|
||||
} else {
|
||||
is(beforeinputFired, false,
|
||||
kDescription + "beforeinput event shouldn't be fired when Enter key is pressed");
|
||||
}
|
||||
if (isTextarea) {
|
||||
is(target.value, "\n",
|
||||
kDescription + "Enter key should cause inputting a line break in <textarea>");
|
||||
} else {
|
||||
is(target.value, "",
|
||||
kDescription + "Enter key should not cause inputting a line break");
|
||||
}
|
||||
|
||||
reset();
|
||||
synthesizeKey("KEY_Enter", {shiftKey: true});
|
||||
is(keydownFired, true,
|
||||
kDescription + "keydown event should be fired when Shift + Enter key is pressed");
|
||||
is(keypressFired, true,
|
||||
kDescription + "keypress event should be fired when Shift + Enter key is pressed");
|
||||
if (isEditable) {
|
||||
todo_is(beforeinputFired, true,
|
||||
kDescription + "beforeinput event should be fired when Shift + Enter key is pressed");
|
||||
} else {
|
||||
is(beforeinputFired, false,
|
||||
kDescription + "beforeinput event shouldn't be fired when Shift + Enter key is pressed");
|
||||
}
|
||||
if (isTextarea) {
|
||||
is(target.value, "\n",
|
||||
kDescription + "Shift + Enter key should cause inputting a line break in <textarea>");
|
||||
} else {
|
||||
is(target.value, "",
|
||||
kDescription + "Shift + Enter key should not cause inputting a line break");
|
||||
}
|
||||
|
||||
reset();
|
||||
synthesizeKey("KEY_Enter", {ctrlKey: true});
|
||||
is(keydownFired, true,
|
||||
kDescription + "keydown event should be fired when Ctrl + Enter key is pressed");
|
||||
is(keypressFired, true,
|
||||
kDescription + "keypress event should be fired when Ctrl + Enter key is pressed");
|
||||
is(beforeinputFired, false,
|
||||
kDescription + "beforeinput event shouldn't be fired when Ctrl + Enter key is pressed");
|
||||
is(target.value, "",
|
||||
kDescription + "Ctrl + Enter key should not cause inputting a line break");
|
||||
|
||||
reset();
|
||||
synthesizeKey("KEY_Enter", {altKey: true});
|
||||
is(keydownFired, true,
|
||||
kDescription + "keydown event should be fired when Alt + Enter key is pressed");
|
||||
is(keypressFired, !kStrictKeyPressEvents,
|
||||
kDescription + "keypress event shouldn't be fired when Alt + Enter key is pressed in strict keypress dispatching mode");
|
||||
is(beforeinputFired, false,
|
||||
kDescription + "beforeinput event shouldn't be fired when Alt + Enter key is pressed");
|
||||
is(target.value, "",
|
||||
kDescription + "Alt + Enter key should not cause inputting a line break");
|
||||
|
||||
reset();
|
||||
synthesizeKey("KEY_Enter", {metaKey: true});
|
||||
is(keydownFired, true,
|
||||
kDescription + "keydown event should be fired when Meta + Enter key is pressed");
|
||||
is(keypressFired, !kStrictKeyPressEvents,
|
||||
kDescription + "keypress event shouldn't be fired when Meta + Enter key is pressed in strict keypress dispatching mode");
|
||||
is(beforeinputFired, false,
|
||||
kDescription + "beforeinput event shouldn't be fired when Meta + Enter key is pressed");
|
||||
is(target.value, "",
|
||||
kDescription + "Meta + Enter key should not cause inputting a line break");
|
||||
|
||||
reset();
|
||||
synthesizeKey("KEY_Enter", {shiftKey: true, ctrlKey: true});
|
||||
is(keydownFired, true,
|
||||
kDescription + "keydown event should be fired when Ctrl + Shift + Enter key is pressed");
|
||||
is(keypressFired, !kStrictKeyPressEvents,
|
||||
kDescription + "keypress event shouldn't be fired when Ctrl + Shift + Enter key is pressed in strict keypress dispatching mode");
|
||||
is(beforeinputFired, false,
|
||||
kDescription + "beforeinput event shouldn't be fired when Ctrl + Shift + Enter key is pressed");
|
||||
is(target.value, "",
|
||||
kDescription + "Ctrl + Shift + Enter key should not cause inputting a line break");
|
||||
|
||||
reset();
|
||||
synthesizeKey("KEY_Enter", {shiftKey: true, altKey: true});
|
||||
is(keydownFired, true,
|
||||
kDescription + "keydown event should be fired when Alt + Shift + Enter key is pressed");
|
||||
is(keypressFired, !kStrictKeyPressEvents,
|
||||
kDescription + "keypress event shouldn't be fired when Alt + Shift + Enter key is pressed in strict keypress dispatching mode");
|
||||
is(beforeinputFired, false,
|
||||
kDescription + "beforeinput event shouldn't be fired when Alt + Shift + Enter key is pressed");
|
||||
is(target.value, "",
|
||||
kDescription + "Alt + Shift + Enter key should not cause inputting a line break");
|
||||
|
||||
reset();
|
||||
synthesizeKey("KEY_Enter", {shiftKey: true, metaKey: true});
|
||||
is(keydownFired, true,
|
||||
kDescription + "keydown event should be fired when Meta + Shift + Enter key is pressed");
|
||||
is(keypressFired, !kStrictKeyPressEvents,
|
||||
kDescription + "keypress event shouldn't be fired when Meta + Shift + Enter key is pressed in strict keypress dispatching mode");
|
||||
is(beforeinputFired, false,
|
||||
kDescription + "beforeinput event shouldn't be fired when Meta + Shift + Enter key is pressed");
|
||||
is(target.value, "",
|
||||
kDescription + "Meta + Shift + Enter key should not cause inputting a line break");
|
||||
|
||||
target.removeEventListener("keydown", onEvent);
|
||||
target.removeEventListener("keypress", onEvent);
|
||||
target.removeEventListener("beforeinput", onEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function runTests()
|
||||
{
|
||||
testInitializingUntrustedEvent();
|
||||
testSynthesizedKeyLocation();
|
||||
testEnterKeyPressEvent();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
||||
@@ -536,7 +536,8 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest,
|
||||
nsAutoCString statusText;
|
||||
httpChannel->GetResponseStatusText(statusText);
|
||||
|
||||
response = new InternalResponse(responseStatus, statusText);
|
||||
response = new InternalResponse(responseStatus, statusText,
|
||||
mRequest->GetCredentialsMode());
|
||||
|
||||
RefPtr<FillResponseHeaders> visitor = new FillResponseHeaders(response);
|
||||
rv = httpChannel->VisitResponseHeaders(visitor);
|
||||
@@ -555,7 +556,8 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest,
|
||||
}
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
} else {
|
||||
response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
|
||||
response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"),
|
||||
mRequest->GetCredentialsMode());
|
||||
|
||||
ErrorResult result;
|
||||
nsAutoCString contentType;
|
||||
|
||||
Loaded 100 of 4640 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user