2abd8968b89009be323cc264de9b749e3aaec64c
[WebKit-https.git] / Source / WebCore / page / Quirks.cpp
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 "Settings.h"
34
35 namespace WebCore {
36
37 static inline OptionSet<AutoplayQuirk> allowedAutoplayQuirks(Document& document)
38 {
39     auto* loader = document.loader();
40     if (!loader)
41         return { };
42
43     return loader->allowedAutoplayQuirks();
44 }
45
46 Quirks::Quirks(Document& document)
47     : m_document(makeWeakPtr(document))
48 {
49 }
50
51 Quirks::~Quirks() = default;
52
53 inline bool Quirks::needsQuirks() const
54 {
55     return m_document && m_document->settings().needsSiteSpecificQuirks();
56 }
57
58 bool Quirks::shouldIgnoreShrinkToFitContent() const
59 {
60 #if PLATFORM(IOS_FAMILY)
61     if (!needsQuirks())
62         return false;
63
64     auto host = m_document->topDocument().url().host();
65     if (equalLettersIgnoringASCIICase(host, "outlook.live.com"))
66         return true;
67 #endif
68     return false;
69 }
70
71 Optional<LayoutUnit> Quirks::overriddenViewLayoutWidth(LayoutUnit currentViewLayoutWidth) const
72 {
73 #if PLATFORM(IOS_FAMILY)
74     if (!needsQuirks())
75         return { };
76
77     auto host = m_document->topDocument().url().host();
78     if (equalLettersIgnoringASCIICase(host, "outlook.live.com")) {
79         if (currentViewLayoutWidth <= 989 || currentViewLayoutWidth >= 1132)
80             return { };
81         return { 989 };
82     }
83 #else
84     UNUSED_PARAM(currentViewLayoutWidth);
85 #endif
86     return { };
87 }
88
89 bool Quirks::shouldIgnoreInvalidSignal() const
90 {
91     if (!needsQuirks())
92         return false;
93
94     auto host = m_document->topDocument().url().host();
95     return equalLettersIgnoringASCIICase(host, "www.thrivepatientportal.com");
96 }
97
98 bool Quirks::needsFormControlToBeMouseFocusable() const
99 {
100 #if PLATFORM(MAC)
101     if (!needsQuirks())
102         return false;
103
104     auto host = m_document->url().host();
105     return equalLettersIgnoringASCIICase(host, "ceac.state.gov") || host.endsWithIgnoringASCIICase(".ceac.state.gov");
106 #else
107     return false;
108 #endif
109 }
110
111 bool Quirks::needsAutoplayPlayPauseEvents() const
112 {
113     if (!needsQuirks())
114         return false;
115
116     if (allowedAutoplayQuirks(*m_document).contains(AutoplayQuirk::SynthesizedPauseEvents))
117         return true;
118
119     return allowedAutoplayQuirks(m_document->topDocument()).contains(AutoplayQuirk::SynthesizedPauseEvents);
120 }
121
122 bool Quirks::needsSeekingSupportDisabled() const
123 {
124     if (!needsQuirks())
125         return false;
126
127     auto host = m_document->topDocument().url().host();
128     return equalLettersIgnoringASCIICase(host, "netflix.com") || host.endsWithIgnoringASCIICase(".netflix.com");
129 }
130
131 bool Quirks::needsPerDocumentAutoplayBehavior() const
132 {
133 #if PLATFORM(MAC)
134     ASSERT(m_document == &m_document->topDocument());
135     return needsQuirks() && allowedAutoplayQuirks(*m_document).contains(AutoplayQuirk::PerDocumentAutoplayBehavior);
136 #else
137     return false;
138 #endif
139 }
140
141 bool Quirks::shouldAutoplayForArbitraryUserGesture() const
142 {
143 #if PLATFORM(MAC)
144     return needsQuirks() && allowedAutoplayQuirks(*m_document).contains(AutoplayQuirk::ArbitraryUserGestures);
145 #else
146     return false;
147 #endif
148 }
149
150 bool Quirks::hasBrokenEncryptedMediaAPISupportQuirk() const
151 {
152     if (!needsQuirks())
153         return false;
154
155     if (m_hasBrokenEncryptedMediaAPISupportQuirk)
156         return m_hasBrokenEncryptedMediaAPISupportQuirk.value();
157
158     auto domain = m_document->securityOrigin().domain().convertToASCIILowercase();
159
160     m_hasBrokenEncryptedMediaAPISupportQuirk = domain == "starz.com"
161         || domain.endsWith(".starz.com")
162         || domain == "youtube.com"
163         || domain.endsWith(".youtube.com")
164         || domain == "hulu.com"
165         || domain.endsWith("hulu.com");
166
167     return m_hasBrokenEncryptedMediaAPISupportQuirk.value();
168 }
169
170 bool Quirks::hasWebSQLSupportQuirk() const
171 {
172     if (!needsQuirks())
173         return false;
174     
175     if (m_hasWebSQLSupportQuirk)
176         return m_hasWebSQLSupportQuirk.value();
177     
178     auto domain = m_document->securityOrigin().domain().convertToASCIILowercase();
179     
180     m_hasWebSQLSupportQuirk = domain == "bostonglobe.com"
181         || domain.endsWith(".bostonglobe.com")
182         || domain == "latimes.com"
183         || domain.endsWith(".latimes.com");
184     
185     return m_hasWebSQLSupportQuirk.value();
186 }
187
188 bool Quirks::isTouchBarUpdateSupressedForHiddenContentEditable() const
189 {
190 #if PLATFORM(MAC)
191     if (!needsQuirks())
192         return false;
193
194     auto host = m_document->topDocument().url().host();
195     return equalLettersIgnoringASCIICase(host, "docs.google.com");
196 #else
197     return false;
198 #endif
199 }
200
201 bool Quirks::isNeverRichlyEditableForTouchBar() const
202 {
203 #if PLATFORM(MAC)
204     if (!needsQuirks())
205         return false;
206
207     auto& url = m_document->topDocument().url();
208     auto host = url.host();
209
210     if (equalLettersIgnoringASCIICase(host, "twitter.com"))
211         return true;
212
213     if (equalLettersIgnoringASCIICase(host, "onedrive.live.com"))
214         return true;
215
216     if (equalLettersIgnoringASCIICase(host, "trix-editor.org"))
217         return true;
218
219     if (equalLettersIgnoringASCIICase(host, "www.icloud.com")) {
220         auto path = url.path();
221         if (path.contains("notes") || url.fragmentIdentifier().contains("notes"))
222             return true;
223     }
224 #endif
225
226     return false;
227 }
228
229 #if USE(APPLE_INTERNAL_SDK)
230 #import <WebKitAdditions/QuirksAdditions.cpp>
231 #else
232
233 static bool shouldSuppressAutocorrectionAndAutocaptializationInHiddenEditableAreasForHost(const StringView&)
234 {
235     return false;
236 }
237
238 static bool shouldEmulateUndoRedoInHiddenEditableAreasForHost(const StringView&)
239 {
240     return false;
241 }
242
243 #endif
244
245 bool Quirks::shouldDispatchSyntheticMouseEventsWhenModifyingSelection() const
246 {
247     if (m_document->settings().shouldDispatchSyntheticMouseEventsWhenModifyingSelection())
248         return true;
249
250     if (!needsQuirks())
251         return false;
252
253     auto host = m_document->topDocument().url().host();
254     if (equalLettersIgnoringASCIICase(host, "medium.com") || host.endsWithIgnoringASCIICase(".medium.com"))
255         return true;
256
257     if (equalLettersIgnoringASCIICase(host, "weebly.com") || host.endsWithIgnoringASCIICase(".weebly.com"))
258         return true;
259
260     return false;
261 }
262
263 bool Quirks::shouldEmulateUndoRedoInHiddenEditableAreas() const
264 {
265     if (!needsQuirks())
266         return false;
267
268     return shouldEmulateUndoRedoInHiddenEditableAreasForHost(m_document->topDocument().url().host());
269 }
270
271 bool Quirks::shouldSuppressAutocorrectionAndAutocaptializationInHiddenEditableAreas() const
272 {
273     if (!needsQuirks())
274         return false;
275
276     return shouldSuppressAutocorrectionAndAutocaptializationInHiddenEditableAreasForHost(m_document->topDocument().url().host());
277 }
278
279 bool Quirks::shouldDispatchSimulatedMouseEvents() const
280 {
281 #if PLATFORM(IOS_FAMILY)
282     if (!needsQuirks())
283         return false;
284
285     auto* loader = m_document->loader();
286     if (!loader || loader->simulatedMouseEventsDispatchPolicy() != SimulatedMouseEventsDispatchPolicy::Allow)
287         return false;
288
289     auto& url = m_document->topDocument().url();
290     auto host = url.host();
291
292     if (equalLettersIgnoringASCIICase(host, "amazon.com") || host.endsWithIgnoringASCIICase(".amazon.com"))
293         return true;
294     if (equalLettersIgnoringASCIICase(host, "wix.com") || host.endsWithIgnoringASCIICase(".wix.com"))
295         return true;
296     if ((equalLettersIgnoringASCIICase(host, "desmos.com") || host.endsWithIgnoringASCIICase(".desmos.com")) && url.path().startsWithIgnoringASCIICase("/calculator/"))
297         return true;
298     if (equalLettersIgnoringASCIICase(host, "figma.com") || host.endsWithIgnoringASCIICase(".figma.com"))
299         return true;
300     if (equalLettersIgnoringASCIICase(host, "trello.com") || host.endsWithIgnoringASCIICase(".trello.com"))
301         return true;
302     if (equalLettersIgnoringASCIICase(host, "airtable.com") || host.endsWithIgnoringASCIICase(".airtable.com"))
303         return true;
304     if (equalLettersIgnoringASCIICase(host, "msn.com") || host.endsWithIgnoringASCIICase(".msn.com"))
305         return true;
306     if (equalLettersIgnoringASCIICase(host, "flipkart.com") || host.endsWithIgnoringASCIICase(".flipkart.com"))
307         return true;
308     if (equalLettersIgnoringASCIICase(host, "www.google.com") && url.path().startsWithIgnoringASCIICase("/maps/"))
309         return true;
310     if (equalLettersIgnoringASCIICase(host, "trailers.apple.com"))
311         return true;
312 #endif
313     return false;
314 }
315
316 bool Quirks::shouldDisablePointerEventsQuirk() const
317 {
318 #if PLATFORM(IOS_FAMILY)
319     if (!needsQuirks())
320         return false;
321
322     auto& url = m_document->topDocument().url();
323     auto host = url.host();
324     if (equalLettersIgnoringASCIICase(host, "mailchimp.com") || host.endsWithIgnoringASCIICase(".mailchimp.com"))
325         return true;
326 #endif
327     return false;
328 }
329
330 // FIXME(<rdar://problem/50394969>): Remove after desmos.com adopts inputmode="none".
331 bool Quirks::needsInputModeNoneImplicitly(const HTMLElement& element) const
332 {
333 #if PLATFORM(IOS_FAMILY)
334     if (!needsQuirks())
335         return false;
336
337     if (!element.hasTagName(HTMLNames::textareaTag))
338         return false;
339
340     auto& url = m_document->url();
341     auto host = url.host();
342     if (!host.endsWithIgnoringASCIICase(".desmos.com"))
343         return false;
344
345     return element.parentElement() && element.parentElement()->classNames().contains("dcg-mq-textarea");
346 #else
347     UNUSED_PARAM(element);
348     return false;
349 #endif
350 }
351
352 // FIXME: Remove after the site is fixed, <rdar://problem/50374200>
353 bool Quirks::needsGMailOverflowScrollQuirk() const
354 {
355 #if PLATFORM(IOS_FAMILY)
356     if (!needsQuirks())
357         return false;
358
359     if (!m_needsGMailOverflowScrollQuirk)
360         m_needsGMailOverflowScrollQuirk = equalLettersIgnoringASCIICase(m_document->url().host(), "mail.google.com");
361
362     return *m_needsGMailOverflowScrollQuirk;
363 #else
364     return false;
365 #endif
366 }
367
368 // FIXME: Remove after the site is fixed, <rdar://problem/50374311>
369 bool Quirks::needsYouTubeOverflowScrollQuirk() const
370 {
371 #if PLATFORM(IOS_FAMILY)
372     if (!needsQuirks())
373         return false;
374
375     if (!m_needsYouTubeOverflowScrollQuirk)
376         m_needsYouTubeOverflowScrollQuirk = equalLettersIgnoringASCIICase(m_document->url().host(), "www.youtube.com");
377
378     return *m_needsYouTubeOverflowScrollQuirk;
379 #else
380     return false;
381 #endif
382 }
383
384
385 }