Unreviewed build-fix; fix !HAVE(AVKIT) build after r193340.
[WebKit-https.git] / Source / WebKit2 / UIProcess / ios / WebVideoFullscreenManagerProxy.mm
1 /*
2  * Copyright (C) 2014 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 #import "config.h"
27 #import "WebVideoFullscreenManagerProxy.h"
28
29 #if PLATFORM(IOS)
30
31 #import "RemoteLayerTreeDrawingAreaProxy.h"
32 #import "UIKitSPI.h"
33 #import "WebPageProxy.h"
34 #import "WebProcessProxy.h"
35 #import "WebVideoFullscreenManagerMessages.h"
36 #import "WebVideoFullscreenManagerProxyMessages.h"
37 #import <QuartzCore/CoreAnimation.h>
38 #import <WebCore/QuartzCoreSPI.h>
39 #import <WebCore/TimeRanges.h>
40 #import <WebKitSystemInterface.h>
41
42 @interface WebLayerHostView : UIView
43 @property (nonatomic, assign) uint32_t contextID;
44 @end
45
46 @implementation WebLayerHostView
47
48 + (Class)layerClass {
49     return [CALayerHost class];
50 }
51
52 - (uint32_t)contextID {
53     return [[self layerHost] contextId];
54 }
55
56 - (void)setContextID:(uint32_t)contextID {
57     [[self layerHost] setContextId:contextID];
58 }
59
60 - (CALayerHost *)layerHost {
61     return (CALayerHost *)[self layer];
62 }
63
64 @end
65
66 using namespace WebCore;
67
68 namespace WebKit {
69
70 #if !HAVE(AVKIT)
71
72 RefPtr<WebVideoFullscreenManagerProxy> WebVideoFullscreenManagerProxy::create(WebPageProxy&)
73 {
74     return nullptr;
75 }
76
77 void WebVideoFullscreenManagerProxy::invalidate()
78 {
79 }
80
81 bool WebVideoFullscreenManagerProxy::hasMode(HTMLMediaElementEnums::VideoFullscreenMode) const
82 {
83     return false;
84 }
85
86 bool WebVideoFullscreenManagerProxy::mayAutomaticallyShowVideoPictureInPicture() const
87 {
88     return false;
89 }
90
91 void WebVideoFullscreenManagerProxy::requestHideAndExitFullscreen()
92 {
93
94 }
95
96 void WebVideoFullscreenManagerProxy::applicationDidBecomeActive()
97 {
98
99 }
100 #else
101
102 #pragma mark - WebVideoFullscreenModelContext
103
104 void WebVideoFullscreenModelContext::play()
105 {
106     if (m_manager)
107         m_manager->play(m_contextId);
108 }
109
110 void WebVideoFullscreenModelContext::pause()
111 {
112     if (m_manager)
113         m_manager->pause(m_contextId);
114 }
115
116 void WebVideoFullscreenModelContext::togglePlayState()
117 {
118     if (m_manager)
119         m_manager->togglePlayState(m_contextId);
120 }
121
122 void WebVideoFullscreenModelContext::beginScrubbing()
123 {
124     if (m_manager)
125         m_manager->beginScrubbing(m_contextId);
126 }
127
128 void WebVideoFullscreenModelContext::endScrubbing()
129 {
130     if (m_manager)
131         m_manager->endScrubbing(m_contextId);
132 }
133
134 void WebVideoFullscreenModelContext::seekToTime(double time)
135 {
136     if (m_manager)
137         m_manager->seekToTime(m_contextId, time);
138 }
139
140 void WebVideoFullscreenModelContext::fastSeek(double time)
141 {
142     if (m_manager)
143         m_manager->fastSeek(m_contextId, time);
144 }
145
146 void WebVideoFullscreenModelContext::beginScanningForward()
147 {
148     if (m_manager)
149         m_manager->beginScanningForward(m_contextId);
150 }
151
152 void WebVideoFullscreenModelContext::beginScanningBackward()
153 {
154     if (m_manager)
155         m_manager->beginScanningBackward(m_contextId);
156 }
157
158 void WebVideoFullscreenModelContext::endScanning()
159 {
160     if (m_manager)
161         m_manager->endScanning(m_contextId);
162 }
163
164 void WebVideoFullscreenModelContext::requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
165 {
166     if (m_manager)
167         m_manager->requestFullscreenMode(m_contextId, mode);
168 }
169
170 void WebVideoFullscreenModelContext::setVideoLayerFrame(WebCore::FloatRect frame)
171 {
172     if (m_manager)
173         m_manager->setVideoLayerFrame(m_contextId, frame);
174 }
175
176 void WebVideoFullscreenModelContext::setVideoLayerGravity(WebCore::WebVideoFullscreenModel::VideoGravity gravity)
177 {
178     if (m_manager)
179         m_manager->setVideoLayerGravity(m_contextId, gravity);
180 }
181
182 void WebVideoFullscreenModelContext::selectAudioMediaOption(uint64_t optionId)
183 {
184     if (m_manager)
185         m_manager->selectAudioMediaOption(m_contextId, optionId);
186 }
187
188 void WebVideoFullscreenModelContext::selectLegibleMediaOption(uint64_t optionId)
189 {
190     if (m_manager)
191         m_manager->selectLegibleMediaOption(m_contextId, optionId);
192 }
193
194 void WebVideoFullscreenModelContext::fullscreenModeChanged(WebCore::HTMLMediaElementEnums::VideoFullscreenMode mode)
195 {
196     if (m_manager)
197         m_manager->fullscreenModeChanged(m_contextId, mode);
198 }
199
200 bool WebVideoFullscreenModelContext::isVisible() const
201 {
202     return m_manager ? m_manager->isVisible() : false;
203 }
204
205 void WebVideoFullscreenModelContext::didSetupFullscreen()
206 {
207     if (m_manager)
208         m_manager->didSetupFullscreen(m_contextId);
209 }
210
211 void WebVideoFullscreenModelContext::didEnterFullscreen()
212 {
213     if (m_manager)
214         m_manager->didEnterFullscreen(m_contextId);
215 }
216
217 void WebVideoFullscreenModelContext::didExitFullscreen()
218 {
219     if (m_manager)
220         m_manager->didExitFullscreen(m_contextId);
221 }
222
223 void WebVideoFullscreenModelContext::didCleanupFullscreen()
224 {
225     if (m_manager)
226         m_manager->didCleanupFullscreen(m_contextId);
227 }
228
229 void WebVideoFullscreenModelContext::fullscreenMayReturnToInline()
230 {
231     if (m_manager)
232         m_manager->fullscreenMayReturnToInline(m_contextId);
233 }
234
235 #pragma mark - WebVideoFullscreenManagerProxy
236
237 RefPtr<WebVideoFullscreenManagerProxy> WebVideoFullscreenManagerProxy::create(WebPageProxy& page)
238 {
239     return adoptRef(new WebVideoFullscreenManagerProxy(page));
240 }
241
242 WebVideoFullscreenManagerProxy::WebVideoFullscreenManagerProxy(WebPageProxy& page)
243     : m_page(&page)
244 {
245     m_page->process().addMessageReceiver(Messages::WebVideoFullscreenManagerProxy::messageReceiverName(), m_page->pageID(), *this);
246 }
247
248 WebVideoFullscreenManagerProxy::~WebVideoFullscreenManagerProxy()
249 {
250     if (!m_page)
251         return;
252     invalidate();
253 }
254
255 void WebVideoFullscreenManagerProxy::invalidate()
256 {
257     m_page->process().removeMessageReceiver(Messages::WebVideoFullscreenManagerProxy::messageReceiverName(), m_page->pageID());
258     m_page = nullptr;
259
260     for (auto& tuple : m_contextMap.values()) {
261         RefPtr<WebVideoFullscreenModelContext> model;
262         RefPtr<WebCore::WebVideoFullscreenInterfaceAVKit> interface;
263         std::tie(model, interface) = tuple;
264
265         interface->invalidate();
266         [model->layerHostView() removeFromSuperview];
267         model->setLayerHostView(nullptr);
268     }
269
270     m_contextMap.clear();
271 }
272
273 void WebVideoFullscreenManagerProxy::requestHideAndExitFullscreen()
274 {
275     for (auto& tuple : m_contextMap.values())
276         std::get<1>(tuple)->requestHideAndExitFullscreen();
277 }
278
279 bool WebVideoFullscreenManagerProxy::hasMode(HTMLMediaElementEnums::VideoFullscreenMode mode) const
280 {
281     for (auto& tuple : m_contextMap.values()) {
282         if (std::get<1>(tuple)->hasMode(mode))
283             return true;
284     }
285     return false;
286 }
287
288 bool WebVideoFullscreenManagerProxy::mayAutomaticallyShowVideoPictureInPicture() const
289 {
290     for (auto& tuple : m_contextMap.values()) {
291         if (std::get<1>(tuple)->mayAutomaticallyShowVideoPictureInPicture())
292             return true;
293     }
294     return false;
295 }
296
297 void WebVideoFullscreenManagerProxy::applicationDidBecomeActive()
298 {
299     for (auto& tuple : m_contextMap.values())
300         std::get<1>(tuple)->applicationDidBecomeActive();
301 }
302
303 WebVideoFullscreenManagerProxy::ModelInterfaceTuple WebVideoFullscreenManagerProxy::createModelAndInterface(uint64_t contextId)
304 {
305     Ref<WebVideoFullscreenModelContext> model = WebVideoFullscreenModelContext::create(*this, contextId);
306     Ref<WebCore::WebVideoFullscreenInterfaceAVKit> interface = WebVideoFullscreenInterfaceAVKit::create();
307
308     interface->setWebVideoFullscreenModel(&model.get());
309     interface->setWebVideoFullscreenChangeObserver(&model.get());
310
311     return std::make_tuple(WTF::move(model), WTF::move(interface));
312 }
313
314 WebVideoFullscreenManagerProxy::ModelInterfaceTuple& WebVideoFullscreenManagerProxy::ensureModelAndInterface(uint64_t contextId)
315 {
316     auto addResult = m_contextMap.add(contextId, ModelInterfaceTuple());
317     if (addResult.isNewEntry)
318         addResult.iterator->value = createModelAndInterface(contextId);
319     return addResult.iterator->value;
320 }
321
322 WebVideoFullscreenModelContext& WebVideoFullscreenManagerProxy::ensureModel(uint64_t contextId)
323 {
324     return *std::get<0>(ensureModelAndInterface(contextId));
325 }
326
327 WebCore::WebVideoFullscreenInterfaceAVKit& WebVideoFullscreenManagerProxy::ensureInterface(uint64_t contextId)
328 {
329     return *std::get<1>(ensureModelAndInterface(contextId));
330 }
331
332 #pragma mark Messages from WebVideoFullscreenManager
333
334 void WebVideoFullscreenManagerProxy::setupFullscreenWithID(uint64_t contextId, uint32_t videoLayerID, const WebCore::IntRect& initialRect, float hostingDeviceScaleFactor, HTMLMediaElementEnums::VideoFullscreenMode videoFullscreenMode, bool allowsPictureInPicture)
335 {
336     ASSERT(videoLayerID);
337     RefPtr<WebVideoFullscreenModelContext> model;
338     RefPtr<WebCore::WebVideoFullscreenInterfaceAVKit> interface;
339
340     std::tie(model, interface) = ensureModelAndInterface(contextId);
341
342     RetainPtr<WebLayerHostView> view = static_cast<WebLayerHostView*>(model->layerHostView());
343     if (!view) {
344         view = adoptNS([[WebLayerHostView alloc] init]);
345         model->setLayerHostView(view);
346     }
347     [view setContextID:videoLayerID];
348     if (hostingDeviceScaleFactor != 1) {
349         // Invert the scale transform added in the WebProcess to fix <rdar://problem/18316542>.
350         float inverseScale = 1 / hostingDeviceScaleFactor;
351         [[view layer] setSublayerTransform:CATransform3DMakeScale(inverseScale, inverseScale, 1)];
352     }
353
354     UIView *parentView = downcast<RemoteLayerTreeDrawingAreaProxy>(*m_page->drawingArea()).remoteLayerTreeHost().rootLayer();
355     interface->setupFullscreen(*model->layerHostView(), initialRect, parentView, videoFullscreenMode, allowsPictureInPicture);
356 }
357
358 void WebVideoFullscreenManagerProxy::resetMediaState(uint64_t contextId)
359 {
360     ensureInterface(contextId).resetMediaState();
361 }
362
363 void WebVideoFullscreenManagerProxy::setCurrentTime(uint64_t contextId, double currentTime, double hostTime)
364 {
365     ensureInterface(contextId).setCurrentTime(currentTime, hostTime);
366 }
367
368 void WebVideoFullscreenManagerProxy::setBufferedTime(uint64_t contextId, double bufferedTime)
369 {
370     ensureInterface(contextId).setBufferedTime(bufferedTime);
371 }
372
373 void WebVideoFullscreenManagerProxy::setVideoDimensions(uint64_t contextId, bool hasVideo, unsigned width, unsigned height)
374 {
375     ensureInterface(contextId).setVideoDimensions(hasVideo, width, height);
376 }
377
378 void WebVideoFullscreenManagerProxy::setSeekableRangesVector(uint64_t contextId, Vector<std::pair<double, double>> ranges)
379 {
380     RefPtr<TimeRanges> timeRanges = TimeRanges::create();
381     for (const auto& range : ranges)
382     {
383         ASSERT(isfinite(range.first));
384         ASSERT(isfinite(range.second));
385         ASSERT(range.second >= range.first);
386         timeRanges->add(range.first, range.second);
387     }
388
389     ensureInterface(contextId).setSeekableRanges(*timeRanges);
390 }
391
392 void WebVideoFullscreenManagerProxy::setCanPlayFastReverse(uint64_t contextId, bool value)
393 {
394     ensureInterface(contextId).setCanPlayFastReverse(value);
395 }
396
397 void WebVideoFullscreenManagerProxy::setAudioMediaSelectionOptions(uint64_t contextId, Vector<String> options, uint64_t selectedIndex)
398 {
399     ensureInterface(contextId).setAudioMediaSelectionOptions(options, selectedIndex);
400 }
401
402 void WebVideoFullscreenManagerProxy::setLegibleMediaSelectionOptions(uint64_t contextId, Vector<String> options, uint64_t selectedIndex)
403 {
404     ensureInterface(contextId).setLegibleMediaSelectionOptions(options, selectedIndex);
405 }
406
407 void WebVideoFullscreenManagerProxy::setExternalPlaybackProperties(uint64_t contextId, bool enabled, uint32_t targetType, String localizedDeviceName)
408 {
409     WebVideoFullscreenInterface::ExternalPlaybackTargetType type = static_cast<WebVideoFullscreenInterface::ExternalPlaybackTargetType>(targetType);
410     ASSERT(type == WebVideoFullscreenInterface::TargetTypeAirPlay || type == WebVideoFullscreenInterface::TargetTypeTVOut || type == WebVideoFullscreenInterface::TargetTypeNone);
411     
412     ensureInterface(contextId).setExternalPlayback(enabled, type, localizedDeviceName);
413 }
414
415 void WebVideoFullscreenManagerProxy::setWirelessVideoPlaybackDisabled(uint64_t contextId, bool disabled)
416 {
417     ensureInterface(contextId).setWirelessVideoPlaybackDisabled(disabled);
418 }
419
420 void WebVideoFullscreenManagerProxy::setDuration(uint64_t contextId, double duration)
421 {
422     ensureInterface(contextId).setDuration(duration);
423 }
424
425 void WebVideoFullscreenManagerProxy::setRate(uint64_t contextId, bool isPlaying, double rate)
426 {
427     ensureInterface(contextId).setRate(isPlaying, rate);
428 }
429
430 void WebVideoFullscreenManagerProxy::enterFullscreen(uint64_t contextId)
431 {
432     auto& interface = ensureInterface(contextId);
433     interface.enterFullscreen();
434
435     // Only one context can be in a given full screen mode at a time:
436     for (auto& contextPair : m_contextMap) {
437         auto& otherContextId = contextPair.key;
438         if (contextId == otherContextId)
439             continue;
440
441         auto& otherInterface = std::get<1>(contextPair.value);
442         if (otherInterface->hasMode(interface.mode()))
443             otherInterface->requestHideAndExitFullscreen();
444     }
445 }
446
447 void WebVideoFullscreenManagerProxy::exitFullscreen(uint64_t contextId, WebCore::IntRect finalRect)
448 {
449     ensureInterface(contextId).exitFullscreen(finalRect);
450 }
451
452 void WebVideoFullscreenManagerProxy::cleanupFullscreen(uint64_t contextId)
453 {
454     ensureInterface(contextId).cleanupFullscreen();
455 }
456
457 void WebVideoFullscreenManagerProxy::preparedToReturnToInline(uint64_t contextId, bool visible, WebCore::IntRect inlineRect)
458 {
459     m_page->fullscreenMayReturnToInline();
460
461     ensureInterface(contextId).preparedToReturnToInline(visible, inlineRect);
462 }
463
464 #pragma mark Messages to WebVideoFullscreenManager
465
466 void WebVideoFullscreenManagerProxy::play(uint64_t contextId)
467 {
468     m_page->send(Messages::WebVideoFullscreenManager::Play(contextId), m_page->pageID());
469 }
470
471 void WebVideoFullscreenManagerProxy::pause(uint64_t contextId)
472 {
473     m_page->send(Messages::WebVideoFullscreenManager::Pause(contextId), m_page->pageID());
474 }
475
476 void WebVideoFullscreenManagerProxy::togglePlayState(uint64_t contextId)
477 {
478     m_page->send(Messages::WebVideoFullscreenManager::TogglePlayState(contextId), m_page->pageID());
479 }
480
481 void WebVideoFullscreenManagerProxy::beginScrubbing(uint64_t contextId)
482 {
483     m_page->send(Messages::WebVideoFullscreenManager::BeginScrubbing(contextId), m_page->pageID());
484 }
485
486 void WebVideoFullscreenManagerProxy::endScrubbing(uint64_t contextId)
487 {
488     m_page->send(Messages::WebVideoFullscreenManager::EndScrubbing(contextId), m_page->pageID());
489 }
490
491 void WebVideoFullscreenManagerProxy::seekToTime(uint64_t contextId, double time)
492 {
493     m_page->send(Messages::WebVideoFullscreenManager::SeekToTime(contextId, time), m_page->pageID());
494 }
495
496 void WebVideoFullscreenManagerProxy::fastSeek(uint64_t contextId, double time)
497 {
498     m_page->send(Messages::WebVideoFullscreenManager::FastSeek(contextId, time), m_page->pageID());
499 }
500
501 void WebVideoFullscreenManagerProxy::beginScanningForward(uint64_t contextId)
502 {
503     m_page->send(Messages::WebVideoFullscreenManager::BeginScanningForward(contextId), m_page->pageID());
504 }
505
506 void WebVideoFullscreenManagerProxy::beginScanningBackward(uint64_t contextId)
507 {
508     m_page->send(Messages::WebVideoFullscreenManager::BeginScanningBackward(contextId), m_page->pageID());
509 }
510
511 void WebVideoFullscreenManagerProxy::endScanning(uint64_t contextId)
512 {
513     m_page->send(Messages::WebVideoFullscreenManager::EndScanning(contextId), m_page->pageID());
514 }
515
516 void WebVideoFullscreenManagerProxy::requestFullscreenMode(uint64_t contextId, WebCore::HTMLMediaElementEnums::VideoFullscreenMode mode)
517 {
518     m_page->send(Messages::WebVideoFullscreenManager::RequestFullscreenMode(contextId, mode), m_page->pageID());
519 }
520
521 void WebVideoFullscreenManagerProxy::didSetupFullscreen(uint64_t contextId)
522 {
523     m_page->send(Messages::WebVideoFullscreenManager::DidSetupFullscreen(contextId), m_page->pageID());
524 }
525
526 void WebVideoFullscreenManagerProxy::didExitFullscreen(uint64_t contextId)
527 {
528     m_page->send(Messages::WebVideoFullscreenManager::DidExitFullscreen(contextId), m_page->pageID());
529     m_page->didExitFullscreen();
530 }
531
532 void WebVideoFullscreenManagerProxy::didEnterFullscreen(uint64_t contextId)
533 {
534     m_page->send(Messages::WebVideoFullscreenManager::DidEnterFullscreen(contextId), m_page->pageID());
535     m_page->didEnterFullscreen();
536 }
537
538 void WebVideoFullscreenManagerProxy::didCleanupFullscreen(uint64_t contextId)
539 {
540     auto& model = ensureModel(contextId);
541
542     [CATransaction flush];
543     [model.layerHostView() removeFromSuperview];
544     model.setLayerHostView(nullptr);
545     m_page->send(Messages::WebVideoFullscreenManager::DidCleanupFullscreen(contextId), m_page->pageID());
546
547     m_contextMap.remove(contextId);
548 }
549
550 void WebVideoFullscreenManagerProxy::setVideoLayerFrame(uint64_t contextId, WebCore::FloatRect frame)
551 {
552     @autoreleasepool {
553         mach_port_name_t fencePort = [UIWindow _synchronizeDrawingAcrossProcesses];
554
555         m_page->send(Messages::WebVideoFullscreenManager::SetVideoLayerFrameFenced(contextId, frame, IPC::Attachment(fencePort, MACH_MSG_TYPE_MOVE_SEND)), m_page->pageID());
556     }
557 }
558
559 void WebVideoFullscreenManagerProxy::setVideoLayerGravity(uint64_t contextId, WebCore::WebVideoFullscreenModel::VideoGravity gravity)
560 {
561     m_page->send(Messages::WebVideoFullscreenManager::SetVideoLayerGravityEnum(contextId, (unsigned)gravity), m_page->pageID());
562 }
563
564 void WebVideoFullscreenManagerProxy::selectAudioMediaOption(uint64_t contextId, uint64_t index)
565 {
566     m_page->send(Messages::WebVideoFullscreenManager::SelectAudioMediaOption(contextId, index), m_page->pageID());
567 }
568
569 void WebVideoFullscreenManagerProxy::selectLegibleMediaOption(uint64_t contextId, uint64_t index)
570 {
571     m_page->send(Messages::WebVideoFullscreenManager::SelectLegibleMediaOption(contextId, index), m_page->pageID());
572 }
573
574 void WebVideoFullscreenManagerProxy::fullscreenModeChanged(uint64_t contextId, WebCore::HTMLMediaElementEnums::VideoFullscreenMode mode)
575 {
576     m_page->send(Messages::WebVideoFullscreenManager::FullscreenModeChanged(contextId, mode), m_page->pageID());
577 }
578
579 bool WebVideoFullscreenManagerProxy::isVisible() const
580 {
581     return m_page->isViewVisible() && m_page->isInWindow();
582 }
583
584 void WebVideoFullscreenManagerProxy::fullscreenMayReturnToInline(uint64_t contextId)
585 {
586     bool isViewVisible = m_page->isViewVisible();
587     m_page->send(Messages::WebVideoFullscreenManager::FullscreenMayReturnToInline(contextId, isViewVisible), m_page->pageID());
588 }
589
590 #endif
591
592 } // namespace WebKit
593
594 #endif // PLATFORM(IOS)