1 | /* |
2 | * Copyright (C) 2010-2018 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 "NetscapeBrowserFuncs.h" |
28 | |
29 | #if ENABLE(NETSCAPE_PLUGIN_API) |
30 | |
31 | #include "NPRuntimeUtilities.h" |
32 | #include "NetscapePlugin.h" |
33 | #include "PluginController.h" |
34 | #include <WebCore/HTTPHeaderMap.h> |
35 | #include <WebCore/HTTPHeaderNames.h> |
36 | #include <WebCore/IdentifierRep.h> |
37 | #include <WebCore/NotImplemented.h> |
38 | #include <WebCore/ProtectionSpace.h> |
39 | #include <WebCore/SharedBuffer.h> |
40 | #include <memory> |
41 | #include <utility> |
42 | #include <wtf/text/StringBuilder.h> |
43 | |
44 | #if PLATFORM(COCOA) |
45 | #include <wtf/MachSendRight.h> |
46 | #endif |
47 | |
48 | #if PLATFORM(X11) |
49 | #include <WebCore/PlatformDisplayX11.h> |
50 | #endif |
51 | |
52 | namespace WebKit { |
53 | using namespace WebCore; |
54 | |
55 | // Helper class for delaying destruction of a plug-in. |
56 | class PluginDestructionProtector { |
57 | public: |
58 | explicit PluginDestructionProtector(NetscapePlugin* plugin) |
59 | { |
60 | if (plugin) |
61 | m_protector = std::make_unique<PluginController::PluginDestructionProtector>(static_cast<Plugin*>(plugin)->controller()); |
62 | } |
63 | |
64 | private: |
65 | std::unique_ptr<PluginController::PluginDestructionProtector> m_protector; |
66 | }; |
67 | |
68 | static bool startsWithBlankLine(const char* bytes, unsigned length) |
69 | { |
70 | return length > 0 && bytes[0] == '\n'; |
71 | } |
72 | |
73 | static int locationAfterFirstBlankLine(const char* bytes, unsigned length) |
74 | { |
75 | for (unsigned i = 0; i < length - 4; i++) { |
76 | // Support for Acrobat. It sends "\n\n". |
77 | if (bytes[i] == '\n' && bytes[i + 1] == '\n') |
78 | return i + 2; |
79 | |
80 | // Returns the position after 2 CRLF's or 1 CRLF if it is the first line. |
81 | if (bytes[i] == '\r' && bytes[i + 1] == '\n') { |
82 | i += 2; |
83 | if (i == 2) |
84 | return i; |
85 | |
86 | if (bytes[i] == '\n') { |
87 | // Support for Director. It sends "\r\n\n" (3880387). |
88 | return i + 1; |
89 | } |
90 | |
91 | if (bytes[i] == '\r' && bytes[i + 1] == '\n') { |
92 | // Support for Flash. It sends "\r\n\r\n" (3758113). |
93 | return i + 2; |
94 | } |
95 | } |
96 | } |
97 | |
98 | return -1; |
99 | } |
100 | |
101 | static const char* findEndOfLine(const char* bytes, unsigned length) |
102 | { |
103 | // According to the HTTP specification EOL is defined as |
104 | // a CRLF pair. Unfortunately, some servers will use LF |
105 | // instead. Worse yet, some servers will use a combination |
106 | // of both (e.g. <header>CRLFLF<body>), so findEOL needs |
107 | // to be more forgiving. It will now accept CRLF, LF or |
108 | // CR. |
109 | // |
110 | // It returns 0 if EOLF is not found or it will return |
111 | // a pointer to the first terminating character. |
112 | for (unsigned i = 0; i < length; i++) { |
113 | if (bytes[i] == '\n') |
114 | return bytes + i; |
115 | if (bytes[i] == '\r') { |
116 | // Check to see if spanning buffer bounds |
117 | // (CRLF is across reads). If so, wait for |
118 | // next read. |
119 | if (i + 1 == length) |
120 | break; |
121 | |
122 | return bytes + i; |
123 | } |
124 | } |
125 | |
126 | return 0; |
127 | } |
128 | |
129 | static String (const String& name) |
130 | { |
131 | bool capitalizeCharacter = true; |
132 | StringBuilder result; |
133 | for (unsigned i = 0; i < name.length(); i++) { |
134 | result.append(capitalizeCharacter ? toASCIIUpper(name[i]) : toASCIILower(name[i])); |
135 | if (name[i] == '-') |
136 | capitalizeCharacter = true; |
137 | else |
138 | capitalizeCharacter = false; |
139 | } |
140 | return result.toString(); |
141 | } |
142 | |
143 | static HTTPHeaderMap (const char* bytes, unsigned length) |
144 | { |
145 | String ; |
146 | HTTPHeaderMap ; |
147 | |
148 | // Loop over lines until we're past the header, or we can't find any more end-of-lines |
149 | while (const char* endOfLine = findEndOfLine(bytes, length)) { |
150 | const char* line = bytes; |
151 | int lineLength = endOfLine - bytes; |
152 | |
153 | // Move bytes to the character after the terminator as returned by findEndOfLine. |
154 | bytes = endOfLine + 1; |
155 | if ((*endOfLine == '\r') && (*bytes == '\n')) |
156 | bytes++; // Safe since findEndOfLine won't return a spanning CRLF. |
157 | |
158 | length -= (bytes - line); |
159 | if (!lineLength) { |
160 | // Blank line; we're at the end of the header |
161 | break; |
162 | } |
163 | |
164 | if (*line == ' ' || *line == '\t') { |
165 | // Continuation of the previous header |
166 | if (lastHeaderKey.isNull()) { |
167 | // malformed header; ignore it and continue |
168 | continue; |
169 | } |
170 | |
171 | // Merge the continuation of the previous header |
172 | String currentValue = headerFields.get(lastHeaderKey); |
173 | String newValue(line, lineLength); |
174 | |
175 | headerFields.set(lastHeaderKey, currentValue + newValue); |
176 | } else { |
177 | // Brand new header |
178 | const char* colon = line; |
179 | while (*colon != ':' && colon != endOfLine) |
180 | colon++; |
181 | |
182 | if (colon == endOfLine) { |
183 | // malformed header; ignore it and continue |
184 | continue; |
185 | } |
186 | |
187 | lastHeaderKey = capitalizeRFC822HeaderFieldName(String(line, colon - line)); |
188 | String value; |
189 | |
190 | for (colon++; colon != endOfLine; colon++) { |
191 | if (*colon != ' ' && *colon != '\t') |
192 | break; |
193 | } |
194 | if (colon == endOfLine) |
195 | value = emptyString(); |
196 | else |
197 | value = String(colon, endOfLine - colon); |
198 | |
199 | String oldValue = headerFields.get(lastHeaderKey); |
200 | if (!oldValue.isNull()) |
201 | value = oldValue + ", " + value; |
202 | |
203 | headerFields.set(lastHeaderKey, value); |
204 | } |
205 | } |
206 | |
207 | return headerFields; |
208 | } |
209 | |
210 | static NPError (bool isFile, const char *buffer, uint32_t length, bool , HTTPHeaderMap& , Vector<uint8_t>& bodyData) |
211 | { |
212 | RefPtr<SharedBuffer> fileContents; |
213 | const char* postBuffer = 0; |
214 | uint32_t postBufferSize = 0; |
215 | |
216 | if (isFile) { |
217 | fileContents = SharedBuffer::createWithContentsOfFile(String::fromUTF8(buffer)); |
218 | if (!fileContents) |
219 | return NPERR_FILE_NOT_FOUND; |
220 | |
221 | postBuffer = fileContents->data(); |
222 | postBufferSize = fileContents->size(); |
223 | |
224 | // FIXME: The NPAPI spec states that the file should be deleted here. |
225 | } else { |
226 | postBuffer = buffer; |
227 | postBufferSize = length; |
228 | } |
229 | |
230 | if (parseHeaders) { |
231 | if (startsWithBlankLine(postBuffer, postBufferSize)) { |
232 | postBuffer++; |
233 | postBufferSize--; |
234 | } else { |
235 | int location = locationAfterFirstBlankLine(postBuffer, postBufferSize); |
236 | if (location != -1) { |
237 | // If the blank line is somewhere in the middle of the buffer, everything before is the header |
238 | headerFields = parseRFC822HeaderFields(postBuffer, location); |
239 | unsigned dataLength = postBufferSize - location; |
240 | |
241 | // Sometimes plugins like to set Content-Length themselves when they post, |
242 | // but WebFoundation does not like that. So we will remove the header |
243 | // and instead truncate the data to the requested length. |
244 | String contentLength = headerFields.get(HTTPHeaderName::ContentLength); |
245 | |
246 | if (!contentLength.isNull()) |
247 | dataLength = std::min(contentLength.toInt(), (int)dataLength); |
248 | headerFields.remove(HTTPHeaderName::ContentLength); |
249 | |
250 | postBuffer += location; |
251 | postBufferSize = dataLength; |
252 | } |
253 | } |
254 | } |
255 | |
256 | ASSERT(bodyData.isEmpty()); |
257 | bodyData.append(postBuffer, postBufferSize); |
258 | |
259 | return NPERR_NO_ERROR; |
260 | } |
261 | |
262 | static String makeURLString(const char* url) |
263 | { |
264 | String urlString(url); |
265 | |
266 | // Strip return characters. |
267 | urlString.replaceWithLiteral('\r', "" ); |
268 | urlString.replaceWithLiteral('\n', "" ); |
269 | |
270 | return urlString; |
271 | } |
272 | |
273 | static NPError NPN_GetURL(NPP npp, const char* url, const char* target) |
274 | { |
275 | if (!url) |
276 | return NPERR_GENERIC_ERROR; |
277 | |
278 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
279 | plugin->loadURL("GET" , makeURLString(url), target, HTTPHeaderMap(), Vector<uint8_t>(), false, 0); |
280 | |
281 | return NPERR_GENERIC_ERROR; |
282 | } |
283 | |
284 | static NPError NPN_PostURL(NPP npp, const char* url, const char* target, uint32_t len, const char* buf, NPBool file) |
285 | { |
286 | HTTPHeaderMap ; |
287 | Vector<uint8_t> postData; |
288 | |
289 | // NPN_PostURL only allows headers if the post buffer points to a file. |
290 | bool = file; |
291 | |
292 | NPError error = parsePostBuffer(file, buf, len, parseHeaders, headerFields, postData); |
293 | if (error != NPERR_NO_ERROR) |
294 | return error; |
295 | |
296 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
297 | plugin->loadURL("POST" , makeURLString(url), target, WTFMove(headerFields), postData, false, 0); |
298 | return NPERR_NO_ERROR; |
299 | } |
300 | |
301 | static NPError NPN_RequestRead(NPStream*, NPByteRange*) |
302 | { |
303 | notImplemented(); |
304 | return NPERR_GENERIC_ERROR; |
305 | } |
306 | |
307 | static NPError NPN_NewStream(NPP, NPMIMEType, const char*, NPStream**) |
308 | { |
309 | notImplemented(); |
310 | return NPERR_GENERIC_ERROR; |
311 | } |
312 | |
313 | static int32_t NPN_Write(NPP, NPStream*, int32_t, void*) |
314 | { |
315 | notImplemented(); |
316 | return -1; |
317 | } |
318 | |
319 | static NPError NPN_DestroyStream(NPP npp, NPStream* stream, NPReason reason) |
320 | { |
321 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
322 | |
323 | return plugin->destroyStream(stream, reason); |
324 | } |
325 | |
326 | static void NPN_Status(NPP npp, const char* message) |
327 | { |
328 | String statusbarText; |
329 | if (!message) |
330 | statusbarText = emptyString(); |
331 | else |
332 | statusbarText = String::fromUTF8WithLatin1Fallback(message, strlen(message)); |
333 | |
334 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
335 | plugin->setStatusbarText(statusbarText); |
336 | } |
337 | |
338 | static const char* NPN_UserAgent(NPP npp) |
339 | { |
340 | return NetscapePlugin::userAgent(npp); |
341 | } |
342 | |
343 | static void* NPN_MemAlloc(uint32_t size) |
344 | { |
345 | return npnMemAlloc(size); |
346 | } |
347 | |
348 | static void NPN_MemFree(void* ptr) |
349 | { |
350 | npnMemFree(ptr); |
351 | } |
352 | |
353 | static uint32_t NPN_MemFlush(uint32_t) |
354 | { |
355 | return 0; |
356 | } |
357 | |
358 | static void NPN_ReloadPlugins(NPBool) |
359 | { |
360 | notImplemented(); |
361 | } |
362 | |
363 | static JRIEnv* NPN_GetJavaEnv(void) |
364 | { |
365 | notImplemented(); |
366 | return 0; |
367 | } |
368 | |
369 | static jref NPN_GetJavaPeer(NPP) |
370 | { |
371 | notImplemented(); |
372 | return 0; |
373 | } |
374 | |
375 | static NPError NPN_GetURLNotify(NPP npp, const char* url, const char* target, void* notifyData) |
376 | { |
377 | if (!url) |
378 | return NPERR_GENERIC_ERROR; |
379 | |
380 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
381 | plugin->loadURL("GET" , makeURLString(url), target, HTTPHeaderMap(), Vector<uint8_t>(), true, notifyData); |
382 | |
383 | return NPERR_NO_ERROR; |
384 | } |
385 | |
386 | static NPError NPN_PostURLNotify(NPP npp, const char* url, const char* target, uint32_t len, const char* buf, NPBool file, void* notifyData) |
387 | { |
388 | HTTPHeaderMap ; |
389 | Vector<uint8_t> postData; |
390 | NPError error = parsePostBuffer(file, buf, len, true, headerFields, postData); |
391 | if (error != NPERR_NO_ERROR) |
392 | return error; |
393 | |
394 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
395 | plugin->loadURL("POST" , makeURLString(url), target, headerFields, postData, true, notifyData); |
396 | return NPERR_NO_ERROR; |
397 | } |
398 | |
399 | #if PLATFORM(COCOA) |
400 | // Whether the browser supports compositing of Core Animation plug-ins. |
401 | static const unsigned WKNVSupportsCompositingCoreAnimationPluginsBool = 74656; |
402 | |
403 | // Whether the browser expects a non-retained Core Animation layer. |
404 | static const unsigned WKNVExpectsNonretainedLayer = 74657; |
405 | |
406 | // 74658 and 74659 are no longer implemented. |
407 | |
408 | #endif |
409 | |
410 | static NPError NPN_GetValue(NPP npp, NPNVariable variable, void *value) |
411 | { |
412 | switch (static_cast<unsigned>(variable)) { |
413 | case NPNVWindowNPObject: { |
414 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
415 | PluginDestructionProtector protector(plugin.get()); |
416 | |
417 | NPObject* windowNPObject = plugin->windowScriptNPObject(); |
418 | if (!windowNPObject) |
419 | return NPERR_GENERIC_ERROR; |
420 | |
421 | *(NPObject**)value = windowNPObject; |
422 | break; |
423 | } |
424 | case NPNVPluginElementNPObject: { |
425 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
426 | PluginDestructionProtector protector(plugin.get()); |
427 | |
428 | NPObject* pluginElementNPObject = plugin->pluginElementNPObject(); |
429 | *(NPObject**)value = pluginElementNPObject; |
430 | break; |
431 | } |
432 | case NPNVprivateModeBool: { |
433 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
434 | |
435 | *(NPBool*)value = plugin->isPrivateBrowsingEnabled(); |
436 | break; |
437 | } |
438 | |
439 | case NPNVmuteAudioBool: { |
440 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
441 | *(NPBool*)value = plugin->isMuted(); |
442 | break; |
443 | } |
444 | #if PLATFORM(COCOA) |
445 | case NPNVsupportsCoreGraphicsBool: |
446 | // Always claim to support the Core Graphics drawing model. |
447 | *(NPBool*)value = true; |
448 | break; |
449 | |
450 | case WKNVSupportsCompositingCoreAnimationPluginsBool: |
451 | case NPNVsupportsCoreAnimationBool: { |
452 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
453 | |
454 | *(NPBool*)value = plugin->isAcceleratedCompositingEnabled(); |
455 | break; |
456 | } |
457 | case NPNVcontentsScaleFactor: { |
458 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
459 | |
460 | *(double*)value = plugin->contentsScaleFactor(); |
461 | break; |
462 | } |
463 | case NPNVsupportsCocoaBool: |
464 | // Always claim to support the Cocoa event model. |
465 | *(NPBool*)value = true; |
466 | break; |
467 | |
468 | case NPNVsupportsUpdatedCocoaTextInputBool: { |
469 | // The plug-in is asking whether we support the updated Cocoa text input model. |
470 | // If we haven't yet delivered a key down event to the plug-in, we can opt into the updated |
471 | // model and say that we support it. Otherwise, we'll just fall back and say that we don't support it. |
472 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
473 | |
474 | bool supportsUpdatedTextInput = !plugin->hasHandledAKeyDownEvent(); |
475 | if (supportsUpdatedTextInput) |
476 | plugin->setPluginWantsLegacyCocoaTextInput(false); |
477 | |
478 | *reinterpret_cast<NPBool*>(value) = supportsUpdatedTextInput; |
479 | break; |
480 | } |
481 | |
482 | case WKNVCALayerRenderServerPort: { |
483 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
484 | |
485 | *(mach_port_t*)value = plugin->compositingRenderServerPort().sendRight(); |
486 | break; |
487 | } |
488 | |
489 | case WKNVExpectsNonretainedLayer: { |
490 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
491 | |
492 | // Asking for this will make us expect a non-retained layer from the plug-in. |
493 | plugin->setPluginReturnsNonretainedLayer(true); |
494 | *(NPBool*)value = true; |
495 | break; |
496 | } |
497 | #elif PLATFORM(X11) |
498 | case NPNVxDisplay: { |
499 | if (!npp) |
500 | return NPERR_GENERIC_ERROR; |
501 | auto& display = PlatformDisplay::sharedDisplay(); |
502 | if (display.type() != PlatformDisplay::Type::X11) |
503 | return NPERR_GENERIC_ERROR; |
504 | *reinterpret_cast<Display**>(value) = downcast<PlatformDisplayX11>(display).native(); |
505 | break; |
506 | } |
507 | case NPNVSupportsXEmbedBool: |
508 | *static_cast<NPBool*>(value) = PlatformDisplay::sharedDisplay().type() == PlatformDisplay::Type::X11; |
509 | break; |
510 | case NPNVSupportsWindowless: |
511 | *static_cast<NPBool*>(value) = true; |
512 | break; |
513 | |
514 | case NPNVToolkit: { |
515 | // Gtk based plugins need to be assured about the toolkit version. |
516 | const uint32_t expectedGtkToolKitVersion = 2; |
517 | *reinterpret_cast<uint32_t*>(value) = expectedGtkToolKitVersion; |
518 | break; |
519 | } |
520 | |
521 | // TODO: implement NPNVnetscapeWindow once we want to support windowed plugins. |
522 | #endif |
523 | default: |
524 | notImplemented(); |
525 | return NPERR_GENERIC_ERROR; |
526 | } |
527 | |
528 | return NPERR_NO_ERROR; |
529 | } |
530 | |
531 | static NPError NPN_SetValue(NPP npp, NPPVariable variable, void *value) |
532 | { |
533 | switch (variable) { |
534 | #if PLATFORM(COCOA) |
535 | case NPPVpluginDrawingModel: { |
536 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
537 | |
538 | NPDrawingModel drawingModel = static_cast<NPDrawingModel>(reinterpret_cast<uintptr_t>(value)); |
539 | return plugin->setDrawingModel(drawingModel); |
540 | } |
541 | |
542 | case NPPVpluginEventModel: { |
543 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
544 | |
545 | NPEventModel eventModel = static_cast<NPEventModel>(reinterpret_cast<uintptr_t>(value)); |
546 | return plugin->setEventModel(eventModel); |
547 | } |
548 | #endif |
549 | |
550 | case NPPVpluginWindowBool: { |
551 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
552 | plugin->setIsWindowed(value); |
553 | return NPERR_NO_ERROR; |
554 | } |
555 | |
556 | case NPPVpluginTransparentBool: { |
557 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
558 | plugin->setIsTransparent(value); |
559 | return NPERR_NO_ERROR; |
560 | } |
561 | |
562 | case NPPVpluginIsPlayingAudio: { |
563 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
564 | plugin->setIsPlayingAudio(value); |
565 | return NPERR_NO_ERROR; |
566 | } |
567 | |
568 | default: |
569 | notImplemented(); |
570 | return NPERR_GENERIC_ERROR; |
571 | } |
572 | } |
573 | |
574 | static void NPN_InvalidateRect(NPP npp, NPRect* invalidRect) |
575 | { |
576 | #if PLUGIN_ARCHITECTURE(UNIX) |
577 | // NSPluginWrapper, a plugin wrapper binary that allows running 32-bit plugins |
578 | // on 64-bit architectures typically used in X11, will sometimes give us a null NPP here. |
579 | if (!npp) |
580 | return; |
581 | #endif |
582 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
583 | plugin->invalidate(invalidRect); |
584 | } |
585 | |
586 | static void NPN_InvalidateRegion(NPP npp, NPRegion) |
587 | { |
588 | // FIXME: We could at least figure out the bounding rectangle of the invalid region. |
589 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
590 | plugin->invalidate(0); |
591 | } |
592 | |
593 | static void NPN_ForceRedraw(NPP) |
594 | { |
595 | notImplemented(); |
596 | } |
597 | |
598 | static NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name) |
599 | { |
600 | return static_cast<NPIdentifier>(IdentifierRep::get(name)); |
601 | } |
602 | |
603 | static void NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount, NPIdentifier *identifiers) |
604 | { |
605 | ASSERT(names); |
606 | ASSERT(identifiers); |
607 | |
608 | if (!names || !identifiers) |
609 | return; |
610 | |
611 | for (int32_t i = 0; i < nameCount; ++i) |
612 | identifiers[i] = NPN_GetStringIdentifier(names[i]); |
613 | } |
614 | |
615 | static NPIdentifier NPN_GetIntIdentifier(int32_t intid) |
616 | { |
617 | return static_cast<NPIdentifier>(IdentifierRep::get(intid)); |
618 | } |
619 | |
620 | static bool NPN_IdentifierIsString(NPIdentifier identifier) |
621 | { |
622 | return static_cast<IdentifierRep*>(identifier)->isString(); |
623 | } |
624 | |
625 | static NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier) |
626 | { |
627 | const char* string = static_cast<IdentifierRep*>(identifier)->string(); |
628 | if (!string) |
629 | return 0; |
630 | |
631 | uint32_t stringLength = strlen(string); |
632 | char* utf8String = npnMemNewArray<char>(stringLength + 1); |
633 | memcpy(utf8String, string, stringLength); |
634 | utf8String[stringLength] = '\0'; |
635 | |
636 | return utf8String; |
637 | } |
638 | |
639 | static int32_t NPN_IntFromIdentifier(NPIdentifier identifier) |
640 | { |
641 | return static_cast<IdentifierRep*>(identifier)->number(); |
642 | } |
643 | |
644 | static NPObject* NPN_CreateObject(NPP npp, NPClass *npClass) |
645 | { |
646 | return createNPObject(npp, npClass); |
647 | } |
648 | |
649 | static NPObject *NPN_RetainObject(NPObject *npObject) |
650 | { |
651 | retainNPObject(npObject); |
652 | return npObject; |
653 | } |
654 | |
655 | static void NPN_ReleaseObject(NPObject *npObject) |
656 | { |
657 | releaseNPObject(npObject); |
658 | } |
659 | |
660 | static bool NPN_Invoke(NPP npp, NPObject *npObject, NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result) |
661 | { |
662 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
663 | PluginDestructionProtector protector(plugin.get()); |
664 | |
665 | if (npObject->_class->invoke) |
666 | return npObject->_class->invoke(npObject, methodName, arguments, argumentCount, result); |
667 | |
668 | return false; |
669 | } |
670 | |
671 | static bool NPN_InvokeDefault(NPP npp, NPObject *npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result) |
672 | { |
673 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
674 | PluginDestructionProtector protector(plugin.get()); |
675 | |
676 | if (npObject->_class->invokeDefault) |
677 | return npObject->_class->invokeDefault(npObject, arguments, argumentCount, result); |
678 | |
679 | return false; |
680 | } |
681 | |
682 | static bool NPN_Evaluate(NPP npp, NPObject *npObject, NPString *script, NPVariant* result) |
683 | { |
684 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
685 | PluginDestructionProtector protector(plugin.get()); |
686 | |
687 | String scriptString = String::fromUTF8WithLatin1Fallback(script->UTF8Characters, script->UTF8Length); |
688 | |
689 | return plugin->evaluate(npObject, scriptString, result); |
690 | } |
691 | |
692 | static bool NPN_GetProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName, NPVariant* result) |
693 | { |
694 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
695 | PluginDestructionProtector protector(plugin.get()); |
696 | |
697 | if (npObject->_class->getProperty) |
698 | return npObject->_class->getProperty(npObject, propertyName, result); |
699 | |
700 | return false; |
701 | } |
702 | |
703 | static bool NPN_SetProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName, const NPVariant* value) |
704 | { |
705 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
706 | PluginDestructionProtector protector(plugin.get()); |
707 | |
708 | if (npObject->_class->setProperty) |
709 | return npObject->_class->setProperty(npObject, propertyName, value); |
710 | |
711 | return false; |
712 | } |
713 | |
714 | static bool NPN_RemoveProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName) |
715 | { |
716 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
717 | PluginDestructionProtector protector(plugin.get()); |
718 | |
719 | if (npObject->_class->removeProperty) |
720 | return npObject->_class->removeProperty(npObject, propertyName); |
721 | |
722 | return false; |
723 | } |
724 | |
725 | static bool NPN_HasProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName) |
726 | { |
727 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
728 | PluginDestructionProtector protector(plugin.get()); |
729 | |
730 | if (npObject->_class->hasProperty) |
731 | return npObject->_class->hasProperty(npObject, propertyName); |
732 | |
733 | return false; |
734 | } |
735 | |
736 | static bool NPN_HasMethod(NPP npp, NPObject* npObject, NPIdentifier methodName) |
737 | { |
738 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
739 | PluginDestructionProtector protector(plugin.get()); |
740 | |
741 | if (npObject->_class->hasMethod) |
742 | return npObject->_class->hasMethod(npObject, methodName); |
743 | |
744 | return false; |
745 | } |
746 | |
747 | static void NPN_ReleaseVariantValue(NPVariant* variant) |
748 | { |
749 | releaseNPVariantValue(variant); |
750 | } |
751 | |
752 | static void NPN_SetException(NPObject*, const NPUTF8* message) |
753 | { |
754 | NetscapePlugin::setException(message); |
755 | } |
756 | |
757 | static void (NPP npp, NPBool enabled) |
758 | { |
759 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
760 | plugin->pushPopupsEnabledState(enabled); |
761 | } |
762 | |
763 | static void (NPP npp) |
764 | { |
765 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
766 | plugin->popPopupsEnabledState(); |
767 | } |
768 | |
769 | static bool NPN_Enumerate(NPP npp, NPObject* npObject, NPIdentifier** identifiers, uint32_t* identifierCount) |
770 | { |
771 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
772 | PluginDestructionProtector protector(plugin.get()); |
773 | |
774 | if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(npObject->_class) && npObject->_class->enumerate) |
775 | return npObject->_class->enumerate(npObject, identifiers, identifierCount); |
776 | |
777 | return false; |
778 | } |
779 | |
780 | static void NPN_PluginThreadAsyncCall(NPP npp, void (*function)(void*), void* userData) |
781 | { |
782 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
783 | |
784 | plugin->pluginThreadAsyncCall(function, userData); |
785 | } |
786 | |
787 | static bool NPN_Construct(NPP npp, NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result) |
788 | { |
789 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
790 | PluginDestructionProtector protector(plugin.get()); |
791 | |
792 | if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(npObject->_class) && npObject->_class->construct) |
793 | return npObject->_class->construct(npObject, arguments, argumentCount, result); |
794 | |
795 | return false; |
796 | } |
797 | |
798 | static NPError copyCString(const CString& string, char** value, uint32_t* len) |
799 | { |
800 | ASSERT(!string.isNull()); |
801 | ASSERT(value); |
802 | ASSERT(len); |
803 | |
804 | *value = npnMemNewArray<char>(string.length()); |
805 | if (!*value) |
806 | return NPERR_GENERIC_ERROR; |
807 | |
808 | memcpy(*value, string.data(), string.length()); |
809 | *len = string.length(); |
810 | return NPERR_NO_ERROR; |
811 | } |
812 | |
813 | static NPError NPN_GetValueForURL(NPP npp, NPNURLVariable variable, const char* url, char** value, uint32_t* len) |
814 | { |
815 | if (!value || !len) |
816 | return NPERR_GENERIC_ERROR; |
817 | |
818 | switch (variable) { |
819 | case NPNURLVCookie: { |
820 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
821 | PluginDestructionProtector protector(plugin.get()); |
822 | |
823 | String cookies = plugin->cookiesForURL(makeURLString(url)); |
824 | if (cookies.isNull()) |
825 | return NPERR_GENERIC_ERROR; |
826 | |
827 | return copyCString(cookies.utf8(), value, len); |
828 | } |
829 | |
830 | case NPNURLVProxy: { |
831 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
832 | PluginDestructionProtector protector(plugin.get()); |
833 | |
834 | String proxies = plugin->proxiesForURL(makeURLString(url)); |
835 | if (proxies.isNull()) |
836 | return NPERR_GENERIC_ERROR; |
837 | |
838 | return copyCString(proxies.utf8(), value, len); |
839 | } |
840 | default: |
841 | notImplemented(); |
842 | return NPERR_GENERIC_ERROR; |
843 | } |
844 | } |
845 | |
846 | static NPError NPN_SetValueForURL(NPP npp, NPNURLVariable variable, const char* url, const char* value, uint32_t len) |
847 | { |
848 | switch (variable) { |
849 | case NPNURLVCookie: { |
850 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
851 | PluginDestructionProtector protector(plugin.get()); |
852 | |
853 | plugin->setCookiesForURL(makeURLString(url), String(value, len)); |
854 | return NPERR_NO_ERROR; |
855 | } |
856 | |
857 | case NPNURLVProxy: |
858 | // Can't set the proxy for a URL. |
859 | return NPERR_GENERIC_ERROR; |
860 | |
861 | default: |
862 | notImplemented(); |
863 | return NPERR_GENERIC_ERROR; |
864 | } |
865 | } |
866 | |
867 | static bool initializeProtectionSpace(const char* protocol, const char* host, int port, const char* scheme, const char* realm, ProtectionSpace& protectionSpace) |
868 | { |
869 | ProtectionSpaceServerType serverType; |
870 | if (equalLettersIgnoringASCIICase(protocol, "http" )) |
871 | serverType = ProtectionSpaceServerHTTP; |
872 | else if (equalLettersIgnoringASCIICase(protocol, "https" )) |
873 | serverType = ProtectionSpaceServerHTTPS; |
874 | else { |
875 | // We only care about http and https. |
876 | return false; |
877 | } |
878 | |
879 | ProtectionSpaceAuthenticationScheme authenticationScheme = ProtectionSpaceAuthenticationSchemeDefault; |
880 | if (serverType == ProtectionSpaceServerHTTP) { |
881 | if (equalLettersIgnoringASCIICase(scheme, "basic" )) |
882 | authenticationScheme = ProtectionSpaceAuthenticationSchemeHTTPBasic; |
883 | else if (equalLettersIgnoringASCIICase(scheme, "digest" )) |
884 | authenticationScheme = ProtectionSpaceAuthenticationSchemeHTTPDigest; |
885 | } |
886 | |
887 | protectionSpace = ProtectionSpace(host, port, serverType, realm, authenticationScheme); |
888 | return true; |
889 | } |
890 | |
891 | static NPError NPN_GetAuthenticationInfo(NPP npp, const char* protocol, const char* host, int32_t port, const char* scheme, |
892 | const char* realm, char** username, uint32_t* usernameLength, char** password, uint32_t* passwordLength) |
893 | { |
894 | if (!protocol || !host || !scheme || !realm || !username || !usernameLength || !password || !passwordLength) |
895 | return NPERR_GENERIC_ERROR; |
896 | |
897 | ProtectionSpace protectionSpace; |
898 | if (!initializeProtectionSpace(protocol, host, port, scheme, realm, protectionSpace)) |
899 | return NPERR_GENERIC_ERROR; |
900 | |
901 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
902 | String usernameString; |
903 | String passwordString; |
904 | if (!plugin->getAuthenticationInfo(protectionSpace, usernameString, passwordString)) |
905 | return NPERR_GENERIC_ERROR; |
906 | |
907 | NPError result = copyCString(usernameString.utf8(), username, usernameLength); |
908 | if (result != NPERR_NO_ERROR) |
909 | return result; |
910 | |
911 | result = copyCString(passwordString.utf8(), password, passwordLength); |
912 | if (result != NPERR_NO_ERROR) { |
913 | npnMemFree(*username); |
914 | return result; |
915 | } |
916 | |
917 | return NPERR_NO_ERROR; |
918 | } |
919 | |
920 | static uint32_t NPN_ScheduleTimer(NPP npp, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID)) |
921 | { |
922 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
923 | |
924 | return plugin->scheduleTimer(interval, repeat, timerFunc); |
925 | } |
926 | |
927 | static void NPN_UnscheduleTimer(NPP npp, uint32_t timerID) |
928 | { |
929 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
930 | |
931 | plugin->unscheduleTimer(timerID); |
932 | } |
933 | |
934 | #if PLATFORM(COCOA) |
935 | static NPError NPN_PopUpContextMenu(NPP npp, NPMenu* menu) |
936 | { |
937 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
938 | |
939 | return plugin->popUpContextMenu(menu); |
940 | } |
941 | |
942 | static NPBool NPN_ConvertPoint(NPP npp, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double* destX, double* destY, NPCoordinateSpace destSpace) |
943 | { |
944 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
945 | |
946 | double destinationX; |
947 | double destinationY; |
948 | |
949 | bool returnValue = plugin->convertPoint(sourceX, sourceY, sourceSpace, destinationX, destinationY, destSpace); |
950 | |
951 | if (destX) |
952 | *destX = destinationX; |
953 | if (destY) |
954 | *destY = destinationY; |
955 | |
956 | return returnValue; |
957 | } |
958 | #endif |
959 | |
960 | static void NPN_URLRedirectResponse(NPP npp, void* notifyData, NPBool allow) |
961 | { |
962 | RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp); |
963 | |
964 | plugin->urlRedirectResponse(notifyData, allow); |
965 | } |
966 | |
967 | static void initializeBrowserFuncs(NPNetscapeFuncs &netscapeFuncs) |
968 | { |
969 | netscapeFuncs.size = sizeof(NPNetscapeFuncs); |
970 | netscapeFuncs.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; |
971 | |
972 | netscapeFuncs.geturl = NPN_GetURL; |
973 | netscapeFuncs.posturl = NPN_PostURL; |
974 | netscapeFuncs.requestread = NPN_RequestRead; |
975 | netscapeFuncs.newstream = NPN_NewStream; |
976 | netscapeFuncs.write = NPN_Write; |
977 | netscapeFuncs.destroystream = NPN_DestroyStream; |
978 | netscapeFuncs.status = NPN_Status; |
979 | netscapeFuncs.uagent = NPN_UserAgent; |
980 | netscapeFuncs.memalloc = NPN_MemAlloc; |
981 | netscapeFuncs.memfree = NPN_MemFree; |
982 | netscapeFuncs.memflush = NPN_MemFlush; |
983 | netscapeFuncs.reloadplugins = NPN_ReloadPlugins; |
984 | netscapeFuncs.getJavaEnv = NPN_GetJavaEnv; |
985 | netscapeFuncs.getJavaPeer = NPN_GetJavaPeer; |
986 | netscapeFuncs.geturlnotify = NPN_GetURLNotify; |
987 | netscapeFuncs.posturlnotify = NPN_PostURLNotify; |
988 | netscapeFuncs.getvalue = NPN_GetValue; |
989 | netscapeFuncs.setvalue = NPN_SetValue; |
990 | netscapeFuncs.invalidaterect = NPN_InvalidateRect; |
991 | netscapeFuncs.invalidateregion = NPN_InvalidateRegion; |
992 | netscapeFuncs.forceredraw = NPN_ForceRedraw; |
993 | |
994 | netscapeFuncs.getstringidentifier = NPN_GetStringIdentifier; |
995 | netscapeFuncs.getstringidentifiers = NPN_GetStringIdentifiers; |
996 | netscapeFuncs.getintidentifier = NPN_GetIntIdentifier; |
997 | netscapeFuncs.identifierisstring = NPN_IdentifierIsString; |
998 | netscapeFuncs.utf8fromidentifier = NPN_UTF8FromIdentifier; |
999 | netscapeFuncs.intfromidentifier = NPN_IntFromIdentifier; |
1000 | netscapeFuncs.createobject = NPN_CreateObject; |
1001 | netscapeFuncs.retainobject = NPN_RetainObject; |
1002 | netscapeFuncs.releaseobject = NPN_ReleaseObject; |
1003 | netscapeFuncs.invoke = NPN_Invoke; |
1004 | netscapeFuncs.invokeDefault = NPN_InvokeDefault; |
1005 | netscapeFuncs.evaluate = NPN_Evaluate; |
1006 | netscapeFuncs.getproperty = NPN_GetProperty; |
1007 | netscapeFuncs.setproperty = NPN_SetProperty; |
1008 | netscapeFuncs.removeproperty = NPN_RemoveProperty; |
1009 | netscapeFuncs.hasproperty = NPN_HasProperty; |
1010 | netscapeFuncs.hasmethod = NPN_HasMethod; |
1011 | netscapeFuncs.releasevariantvalue = NPN_ReleaseVariantValue; |
1012 | netscapeFuncs.setexception = NPN_SetException; |
1013 | netscapeFuncs.pushpopupsenabledstate = NPN_PushPopupsEnabledState; |
1014 | netscapeFuncs.poppopupsenabledstate = NPN_PopPopupsEnabledState; |
1015 | netscapeFuncs.enumerate = NPN_Enumerate; |
1016 | netscapeFuncs.pluginthreadasynccall = NPN_PluginThreadAsyncCall; |
1017 | netscapeFuncs.construct = NPN_Construct; |
1018 | netscapeFuncs.getvalueforurl = NPN_GetValueForURL; |
1019 | netscapeFuncs.setvalueforurl = NPN_SetValueForURL; |
1020 | netscapeFuncs.getauthenticationinfo = NPN_GetAuthenticationInfo; |
1021 | netscapeFuncs.scheduletimer = NPN_ScheduleTimer; |
1022 | netscapeFuncs.unscheduletimer = NPN_UnscheduleTimer; |
1023 | #if PLATFORM(COCOA) |
1024 | netscapeFuncs.popupcontextmenu = NPN_PopUpContextMenu; |
1025 | netscapeFuncs.convertpoint = NPN_ConvertPoint; |
1026 | #else |
1027 | netscapeFuncs.popupcontextmenu = 0; |
1028 | netscapeFuncs.convertpoint = 0; |
1029 | #endif |
1030 | netscapeFuncs.urlredirectresponse = NPN_URLRedirectResponse; |
1031 | } |
1032 | |
1033 | NPNetscapeFuncs* netscapeBrowserFuncs() |
1034 | { |
1035 | static NPNetscapeFuncs netscapeFuncs; |
1036 | static bool initialized = false; |
1037 | |
1038 | if (!initialized) { |
1039 | initializeBrowserFuncs(netscapeFuncs); |
1040 | initialized = true; |
1041 | } |
1042 | |
1043 | return &netscapeFuncs; |
1044 | } |
1045 | |
1046 | } // namespace WebKit |
1047 | |
1048 | #endif // ENABLE(NETSCAPE_PLUGIN_API) |
1049 | |