Remove all uses of deprecatedCharacters from WebKit2
[WebKit-https.git] / Source / WebKit2 / WebProcess / Plugins / Netscape / mac / NetscapePluginMac.mm
1 /*
2  * Copyright (C) 2010 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 "NetscapePlugin.h"
28
29 #if ENABLE(NETSCAPE_PLUGIN_API)
30
31 #import "NetscapeBrowserFuncs.h"
32 #import "PluginController.h"
33 #import "WKNPAPIPlugInContainerInternal.h"
34 #import "WebEvent.h"
35 #import <Carbon/Carbon.h>
36 #import <WebCore/GraphicsContext.h>
37 #import <WebCore/NotImplemented.h>
38 #import <WebKitSystemInterface.h>
39 #import <wtf/NeverDestroyed.h>
40
41 using namespace WebCore;
42
43 namespace WebKit {
44
45 #ifndef NP_NO_CARBON
46 static const double nullEventIntervalActive = 0.02;
47
48 static unsigned buttonStateFromLastMouseEvent;
49
50 #endif
51
52 NPError NetscapePlugin::setDrawingModel(NPDrawingModel drawingModel)
53 {
54     // The drawing model can only be set from NPP_New.
55     if (!m_inNPPNew)
56         return NPERR_GENERIC_ERROR;
57
58     switch (drawingModel) {
59 #ifndef NP_NO_QUICKDRAW
60         case NPDrawingModelQuickDraw:
61 #endif
62         case NPDrawingModelCoreGraphics:
63         case NPDrawingModelCoreAnimation:
64             m_drawingModel = drawingModel;
65             break;
66
67         default:
68             return NPERR_GENERIC_ERROR;
69     }
70
71     return NPERR_NO_ERROR;
72 }
73     
74 NPError NetscapePlugin::setEventModel(NPEventModel eventModel)
75 {
76     // The event model can only be set from NPP_New.
77     if (!m_inNPPNew)
78         return NPERR_GENERIC_ERROR;
79
80     switch (eventModel) {
81 #ifndef NP_NO_CARBON
82         case NPEventModelCarbon:
83 #endif
84         case NPEventModelCocoa:
85             m_eventModel = eventModel;
86             break;
87
88         default:
89             return NPERR_GENERIC_ERROR;
90     }
91     
92     return NPERR_NO_ERROR;
93 }
94
95 bool NetscapePlugin::getScreenTransform(NPCoordinateSpace sourceSpace, AffineTransform& transform)
96 {
97     ASSERT(transform.isIdentity());
98
99     switch (sourceSpace) {
100         case NPCoordinateSpacePlugin: {
101             transform.translate(m_windowFrameInScreenCoordinates.x(), m_windowFrameInScreenCoordinates.y());
102             transform.translate(m_viewFrameInWindowCoordinates.x(), m_viewFrameInWindowCoordinates.height() + m_viewFrameInWindowCoordinates.y());
103             transform.flipY();
104             transform *= m_pluginToRootViewTransform;
105             return true;
106         }
107
108         case NPCoordinateSpaceWindow: {
109             transform.translate(m_windowFrameInScreenCoordinates.x(), m_windowFrameInScreenCoordinates.y());
110             return true;
111         }
112
113         case NPCoordinateSpaceFlippedWindow: {
114             transform.translate(m_windowFrameInScreenCoordinates.x(), m_windowFrameInScreenCoordinates.height() + m_windowFrameInScreenCoordinates.y());
115             transform.flipY();
116             return true;
117         }
118
119         case NPCoordinateSpaceScreen: {
120             // Nothing to do.
121             return true;
122         }
123
124         case NPCoordinateSpaceFlippedScreen: {
125             double screenHeight = [(NSScreen *)[[NSScreen screens] objectAtIndex:0] frame].size.height;
126             transform.translate(0, screenHeight);
127             transform.flipY();
128             return true;
129         }
130
131         default:
132             return false;
133     }
134 }
135
136 NPBool NetscapePlugin::convertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double& destX, double& destY, NPCoordinateSpace destSpace)
137 {
138     AffineTransform sourceTransform;
139     if (!getScreenTransform(sourceSpace, sourceTransform))
140         return false;
141
142     AffineTransform destTransform;
143     if (!getScreenTransform(destSpace, destTransform))
144         return false;
145
146     if (!destTransform.isInvertible())
147         return false;
148
149     AffineTransform transform = destTransform.inverse() * sourceTransform;
150
151     FloatPoint destinationPoint = transform.mapPoint(FloatPoint(sourceX, sourceY));
152
153     destX = destinationPoint.x();
154     destY = destinationPoint.y();
155     return true;
156 }
157
158
159 NPError NetscapePlugin::popUpContextMenu(NPMenu* npMenu)
160 {
161     if (!m_currentMouseEvent)
162         return NPERR_GENERIC_ERROR;
163
164     double screenX, screenY;
165     if (!convertPoint(m_currentMouseEvent->data.mouse.pluginX, m_currentMouseEvent->data.mouse.pluginY, NPCoordinateSpacePlugin, screenX, screenY, NPCoordinateSpaceScreen))
166         ASSERT_NOT_REACHED();
167
168     WKPopupContextMenu(reinterpret_cast<NSMenu *>(npMenu), NSMakePoint(screenX, screenY));
169     return NPERR_NO_ERROR;
170 }
171
172 mach_port_t NetscapePlugin::compositingRenderServerPort()
173 {
174 #if HAVE(OUT_OF_PROCESS_LAYER_HOSTING)
175     if (m_layerHostingMode == LayerHostingMode::OutOfProcess)
176         return MACH_PORT_NULL;
177 #endif
178
179     return controller()->compositingRenderServerPort();
180 }
181
182 void NetscapePlugin::openPluginPreferencePane()
183 {
184     controller()->openPluginPreferencePane();
185 }
186
187 WKNPAPIPlugInContainer* NetscapePlugin::plugInContainer()
188 {
189     if (!m_plugInContainer)
190         m_plugInContainer = adoptNS([[WKNPAPIPlugInContainer alloc] _initWithNetscapePlugin:this]);
191
192     return m_plugInContainer.get();
193 }
194
195 #ifndef NP_NO_CARBON
196 typedef HashMap<WindowRef, NetscapePlugin*> WindowMap;
197
198 static WindowMap& windowMap()
199 {
200     static NeverDestroyed<WindowMap> windowMap;
201     return windowMap;
202 }
203 #endif
204
205 static void NSException_release(id, SEL)
206 {
207     // Do nothing.
208 }
209
210 void NetscapePlugin::platformPreInitialize()
211 {
212     if (m_pluginModule->pluginQuirks().contains(PluginQuirks::LeakAllThrownNSExceptions)) {
213         // Patch -[NSException release] to not release the object.
214         static dispatch_once_t once;
215         dispatch_once(&once, ^{
216             Class exceptionClass = [NSException class];
217             Method exceptionReleaseMethod = class_getInstanceMethod(exceptionClass, @selector(release));
218             class_replaceMethod(exceptionClass, @selector(release), reinterpret_cast<IMP>(NSException_release), method_getTypeEncoding(exceptionReleaseMethod));
219         });
220     }
221 }
222
223 bool NetscapePlugin::platformPostInitialize()
224 {
225     if (m_drawingModel == static_cast<NPDrawingModel>(-1)) {
226 #ifndef NP_NO_QUICKDRAW
227         // Default to QuickDraw if the plugin did not specify a drawing model.
228         m_drawingModel = NPDrawingModelQuickDraw;
229 #else
230         // QuickDraw is not available, so we can't default to it. Instead, default to CoreGraphics.
231         m_drawingModel = NPDrawingModelCoreGraphics;
232 #endif
233     }
234     
235     if (m_eventModel == static_cast<NPEventModel>(-1)) {
236         // If the plug-in did not specify a drawing model we default to Carbon when it is available.
237 #ifndef NP_NO_CARBON
238         m_eventModel = NPEventModelCarbon;
239 #else
240         m_eventModel = NPEventModelCocoa;
241 #endif // NP_NO_CARBON
242     }
243
244 #if !defined(NP_NO_CARBON) && !defined(NP_NO_QUICKDRAW)
245     // The CA drawing model does not work with the Carbon event model.
246     if (m_drawingModel == NPDrawingModelCoreAnimation && m_eventModel == NPEventModelCarbon)
247         return false;
248     
249     // The Cocoa event model does not work with the QuickDraw drawing model.
250     if (m_eventModel == NPEventModelCocoa && m_drawingModel == NPDrawingModelQuickDraw)
251         return false;
252 #endif
253     
254 #ifndef NP_NO_QUICKDRAW
255     // Right now we don't support the QuickDraw drawing model at all
256     if (m_drawingModel == NPDrawingModelQuickDraw &&
257         !m_pluginModule->pluginQuirks().contains(PluginQuirks::AllowHalfBakedQuickDrawSupport))
258         return false;
259 #endif
260
261     updatePluginLayer();
262
263 #ifndef NP_NO_CARBON
264     if (m_eventModel == NPEventModelCarbon) {
265         // Initialize the fake Carbon window.
266         ::Rect bounds = { 0, 0, 0, 0 };
267         CreateNewWindow(kDocumentWindowClass, kWindowNoTitleBarAttribute, &bounds, reinterpret_cast<WindowRef*>(&m_npCGContext.window));
268         ASSERT(m_npCGContext.window);
269
270         // FIXME: Disable the backing store.
271
272         m_npWindow.window = &m_npCGContext;
273
274         ASSERT(!windowMap().contains(windowRef()));
275         windowMap().set(windowRef(), this);
276
277         // Start the null event timer.
278         // FIXME: Throttle null events when the plug-in isn't visible on screen.
279         m_nullEventTimer.startRepeating(nullEventIntervalActive);
280     }
281 #endif
282
283     return true;
284 }
285
286 void NetscapePlugin::platformDestroy()
287 {
288     [m_plugInContainer _invalidate];
289
290 #ifndef NP_NO_CARBON
291     if (m_eventModel == NPEventModelCarbon) {
292         if (WindowRef window = windowRef()) {
293             // Destroy the fake Carbon window.
294             DisposeWindow(window);
295
296             ASSERT(windowMap().contains(window));
297             windowMap().remove(window);
298         }
299
300         // Stop the null event timer.
301         m_nullEventTimer.stop();
302     }
303 #endif
304 }
305
306 bool NetscapePlugin::platformInvalidate(const IntRect&)
307 {
308     // NPN_InvalidateRect is just a no-op in the Core Animation drawing model.
309     if (m_drawingModel == NPDrawingModelCoreAnimation)
310         return true;
311
312     return false;
313 }
314
315 void NetscapePlugin::platformGeometryDidChange()
316 {
317     switch (m_eventModel) {
318     case NPEventModelCocoa:
319         // Nothing to do
320         break;
321 #ifndef NP_NO_CARBON
322     case NPEventModelCarbon:
323         updateFakeWindowBounds();
324         break;
325 #endif
326     default:
327         ASSERT_NOT_REACHED();
328     }
329 }
330
331 void NetscapePlugin::platformVisibilityDidChange()
332 {
333     // FIXME: Implement this. <http://webkit.org/b/44368>.
334     notImplemented();
335 }
336
337 static inline NPCocoaEvent initializeEvent(NPCocoaEventType type)
338 {
339     NPCocoaEvent event;
340     
341     event.type = type;
342     event.version = 0;
343     
344     return event;
345 }
346
347 #ifndef NP_NO_CARBON
348 NetscapePlugin* NetscapePlugin::netscapePluginFromWindow(WindowRef windowRef)
349 {
350     return windowMap().get(windowRef);
351 }
352
353 WindowRef NetscapePlugin::windowRef() const
354 {
355     ASSERT(m_eventModel == NPEventModelCarbon);
356
357     return reinterpret_cast<WindowRef>(m_npCGContext.window);
358 }
359
360 void NetscapePlugin::updateFakeWindowBounds()
361 {
362     double screenX, screenY;
363     bool didConvert = convertPoint(0, 0, NPCoordinateSpacePlugin, screenX, screenY, NPCoordinateSpaceFlippedScreen);
364     ASSERT_UNUSED(didConvert, didConvert);
365     
366     Rect bounds;
367     bounds.top = screenY;
368     bounds.left = screenX;
369     bounds.bottom = screenY + m_pluginSize.height();
370     bounds.right = screenX + m_pluginSize.width();
371     
372     ::SetWindowBounds(windowRef(), kWindowStructureRgn, &bounds);
373 }
374
375 unsigned NetscapePlugin::buttonState()
376 {
377     return buttonStateFromLastMouseEvent;
378 }
379
380 static inline EventRecord initializeEventRecord(EventKind eventKind)
381 {
382     EventRecord eventRecord;
383
384     eventRecord.what = eventKind;
385     eventRecord.message = 0;
386     eventRecord.when = TickCount();
387     eventRecord.where = Point();
388     eventRecord.modifiers = 0;
389
390     return eventRecord;
391 }
392
393 static bool anyMouseButtonIsDown(const WebEvent& event)
394 {
395     if (event.type() == WebEvent::MouseDown)
396         return true;
397
398     if (event.type() == WebEvent::MouseMove && static_cast<const WebMouseEvent&>(event).button() != WebMouseEvent::NoButton)
399         return true;
400
401     return false;
402 }
403
404 static bool rightMouseButtonIsDown(const WebEvent& event)
405 {
406     if (event.type() == WebEvent::MouseDown && static_cast<const WebMouseEvent&>(event).button() == WebMouseEvent::RightButton)
407         return true;
408     
409     if (event.type() == WebEvent::MouseMove && static_cast<const WebMouseEvent&>(event).button() == WebMouseEvent::RightButton)
410         return true;
411     
412     return false;
413 }
414
415 static EventModifiers modifiersForEvent(const WebEvent& event)
416 {
417     EventModifiers modifiers = 0;
418
419     // We only want to set the btnState if a mouse button is _not_ down.
420     if (!anyMouseButtonIsDown(event))
421         modifiers |= btnState;
422
423     if (event.metaKey())
424         modifiers |= cmdKey;
425
426     if (event.shiftKey())
427         modifiers |= shiftKey;
428
429     if (event.altKey())
430         modifiers |= optionKey;
431
432     // Set controlKey if the control key is down or the right mouse button is down.
433     if (event.controlKey() || rightMouseButtonIsDown(event))
434         modifiers |= controlKey;
435
436     return modifiers;
437 }
438
439 #endif
440
441 void NetscapePlugin::platformPaint(GraphicsContext* context, const IntRect& dirtyRect, bool isSnapshot)
442 {
443     CGContextRef platformContext = context->platformContext();
444
445     switch (m_eventModel) {
446         case NPEventModelCocoa: {
447             // Don't send draw events when we're using the Core Animation drawing model.
448             if (!isSnapshot && m_drawingModel == NPDrawingModelCoreAnimation)
449                 return;
450
451             NPCocoaEvent event = initializeEvent(NPCocoaEventDrawRect);
452
453             event.data.draw.context = platformContext;
454             event.data.draw.x = dirtyRect.x();
455             event.data.draw.y = dirtyRect.y();
456             event.data.draw.width = dirtyRect.width();
457             event.data.draw.height = dirtyRect.height();
458             
459             NPP_HandleEvent(&event);
460             break;
461         }
462
463 #ifndef NP_NO_CARBON
464         case NPEventModelCarbon: {
465             if (platformContext != m_npCGContext.context) {
466                 m_npCGContext.context = platformContext;
467                 callSetWindow();
468             }
469
470             EventRecord event = initializeEventRecord(updateEvt);
471             event.message = reinterpret_cast<unsigned long>(windowRef());
472             
473             NPP_HandleEvent(&event);
474             break;            
475         }
476 #endif
477
478         default:
479             ASSERT_NOT_REACHED();
480     }
481 }
482
483 static uint32_t modifierFlags(const WebEvent& event)
484 {
485     uint32_t modifiers = 0;
486
487     if (event.shiftKey())
488         modifiers |= NSShiftKeyMask;
489     if (event.controlKey())
490         modifiers |= NSControlKeyMask;
491     if (event.altKey())
492         modifiers |= NSAlternateKeyMask;
493     if (event.metaKey())
494         modifiers |= NSCommandKeyMask;
495     
496     return modifiers;
497 }
498     
499 static int32_t buttonNumber(WebMouseEvent::Button button)
500 {
501     switch (button) {
502     case WebMouseEvent::NoButton:
503     case WebMouseEvent::LeftButton:
504         return 0;
505     case WebMouseEvent::RightButton:
506         return 1;
507     case WebMouseEvent::MiddleButton:
508         return 2;
509     }
510
511     ASSERT_NOT_REACHED();
512     return -1;
513 }
514
515 static void fillInCocoaEventFromMouseEvent(NPCocoaEvent& event, const WebMouseEvent& mouseEvent, const WebCore::IntPoint& eventPositionInPluginCoordinates)
516 {
517     event.data.mouse.modifierFlags = modifierFlags(mouseEvent);
518     event.data.mouse.pluginX = eventPositionInPluginCoordinates.x();
519     event.data.mouse.pluginY = eventPositionInPluginCoordinates.y();
520     event.data.mouse.buttonNumber = buttonNumber(mouseEvent.button());
521     event.data.mouse.clickCount = mouseEvent.clickCount();
522     event.data.mouse.deltaX = mouseEvent.deltaX();
523     event.data.mouse.deltaY = mouseEvent.deltaY();
524     event.data.mouse.deltaZ = mouseEvent.deltaZ();
525 }
526     
527 static NPCocoaEvent initializeMouseEvent(const WebMouseEvent& mouseEvent, const WebCore::IntPoint& eventPositionInPluginCoordinates)
528 {
529     NPCocoaEventType eventType;
530
531     switch (mouseEvent.type()) {
532     case WebEvent::MouseDown:
533         eventType = NPCocoaEventMouseDown;
534         break;
535     case WebEvent::MouseUp:
536         eventType = NPCocoaEventMouseUp;
537         break;
538     case WebEvent::MouseMove:
539         if (mouseEvent.button() == WebMouseEvent::NoButton)
540             eventType = NPCocoaEventMouseMoved;
541         else
542             eventType = NPCocoaEventMouseDragged;
543         break;
544     default:
545         ASSERT_NOT_REACHED();
546         return NPCocoaEvent();
547     }
548
549     NPCocoaEvent event = initializeEvent(eventType);
550     fillInCocoaEventFromMouseEvent(event, mouseEvent, eventPositionInPluginCoordinates);
551     return event;
552 }
553
554 bool NetscapePlugin::platformHandleMouseEvent(const WebMouseEvent& mouseEvent)
555 {
556     IntPoint eventPositionInPluginCoordinates;
557     if (!convertFromRootView(mouseEvent.position(), eventPositionInPluginCoordinates))
558         return true;
559
560     switch (m_eventModel) {
561         case NPEventModelCocoa: {
562             NPCocoaEvent event = initializeMouseEvent(mouseEvent, eventPositionInPluginCoordinates);
563
564             NPCocoaEvent* previousMouseEvent = m_currentMouseEvent;
565             m_currentMouseEvent = &event;
566
567             // Protect against NPP_HandleEvent causing the plug-in to be destroyed, since we
568             // access m_currentMouseEvent afterwards.
569             Ref<NetscapePlugin> protect(*this);
570
571             NPP_HandleEvent(&event);
572
573             m_currentMouseEvent = previousMouseEvent;
574
575             // Some plug-ins return false even if the mouse event has been handled.
576             // This leads to bugs such as <rdar://problem/9167611>. Work around this
577             // by always returning true.
578             return true;
579         }
580
581 #ifndef NP_NO_CARBON
582         case NPEventModelCarbon: {
583             EventKind eventKind = nullEvent;
584
585             switch (mouseEvent.type()) {
586             case WebEvent::MouseDown:
587                 eventKind = mouseDown;
588                 buttonStateFromLastMouseEvent |= (1 << buttonNumber(mouseEvent.button()));
589                 break;
590             case WebEvent::MouseUp:
591                 eventKind = mouseUp;
592                 buttonStateFromLastMouseEvent &= ~(1 << buttonNumber(mouseEvent.button()));
593                 break;
594             case WebEvent::MouseMove:
595                 eventKind = nullEvent;
596                 break;
597             default:
598                 ASSERT_NOT_REACHED();
599             }
600
601             EventRecord event = initializeEventRecord(eventKind);
602             event.modifiers = modifiersForEvent(mouseEvent);
603
604             double globalX;
605             double globalY;
606             if (!convertPoint(eventPositionInPluginCoordinates.x(), eventPositionInPluginCoordinates.y(), NPCoordinateSpacePlugin, globalX, globalY, NPCoordinateSpaceFlippedScreen))
607                 ASSERT_NOT_REACHED();
608
609             event.where.h = globalX;
610             event.where.v = globalY;
611
612             NPP_HandleEvent(&event);
613
614             // Some plug-ins return false even if the mouse event has been handled.
615             // This leads to bugs such as <rdar://problem/9167611>. Work around this
616             // by always returning true.
617             return true;
618         }
619 #endif
620
621         default:
622             ASSERT_NOT_REACHED();
623     }
624
625     return false;
626 }
627
628 bool NetscapePlugin::platformHandleWheelEvent(const WebWheelEvent& wheelEvent)
629 {
630     switch (m_eventModel) {
631         case NPEventModelCocoa: {
632             IntPoint eventPositionInPluginCoordinates;
633             if (!convertFromRootView(wheelEvent.position(), eventPositionInPluginCoordinates))
634                 return true;
635
636             NPCocoaEvent event = initializeEvent(NPCocoaEventScrollWheel);
637             
638             event.data.mouse.modifierFlags = modifierFlags(wheelEvent);
639             event.data.mouse.pluginX = eventPositionInPluginCoordinates.x();
640             event.data.mouse.pluginY = eventPositionInPluginCoordinates.y();
641             event.data.mouse.buttonNumber = 0;
642             event.data.mouse.clickCount = 0;
643             event.data.mouse.deltaX = wheelEvent.delta().width();
644             event.data.mouse.deltaY = wheelEvent.delta().height();
645             event.data.mouse.deltaZ = 0;
646             return NPP_HandleEvent(&event);
647         }
648
649 #ifndef NP_NO_CARBON
650         case NPEventModelCarbon:
651             // Carbon doesn't have wheel events.
652             break;
653 #endif
654
655         default:
656             ASSERT_NOT_REACHED();
657     }
658
659     return false;
660 }
661
662 bool NetscapePlugin::platformHandleMouseEnterEvent(const WebMouseEvent& mouseEvent)
663 {
664     switch (m_eventModel) {
665         case NPEventModelCocoa: {
666             NPCocoaEvent event = initializeEvent(NPCocoaEventMouseEntered);
667             
668             fillInCocoaEventFromMouseEvent(event, mouseEvent, IntPoint());
669             return NPP_HandleEvent(&event);
670         }
671
672 #ifndef NP_NO_CARBON
673         case NPEventModelCarbon: {
674             EventRecord eventRecord = initializeEventRecord(NPEventType_AdjustCursorEvent);
675             eventRecord.modifiers = modifiersForEvent(mouseEvent);
676             
677             return NPP_HandleEvent(&eventRecord);
678         }
679 #endif
680
681         default:
682             ASSERT_NOT_REACHED();
683     }
684
685     return false;
686 }
687
688 bool NetscapePlugin::platformHandleMouseLeaveEvent(const WebMouseEvent& mouseEvent)
689 {
690     switch (m_eventModel) {
691         case NPEventModelCocoa: {
692             NPCocoaEvent event = initializeEvent(NPCocoaEventMouseExited);
693             
694             fillInCocoaEventFromMouseEvent(event, mouseEvent, IntPoint());
695             return NPP_HandleEvent(&event);
696         }
697
698 #ifndef NP_NO_CARBON
699         case NPEventModelCarbon: {
700             EventRecord eventRecord = initializeEventRecord(NPEventType_AdjustCursorEvent);
701             eventRecord.modifiers = modifiersForEvent(mouseEvent);
702             
703             return NPP_HandleEvent(&eventRecord);
704         }
705 #endif
706
707         default:
708             ASSERT_NOT_REACHED();
709     }
710
711     return false;
712 }
713
714 static unsigned modifierFlags(const WebKeyboardEvent& keyboardEvent)
715 {
716     unsigned modifierFlags = 0;
717
718     if (keyboardEvent.capsLockKey())
719         modifierFlags |= NSAlphaShiftKeyMask;
720     if (keyboardEvent.shiftKey())
721         modifierFlags |= NSShiftKeyMask;
722     if (keyboardEvent.controlKey())
723         modifierFlags |= NSControlKeyMask;
724     if (keyboardEvent.altKey())
725         modifierFlags |= NSAlternateKeyMask;
726     if (keyboardEvent.metaKey())
727         modifierFlags |= NSCommandKeyMask;
728
729     return modifierFlags;
730 }
731
732 static bool isFlagsChangedEvent(const WebKeyboardEvent& keyboardEvent)
733 {
734     switch (keyboardEvent.nativeVirtualKeyCode()) {
735     case 54: // Right Command
736     case 55: // Left Command
737
738     case 57: // Capslock
739
740     case 56: // Left Shift
741     case 60: // Right Shift
742
743     case 58: // Left Alt
744     case 61: // Right Alt
745             
746     case 59: // Left Ctrl
747     case 62: // Right Ctrl
748         return true;
749     }
750
751     return false;
752 }
753
754 static NPCocoaEvent initializeKeyboardEvent(const WebKeyboardEvent& keyboardEvent)
755 {
756     NPCocoaEventType eventType;
757
758     if (isFlagsChangedEvent(keyboardEvent))
759         eventType = NPCocoaEventFlagsChanged;
760     else {
761         switch (keyboardEvent.type()) {
762             case WebEvent::KeyDown:
763                 eventType = NPCocoaEventKeyDown;
764                 break;
765             case WebEvent::KeyUp:
766                 eventType = NPCocoaEventKeyUp;
767                 break;
768             default:
769                 ASSERT_NOT_REACHED();
770                 return NPCocoaEvent();
771         }
772     }
773
774     NPCocoaEvent event = initializeEvent(eventType);
775     event.data.key.modifierFlags = modifierFlags(keyboardEvent);
776     event.data.key.characters = reinterpret_cast<NPNSString*>(static_cast<NSString*>(keyboardEvent.text()));
777     event.data.key.charactersIgnoringModifiers = reinterpret_cast<NPNSString*>(static_cast<NSString*>(keyboardEvent.unmodifiedText()));
778     event.data.key.isARepeat = keyboardEvent.isAutoRepeat();
779     event.data.key.keyCode = keyboardEvent.nativeVirtualKeyCode();
780
781     return event;
782 }
783
784 bool NetscapePlugin::platformHandleKeyboardEvent(const WebKeyboardEvent& keyboardEvent)
785 {
786     bool handled = false;
787
788     switch (m_eventModel) {
789     case NPEventModelCocoa: {
790         if (keyboardEvent.type() == WebEvent::KeyDown) {
791             m_hasHandledAKeyDownEvent = true;
792
793             if (!m_pluginWantsLegacyCocoaTextInput && m_isComplexTextInputEnabled && !keyboardEvent.isAutoRepeat()) {
794                 // When complex text is enabled in the new model, the plug-in should never
795                 // receive any key down or key up events until the composition is complete.
796                 m_ignoreNextKeyUpEventCounter++;
797                 return true;
798             }
799         } else if (keyboardEvent.type() == WebEvent::KeyUp && m_ignoreNextKeyUpEventCounter) {
800             m_ignoreNextKeyUpEventCounter--;
801             return true;
802         }
803
804         NPCocoaEvent event = initializeKeyboardEvent(keyboardEvent);
805         int16_t returnValue = NPP_HandleEvent(&event);
806         handled = returnValue;
807
808         if (!m_pluginWantsLegacyCocoaTextInput) {
809             if (event.type == NPCocoaEventKeyDown && returnValue == kNPEventStartIME) {
810                 if (!keyboardEvent.isAutoRepeat())
811                     m_ignoreNextKeyUpEventCounter++;
812                 setComplexTextInputEnabled(true);
813             }
814         }
815
816         break;
817     }
818
819 #ifndef NP_NO_CARBON
820     case NPEventModelCarbon: {
821         EventKind eventKind = nullEvent;
822
823         switch (keyboardEvent.type()) {
824         case WebEvent::KeyDown:
825             eventKind = keyboardEvent.isAutoRepeat() ? autoKey : keyDown;
826             break;
827         case WebEvent::KeyUp:
828             eventKind = keyUp;
829             break;
830         default:
831             ASSERT_NOT_REACHED();
832         }
833
834         EventRecord event = initializeEventRecord(eventKind);
835         event.modifiers = modifiersForEvent(keyboardEvent);
836         event.message = keyboardEvent.nativeVirtualKeyCode() << 8 | keyboardEvent.macCharCode();
837         handled = NPP_HandleEvent(&event);
838         break;
839     }
840 #endif
841
842     default:
843         ASSERT_NOT_REACHED();
844     }
845
846     // Most plug-ins simply return true for all keyboard events, even those that aren't handled.
847     // This leads to bugs such as <rdar://problem/8740926>. We work around this by returning false
848     // if the keyboard event has the command modifier pressed.
849     // However, for command-A (the shortcurt for 'Select All' we will always return true, since we don't
850     // want the entire page to be selected if the focus is in a plug-in text field (see <rdar://problem/9309903>).
851     if (keyboardEvent.metaKey()) {
852         if (keyboardEvent.text() == "a")
853             return true;
854
855         return false;
856     }
857
858     return handled;
859 }
860
861 void NetscapePlugin::platformSetFocus(bool hasFocus)
862 {
863     m_pluginHasFocus = hasFocus;
864     pluginFocusOrWindowFocusChanged();
865
866     switch (m_eventModel) {
867         case NPEventModelCocoa: {
868             NPCocoaEvent event = initializeEvent(NPCocoaEventFocusChanged);
869             
870             event.data.focus.hasFocus = hasFocus;
871             NPP_HandleEvent(&event);
872             break;
873         }
874
875 #ifndef NP_NO_CARBON
876         case NPEventModelCarbon: {
877             EventRecord event = initializeEventRecord(hasFocus ? NPEventType_GetFocusEvent : NPEventType_LoseFocusEvent);
878
879             NPP_HandleEvent(&event);
880             break;
881         }
882 #endif
883             
884         default:
885             ASSERT_NOT_REACHED();
886     }
887 }
888
889 bool NetscapePlugin::wantsPluginRelativeNPWindowCoordinates()
890 {
891     return true;
892 }
893
894 void NetscapePlugin::windowFocusChanged(bool hasFocus)
895 {
896     m_windowHasFocus = hasFocus;
897     pluginFocusOrWindowFocusChanged();
898
899     switch (m_eventModel) {
900         case NPEventModelCocoa: {
901             NPCocoaEvent event = initializeEvent(NPCocoaEventWindowFocusChanged);
902             
903             event.data.focus.hasFocus = hasFocus;
904             NPP_HandleEvent(&event);
905             break;
906         }
907         
908 #ifndef NP_NO_CARBON
909         case NPEventModelCarbon: {
910             HiliteWindow(windowRef(), hasFocus);
911             if (hasFocus)
912                 SetUserFocusWindow(windowRef());
913
914             EventRecord event = initializeEventRecord(activateEvt);
915             event.message = reinterpret_cast<unsigned long>(windowRef());
916             if (hasFocus)
917                 event.modifiers |= activeFlag;
918             
919             NPP_HandleEvent(&event);
920             break;
921         }
922 #endif
923
924         default:
925             ASSERT_NOT_REACHED();
926     }
927 }
928
929 void NetscapePlugin::windowAndViewFramesChanged(const IntRect& windowFrameInScreenCoordinates, const IntRect& viewFrameInWindowCoordinates)
930 {
931     m_windowFrameInScreenCoordinates = windowFrameInScreenCoordinates;
932     m_viewFrameInWindowCoordinates = viewFrameInWindowCoordinates;
933
934     switch (m_eventModel) {
935         case NPEventModelCocoa:
936             // Nothing to do.
937             break;
938
939 #ifndef NP_NO_CARBON
940         case NPEventModelCarbon:
941             updateFakeWindowBounds();
942             break;
943 #endif
944
945         default:
946             ASSERT_NOT_REACHED();
947     }
948 }
949     
950 void NetscapePlugin::windowVisibilityChanged(bool visible)
951 {
952     if (visible)
953         callSetWindow();
954     else
955         callSetWindowInvisible();
956 }
957
958 uint64_t NetscapePlugin::pluginComplexTextInputIdentifier() const
959 {
960     // Just return a dummy value; this is only called for in-process plug-ins, which we don't support on Mac.
961     return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this));
962 }
963
964
965 #ifndef NP_NO_CARBON
966 static bool convertStringToKeyCodes(StringView string, ScriptCode scriptCode, Vector<UInt8>& keyCodes)
967 {
968     // Create the mapping.
969     UnicodeMapping mapping;
970
971     if (GetTextEncodingFromScriptInfo(scriptCode, kTextLanguageDontCare, kTextRegionDontCare, &mapping.otherEncoding) != noErr)
972         return false;
973
974     mapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault, kTextEncodingDefaultVariant, kTextEncodingDefaultFormat);
975     mapping.mappingVersion = kUnicodeUseLatestMapping;
976
977     // Create the converter
978     UnicodeToTextInfo textInfo;
979
980     if (CreateUnicodeToTextInfo(&mapping, &textInfo) != noErr)
981         return false;
982
983     ByteCount inputLength = string.length() * sizeof(UniChar);
984     ByteCount inputRead;
985     ByteCount outputLength;
986     ByteCount maxOutputLength = string.length() * sizeof(UniChar);
987
988     Vector<UInt8> outputData(maxOutputLength);
989     OSStatus status = ConvertFromUnicodeToText(textInfo, inputLength, string.upconvertedCharacters(), kNilOptions, 0, 0, 0, 0, maxOutputLength, &inputRead, &outputLength, outputData.data());
990     
991     DisposeUnicodeToTextInfo(&textInfo);
992     
993     if (status != noErr)
994         return false;
995
996     keyCodes = std::move(outputData);
997     return true;
998 }
999 #endif
1000
1001 void NetscapePlugin::sendComplexTextInput(const String& textInput)
1002 {
1003     if (!m_pluginWantsLegacyCocoaTextInput) {
1004         // In the updated Cocoa text input spec, text input is disabled when the text input string has been sent
1005         // by the UI process. Since the UI process has also updated its state, we can just reset the variable here
1006         // instead of calling setComplexTextInputEnabled.
1007         m_isComplexTextInputEnabled = false;
1008
1009         // The UI process can also disable text input by sending an empty input string. In this case, we don't want
1010         // to send it to the plug-in.
1011         if (textInput.isNull())
1012             return;
1013     }
1014
1015     switch (m_eventModel) {
1016     case NPEventModelCocoa: {
1017         NPCocoaEvent event = initializeEvent(NPCocoaEventTextInput);
1018         event.data.text.text = reinterpret_cast<NPNSString*>(static_cast<NSString*>(textInput));
1019         NPP_HandleEvent(&event);
1020         break;
1021     }
1022 #ifndef NP_NO_CARBON
1023     case NPEventModelCarbon: {
1024         ScriptCode scriptCode = WKGetScriptCodeFromCurrentKeyboardInputSource();
1025         Vector<UInt8> keyCodes;
1026
1027         if (!convertStringToKeyCodes(textInput, scriptCode, keyCodes))
1028             return;
1029
1030         // Set the script code as the keyboard script. Normally Carbon does this whenever the input source changes.
1031         // However, this is only done for the process that has the keyboard focus. We cheat and do it here instead.
1032         SetScriptManagerVariable(smKeyScript, scriptCode);
1033         
1034         EventRecord event = initializeEventRecord(keyDown);
1035         event.modifiers = 0;
1036
1037         for (auto& keyCode : keyCodes) {
1038             event.message = keyCode;
1039             NPP_HandleEvent(&event);
1040         }
1041         break;
1042     }
1043 #endif
1044     default:
1045         ASSERT_NOT_REACHED();
1046     }
1047 }
1048
1049 void NetscapePlugin::setLayerHostingMode(LayerHostingMode layerHostingMode)
1050 {
1051     m_layerHostingMode = layerHostingMode;
1052
1053     // Tell the plug-in about the new compositing render server port. If it returns OK we'll ask it again for a new layer.
1054     mach_port_t port = NetscapePlugin::compositingRenderServerPort();
1055     if (NPP_SetValue(static_cast<NPNVariable>(WKNVCALayerRenderServerPort), &port) != NPERR_NO_ERROR)
1056         return;
1057
1058     m_pluginLayer = nullptr;
1059     updatePluginLayer();
1060 }
1061
1062 void NetscapePlugin::pluginFocusOrWindowFocusChanged()
1063 {
1064     bool pluginHasFocusAndWindowHasFocus = m_pluginHasFocus && m_windowHasFocus;
1065
1066     controller()->pluginFocusOrWindowFocusChanged(pluginHasFocusAndWindowHasFocus);
1067
1068     // In the updated Cocoa text input spec, the plug-in will enable complex text input
1069     // by returning kNPEventStartIME from it's NPCocoaEventKeyDown handler.
1070     if (!m_pluginWantsLegacyCocoaTextInput)
1071         return;
1072
1073     // In the old model, if the plug-in is focused, enable complex text input.
1074     setComplexTextInputEnabled(pluginHasFocusAndWindowHasFocus);
1075 }
1076
1077 void NetscapePlugin::setComplexTextInputEnabled(bool complexTextInputEnabled)
1078 {
1079     if (m_isComplexTextInputEnabled == complexTextInputEnabled)
1080         return;
1081
1082     m_isComplexTextInputEnabled = complexTextInputEnabled;
1083
1084     PluginComplexTextInputState complexTextInputState = PluginComplexTextInputDisabled;
1085     if (m_isComplexTextInputEnabled)
1086         complexTextInputState = m_pluginWantsLegacyCocoaTextInput ? PluginComplexTextInputEnabledLegacy : PluginComplexTextInputEnabled;
1087
1088     controller()->setComplexTextInputState(complexTextInputState);
1089 }
1090
1091 PlatformLayer* NetscapePlugin::pluginLayer()
1092 {
1093     return static_cast<PlatformLayer*>(m_pluginLayer.get());
1094 }
1095
1096 static void makeCGLPresentLayerOpaque(CALayer *pluginRootLayer)
1097 {
1098     // We look for a layer that's the only sublayer of the root layer that is an instance
1099     // of the CGLPresentLayer class which in turn is a subclass of CAOpenGLLayer and make
1100     // it opaque if all these conditions hold.
1101
1102     NSArray *sublayers = [pluginRootLayer sublayers];
1103     if ([sublayers count] != 1)
1104         return;
1105
1106     Class cglPresentLayerClass = NSClassFromString(@"CGLPresentLayer");
1107     if (![cglPresentLayerClass isSubclassOfClass:[CAOpenGLLayer class]])
1108         return;
1109
1110     CALayer *layer = [sublayers objectAtIndex:0];
1111     if (![layer isKindOfClass:cglPresentLayerClass])
1112         return;
1113
1114     [layer setOpaque:YES];
1115 }
1116
1117 void NetscapePlugin::updatePluginLayer()
1118 {
1119     if (m_drawingModel != NPDrawingModelCoreAnimation)
1120         return;
1121
1122     void* value = 0;
1123
1124     // Get the Core Animation layer.
1125     if (NPP_GetValue(NPPVpluginCoreAnimationLayer, &value) != NPERR_NO_ERROR)
1126         return;
1127
1128     if (!value)
1129         return;
1130
1131     ASSERT(!m_pluginLayer);
1132
1133     // The original Core Animation drawing model required that plug-ins pass a retained layer
1134     // to the browser, which the browser would then adopt. However, the final spec changed this
1135     // (See https://wiki.mozilla.org/NPAPI:CoreAnimationDrawingModel for more information)
1136     // after a version of WebKit1 with the original implementation had shipped, but that now means
1137     // that any plug-ins that expect the WebKit1 behavior would leak the CALayer.
1138     // For plug-ins that we know return retained layers, we have the ReturnsRetainedCoreAnimationLayer
1139     // plug-in quirk. Plug-ins can also check for whether the browser expects a non-retained layer to
1140     // be returned by using NPN_GetValue and pass the WKNVExpectsNonretainedLayer parameter.
1141     // https://bugs.webkit.org/show_bug.cgi?id=58282 describes the bug where WebKit expects retained layers.
1142     if (m_pluginReturnsNonretainedLayer)
1143         m_pluginLayer = reinterpret_cast<CALayer *>(value);
1144     else
1145         m_pluginLayer = adoptNS(reinterpret_cast<CALayer *>(value));
1146
1147     if (m_pluginModule->pluginQuirks().contains(PluginQuirks::MakeOpaqueUnlessTransparentSilverlightBackgroundAttributeExists) &&
1148         !m_isTransparent)
1149         makeCGLPresentLayerOpaque(m_pluginLayer.get());
1150 }
1151
1152 #ifndef NP_NO_CARBON
1153 void NetscapePlugin::nullEventTimerFired()
1154 {
1155     EventRecord event = initializeEventRecord(nullEvent);
1156
1157     event.message = 0;
1158     CGPoint mousePosition;
1159     HIGetMousePosition(kHICoordSpaceScreenPixel, 0, &mousePosition);
1160     event.where.h = mousePosition.x;
1161     event.where.v = mousePosition.y;
1162
1163     event.modifiers = GetCurrentKeyModifiers();
1164     if (!Button())
1165         event.modifiers |= btnState;
1166
1167     NPP_HandleEvent(&event);
1168 }
1169 #endif
1170
1171 } // namespace WebKit
1172
1173 #endif // ENABLE(NETSCAPE_PLUGIN_API)