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
36namespace WebCore {
37
38static inline OptionSet<AutoplayQuirk> allowedAutoplayQuirks(Document& document)
39{
40 auto* loader = document.loader();
41 if (!loader)
42 return { };
43
44 return loader->allowedAutoplayQuirks();
45}
46
47Quirks::Quirks(Document& document)
48 : m_document(makeWeakPtr(document))
49{
50}
51
52Quirks::~Quirks() = default;
53
54inline bool Quirks::needsQuirks() const
55{
56 return m_document && m_document->settings().needsSiteSpecificQuirks();
57}
58
59bool 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
72Optional<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
90bool 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
99bool 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
112bool 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
123bool 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
132bool 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
142bool 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
151bool 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
171bool 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
189bool 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
202bool 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
234static bool shouldSuppressAutocorrectionAndAutocaptializationInHiddenEditableAreasForHost(const StringView&)
235{
236 return false;
237}
238
239static bool shouldEmulateUndoRedoInHiddenEditableAreasForHost(const StringView&)
240{
241 return false;
242}
243
244#endif
245
246bool 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
264bool Quirks::shouldEmulateUndoRedoInHiddenEditableAreas() const
265{
266 if (!needsQuirks())
267 return false;
268
269 return shouldEmulateUndoRedoInHiddenEditableAreasForHost(m_document->topDocument().url().host());
270}
271
272bool Quirks::shouldSuppressAutocorrectionAndAutocaptializationInHiddenEditableAreas() const
273{
274 if (!needsQuirks())
275 return false;
276
277 return shouldSuppressAutocorrectionAndAutocaptializationInHiddenEditableAreasForHost(m_document->topDocument().url().host());
278}
279
280bool 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
317bool 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".
332bool 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>
354bool 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>
370bool 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