1 | /* |
2 | * Copyright (C) 2018-2019 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "Quirks.h" |
28 | |
29 | #include "Document.h" |
30 | #include "DocumentLoader.h" |
31 | #include "HTMLMetaElement.h" |
32 | #include "HTMLObjectElement.h" |
33 | #include "LayoutUnit.h" |
34 | #include "Settings.h" |
35 | |
36 | namespace WebCore { |
37 | |
38 | static inline OptionSet<AutoplayQuirk> allowedAutoplayQuirks(Document& document) |
39 | { |
40 | auto* loader = document.loader(); |
41 | if (!loader) |
42 | return { }; |
43 | |
44 | return loader->allowedAutoplayQuirks(); |
45 | } |
46 | |
47 | Quirks::Quirks(Document& document) |
48 | : m_document(makeWeakPtr(document)) |
49 | { |
50 | } |
51 | |
52 | Quirks::~Quirks() = default; |
53 | |
54 | inline bool Quirks::needsQuirks() const |
55 | { |
56 | return m_document && m_document->settings().needsSiteSpecificQuirks(); |
57 | } |
58 | |
59 | bool Quirks::shouldIgnoreShrinkToFitContent() const |
60 | { |
61 | #if PLATFORM(IOS_FAMILY) |
62 | if (!needsQuirks()) |
63 | return false; |
64 | |
65 | auto host = m_document->topDocument().url().host(); |
66 | if (equalLettersIgnoringASCIICase(host, "outlook.live.com" )) |
67 | return true; |
68 | #endif |
69 | return false; |
70 | } |
71 | |
72 | Optional<LayoutUnit> Quirks::overriddenViewLayoutWidth(LayoutUnit currentViewLayoutWidth) const |
73 | { |
74 | #if PLATFORM(IOS_FAMILY) |
75 | if (!needsQuirks()) |
76 | return { }; |
77 | |
78 | auto host = m_document->topDocument().url().host(); |
79 | if (equalLettersIgnoringASCIICase(host, "outlook.live.com" )) { |
80 | if (currentViewLayoutWidth <= 989 || currentViewLayoutWidth >= 1132) |
81 | return { }; |
82 | return { 989 }; |
83 | } |
84 | #else |
85 | UNUSED_PARAM(currentViewLayoutWidth); |
86 | #endif |
87 | return { }; |
88 | } |
89 | |
90 | bool Quirks::shouldIgnoreInvalidSignal() const |
91 | { |
92 | if (!needsQuirks()) |
93 | return false; |
94 | |
95 | auto host = m_document->topDocument().url().host(); |
96 | return equalLettersIgnoringASCIICase(host, "www.thrivepatientportal.com" ); |
97 | } |
98 | |
99 | bool Quirks::needsFormControlToBeMouseFocusable() const |
100 | { |
101 | #if PLATFORM(MAC) |
102 | if (!needsQuirks()) |
103 | return false; |
104 | |
105 | auto host = m_document->url().host(); |
106 | return equalLettersIgnoringASCIICase(host, "ceac.state.gov" ) || host.endsWithIgnoringASCIICase(".ceac.state.gov" ); |
107 | #else |
108 | return false; |
109 | #endif |
110 | } |
111 | |
112 | bool Quirks::needsAutoplayPlayPauseEvents() const |
113 | { |
114 | if (!needsQuirks()) |
115 | return false; |
116 | |
117 | if (allowedAutoplayQuirks(*m_document).contains(AutoplayQuirk::SynthesizedPauseEvents)) |
118 | return true; |
119 | |
120 | return allowedAutoplayQuirks(m_document->topDocument()).contains(AutoplayQuirk::SynthesizedPauseEvents); |
121 | } |
122 | |
123 | bool Quirks::needsSeekingSupportDisabled() const |
124 | { |
125 | if (!needsQuirks()) |
126 | return false; |
127 | |
128 | auto host = m_document->topDocument().url().host(); |
129 | return equalLettersIgnoringASCIICase(host, "netflix.com" ) || host.endsWithIgnoringASCIICase(".netflix.com" ); |
130 | } |
131 | |
132 | bool Quirks::needsPerDocumentAutoplayBehavior() const |
133 | { |
134 | #if PLATFORM(MAC) |
135 | ASSERT(m_document == &m_document->topDocument()); |
136 | return needsQuirks() && allowedAutoplayQuirks(*m_document).contains(AutoplayQuirk::PerDocumentAutoplayBehavior); |
137 | #else |
138 | return false; |
139 | #endif |
140 | } |
141 | |
142 | bool Quirks::shouldAutoplayForArbitraryUserGesture() const |
143 | { |
144 | #if PLATFORM(MAC) |
145 | return needsQuirks() && allowedAutoplayQuirks(*m_document).contains(AutoplayQuirk::ArbitraryUserGestures); |
146 | #else |
147 | return false; |
148 | #endif |
149 | } |
150 | |
151 | bool Quirks::hasBrokenEncryptedMediaAPISupportQuirk() const |
152 | { |
153 | if (!needsQuirks()) |
154 | return false; |
155 | |
156 | if (m_hasBrokenEncryptedMediaAPISupportQuirk) |
157 | return m_hasBrokenEncryptedMediaAPISupportQuirk.value(); |
158 | |
159 | auto domain = m_document->securityOrigin().domain().convertToASCIILowercase(); |
160 | |
161 | m_hasBrokenEncryptedMediaAPISupportQuirk = domain == "starz.com" |
162 | || domain.endsWith(".starz.com" ) |
163 | || domain == "youtube.com" |
164 | || domain.endsWith(".youtube.com" ) |
165 | || domain == "hulu.com" |
166 | || domain.endsWith("hulu.com" ); |
167 | |
168 | return m_hasBrokenEncryptedMediaAPISupportQuirk.value(); |
169 | } |
170 | |
171 | bool Quirks::hasWebSQLSupportQuirk() const |
172 | { |
173 | if (!needsQuirks()) |
174 | return false; |
175 | |
176 | if (m_hasWebSQLSupportQuirk) |
177 | return m_hasWebSQLSupportQuirk.value(); |
178 | |
179 | auto domain = m_document->securityOrigin().domain().convertToASCIILowercase(); |
180 | |
181 | m_hasWebSQLSupportQuirk = domain == "bostonglobe.com" |
182 | || domain.endsWith(".bostonglobe.com" ) |
183 | || domain == "latimes.com" |
184 | || domain.endsWith(".latimes.com" ); |
185 | |
186 | return m_hasWebSQLSupportQuirk.value(); |
187 | } |
188 | |
189 | bool Quirks::isTouchBarUpdateSupressedForHiddenContentEditable() const |
190 | { |
191 | #if PLATFORM(MAC) |
192 | if (!needsQuirks()) |
193 | return false; |
194 | |
195 | auto host = m_document->topDocument().url().host(); |
196 | return equalLettersIgnoringASCIICase(host, "docs.google.com" ); |
197 | #else |
198 | return false; |
199 | #endif |
200 | } |
201 | |
202 | bool Quirks::isNeverRichlyEditableForTouchBar() const |
203 | { |
204 | #if PLATFORM(MAC) |
205 | if (!needsQuirks()) |
206 | return false; |
207 | |
208 | auto& url = m_document->topDocument().url(); |
209 | auto host = url.host(); |
210 | |
211 | if (equalLettersIgnoringASCIICase(host, "twitter.com" )) |
212 | return true; |
213 | |
214 | if (equalLettersIgnoringASCIICase(host, "onedrive.live.com" )) |
215 | return true; |
216 | |
217 | if (equalLettersIgnoringASCIICase(host, "trix-editor.org" )) |
218 | return true; |
219 | |
220 | if (equalLettersIgnoringASCIICase(host, "www.icloud.com" )) { |
221 | auto path = url.path(); |
222 | if (path.contains("notes" ) || url.fragmentIdentifier().contains("notes" )) |
223 | return true; |
224 | } |
225 | #endif |
226 | |
227 | return false; |
228 | } |
229 | |
230 | #if USE(APPLE_INTERNAL_SDK) |
231 | #import <WebKitAdditions/QuirksAdditions.cpp> |
232 | #else |
233 | |
234 | static bool shouldSuppressAutocorrectionAndAutocaptializationInHiddenEditableAreasForHost(const StringView&) |
235 | { |
236 | return false; |
237 | } |
238 | |
239 | static bool shouldEmulateUndoRedoInHiddenEditableAreasForHost(const StringView&) |
240 | { |
241 | return false; |
242 | } |
243 | |
244 | #endif |
245 | |
246 | bool Quirks::shouldDispatchSyntheticMouseEventsWhenModifyingSelection() const |
247 | { |
248 | if (m_document->settings().shouldDispatchSyntheticMouseEventsWhenModifyingSelection()) |
249 | return true; |
250 | |
251 | if (!needsQuirks()) |
252 | return false; |
253 | |
254 | auto host = m_document->topDocument().url().host(); |
255 | if (equalLettersIgnoringASCIICase(host, "medium.com" ) || host.endsWithIgnoringASCIICase(".medium.com" )) |
256 | return true; |
257 | |
258 | if (equalLettersIgnoringASCIICase(host, "weebly.com" ) || host.endsWithIgnoringASCIICase(".weebly.com" )) |
259 | return true; |
260 | |
261 | return false; |
262 | } |
263 | |
264 | bool Quirks::shouldEmulateUndoRedoInHiddenEditableAreas() const |
265 | { |
266 | if (!needsQuirks()) |
267 | return false; |
268 | |
269 | return shouldEmulateUndoRedoInHiddenEditableAreasForHost(m_document->topDocument().url().host()); |
270 | } |
271 | |
272 | bool Quirks::shouldSuppressAutocorrectionAndAutocaptializationInHiddenEditableAreas() const |
273 | { |
274 | if (!needsQuirks()) |
275 | return false; |
276 | |
277 | return shouldSuppressAutocorrectionAndAutocaptializationInHiddenEditableAreasForHost(m_document->topDocument().url().host()); |
278 | } |
279 | |
280 | bool Quirks::shouldDispatchSimulatedMouseEvents() const |
281 | { |
282 | #if PLATFORM(IOS_FAMILY) |
283 | if (!needsQuirks()) |
284 | return false; |
285 | |
286 | auto* loader = m_document->loader(); |
287 | if (!loader || loader->simulatedMouseEventsDispatchPolicy() != SimulatedMouseEventsDispatchPolicy::Allow) |
288 | return false; |
289 | |
290 | auto& url = m_document->topDocument().url(); |
291 | auto host = url.host(); |
292 | |
293 | if (equalLettersIgnoringASCIICase(host, "amazon.com" ) || host.endsWithIgnoringASCIICase(".amazon.com" )) |
294 | return true; |
295 | if (equalLettersIgnoringASCIICase(host, "wix.com" ) || host.endsWithIgnoringASCIICase(".wix.com" )) |
296 | return true; |
297 | if ((equalLettersIgnoringASCIICase(host, "desmos.com" ) || host.endsWithIgnoringASCIICase(".desmos.com" )) && url.path().startsWithIgnoringASCIICase("/calculator/" )) |
298 | return true; |
299 | if (equalLettersIgnoringASCIICase(host, "figma.com" ) || host.endsWithIgnoringASCIICase(".figma.com" )) |
300 | return true; |
301 | if (equalLettersIgnoringASCIICase(host, "trello.com" ) || host.endsWithIgnoringASCIICase(".trello.com" )) |
302 | return true; |
303 | if (equalLettersIgnoringASCIICase(host, "airtable.com" ) || host.endsWithIgnoringASCIICase(".airtable.com" )) |
304 | return true; |
305 | if (equalLettersIgnoringASCIICase(host, "msn.com" ) || host.endsWithIgnoringASCIICase(".msn.com" )) |
306 | return true; |
307 | if (equalLettersIgnoringASCIICase(host, "flipkart.com" ) || host.endsWithIgnoringASCIICase(".flipkart.com" )) |
308 | return true; |
309 | if (equalLettersIgnoringASCIICase(host, "www.google.com" ) && url.path().startsWithIgnoringASCIICase("/maps/" )) |
310 | return true; |
311 | if (equalLettersIgnoringASCIICase(host, "trailers.apple.com" )) |
312 | return true; |
313 | #endif |
314 | return false; |
315 | } |
316 | |
317 | bool Quirks::shouldDisablePointerEventsQuirk() const |
318 | { |
319 | #if PLATFORM(IOS_FAMILY) |
320 | if (!needsQuirks()) |
321 | return false; |
322 | |
323 | auto& url = m_document->topDocument().url(); |
324 | auto host = url.host(); |
325 | if (equalLettersIgnoringASCIICase(host, "mailchimp.com" ) || host.endsWithIgnoringASCIICase(".mailchimp.com" )) |
326 | return true; |
327 | #endif |
328 | return false; |
329 | } |
330 | |
331 | // FIXME(<rdar://problem/50394969>): Remove after desmos.com adopts inputmode="none". |
332 | bool Quirks::needsInputModeNoneImplicitly(const HTMLElement& element) const |
333 | { |
334 | #if PLATFORM(IOS_FAMILY) |
335 | if (!needsQuirks()) |
336 | return false; |
337 | |
338 | if (!element.hasTagName(HTMLNames::textareaTag)) |
339 | return false; |
340 | |
341 | auto& url = m_document->url(); |
342 | auto host = url.host(); |
343 | if (!host.endsWithIgnoringASCIICase(".desmos.com" )) |
344 | return false; |
345 | |
346 | return element.parentElement() && element.parentElement()->classNames().contains("dcg-mq-textarea" ); |
347 | #else |
348 | UNUSED_PARAM(element); |
349 | return false; |
350 | #endif |
351 | } |
352 | |
353 | // FIXME: Remove after the site is fixed, <rdar://problem/50374200> |
354 | bool Quirks::needsGMailOverflowScrollQuirk() const |
355 | { |
356 | #if PLATFORM(IOS_FAMILY) |
357 | if (!needsQuirks()) |
358 | return false; |
359 | |
360 | if (!m_needsGMailOverflowScrollQuirk) |
361 | m_needsGMailOverflowScrollQuirk = equalLettersIgnoringASCIICase(m_document->url().host(), "mail.google.com" ); |
362 | |
363 | return *m_needsGMailOverflowScrollQuirk; |
364 | #else |
365 | return false; |
366 | #endif |
367 | } |
368 | |
369 | // FIXME: Remove after the site is fixed, <rdar://problem/50374311> |
370 | bool Quirks::needsYouTubeOverflowScrollQuirk() const |
371 | { |
372 | #if PLATFORM(IOS_FAMILY) |
373 | if (!needsQuirks()) |
374 | return false; |
375 | |
376 | if (!m_needsYouTubeOverflowScrollQuirk) |
377 | m_needsYouTubeOverflowScrollQuirk = equalLettersIgnoringASCIICase(m_document->url().host(), "www.youtube.com" ); |
378 | |
379 | return *m_needsYouTubeOverflowScrollQuirk; |
380 | #else |
381 | return false; |
382 | #endif |
383 | } |
384 | |
385 | |
386 | } |
387 | |