1 /* 2 * Copyright (C) 2008, 2009, 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. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) 27 28 #import "NetscapePluginInstanceProxy.h" 29 30 #import "HostedNetscapePluginStream.h" 31 #import "NetscapePluginHostProxy.h" 32 #import "ProxyInstance.h" 33 #import "ProxyRuntimeObject.h" 34 #import "WebDataSourceInternal.h" 35 #import "WebFrameInternal.h" 36 #import "WebHostedNetscapePluginView.h" 37 #import "WebKitNSStringExtras.h" 38 #import "WebNSDataExtras.h" 39 #import "WebNSURLExtras.h" 40 #import "WebPluginRequest.h" 41 #import "WebUIDelegate.h" 42 #import "WebUIDelegatePrivate.h" 43 #import "WebViewInternal.h" 44 #import <JavaScriptCore/Error.h> 45 #import <JavaScriptCore/JSLock.h> 46 #import <JavaScriptCore/PropertyNameArray.h> 47 #import <WebCore/CookieJar.h> 48 #import <WebCore/DocumentLoader.h> 49 #import <WebCore/Frame.h> 50 #import <WebCore/FrameLoader.h> 51 #import <WebCore/FrameTree.h> 52 #import <WebCore/KURL.h> 53 #import <WebCore/ProxyServer.h> 54 #import <WebCore/SecurityOrigin.h> 55 #import <WebCore/ScriptController.h> 56 #import <WebCore/ScriptValue.h> 57 #import <WebCore/StringSourceProvider.h> 58 #import <WebCore/npruntime_impl.h> 59 #import <WebCore/runtime_object.h> 60 #import <WebKitSystemInterface.h> 61 #import <mach/mach.h> 62 #import <utility> 63 #import <wtf/RefCountedLeakCounter.h> 64 #import <wtf/text/CString.h> 65 66 extern "C" { 67 #import "WebKitPluginClientServer.h" 68 #import "WebKitPluginHost.h" 69 } 70 71 using namespace JSC; 72 using namespace JSC::Bindings; 73 using namespace std; 74 using namespace WebCore; 75 76 namespace WebKit { 77 78 class NetscapePluginInstanceProxy::PluginRequest : public RefCounted<NetscapePluginInstanceProxy::PluginRequest> { 79 public: 80 static PassRefPtr<PluginRequest> create(uint32_t requestID, NSURLRequest* request, NSString* frameName, bool allowPopups) 81 { 82 return adoptRef(new PluginRequest(requestID, request, frameName, allowPopups)); 83 } 84 85 uint32_t requestID() const { return m_requestID; } 86 NSURLRequest* request() const { return m_request.get(); } 87 NSString* frameName() const { return m_frameName.get(); } 88 bool allowPopups() const { return m_allowPopups; } 89 90 private: 91 PluginRequest(uint32_t requestID, NSURLRequest* request, NSString* frameName, bool allowPopups) 92 : m_requestID(requestID) 93 , m_request(request) 94 , m_frameName(frameName) 95 , m_allowPopups(allowPopups) 96 { 97 } 98 99 uint32_t m_requestID; 100 RetainPtr<NSURLRequest*> m_request; 101 RetainPtr<NSString*> m_frameName; 102 bool m_allowPopups; 103 }; 104 105 NetscapePluginInstanceProxy::LocalObjectMap::LocalObjectMap() 106 : m_objectIDCounter(0) 107 { 108 } 109 110 NetscapePluginInstanceProxy::LocalObjectMap::~LocalObjectMap() 111 { 112 } 113 114 inline bool NetscapePluginInstanceProxy::LocalObjectMap::contains(uint32_t objectID) const 115 { 116 return m_idToJSObjectMap.contains(objectID); 117 } 118 119 inline JSC::JSObject* NetscapePluginInstanceProxy::LocalObjectMap::get(uint32_t objectID) const 120 { 121 if (objectID == HashTraits<uint32_t>::emptyValue() || HashTraits<uint32_t>::isDeletedValue(objectID)) 122 return 0; 123 124 return m_idToJSObjectMap.get(objectID).get(); 125 } 126 127 uint32_t NetscapePluginInstanceProxy::LocalObjectMap::idForObject(JSGlobalData& globalData, JSObject* object) 128 { 129 // This method creates objects with refcount of 1, but doesn't increase refcount when returning 130 // found objects. This extra count accounts for the main "reference" kept by plugin process. 131 132 // To avoid excessive IPC, plugin process doesn't send each NPObject release/retain call to 133 // Safari. It only sends one when the last reference is removed, and it can destroy the proxy 134 // NPObject. 135 136 // However, the browser may be sending the same object out to plug-in as a function call 137 // argument at the same time - neither side can know what the other one is doing. So, 138 // is to make PCForgetBrowserObject call return a boolean result, making it possible for 139 // the browser to make plugin host keep the proxy with zero refcount for a little longer. 140 141 uint32_t objectID = 0; 142 143 HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator iter = m_jsObjectToIDMap.find(object); 144 if (iter != m_jsObjectToIDMap.end()) 145 return iter->second.first; 146 147 do { 148 objectID = ++m_objectIDCounter; 149 } while (!m_objectIDCounter || m_objectIDCounter == static_cast<uint32_t>(-1) || m_idToJSObjectMap.contains(objectID)); 150 151 m_idToJSObjectMap.set(objectID, Strong<JSObject>(globalData, object)); 152 m_jsObjectToIDMap.set(object, make_pair<uint32_t, uint32_t>(objectID, 1)); 153 154 return objectID; 155 } 156 157 void NetscapePluginInstanceProxy::LocalObjectMap::retain(JSC::JSObject* object) 158 { 159 HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator iter = m_jsObjectToIDMap.find(object); 160 ASSERT(iter != m_jsObjectToIDMap.end()); 161 162 iter->second.second = iter->second.second + 1; 163 } 164 165 void NetscapePluginInstanceProxy::LocalObjectMap::release(JSC::JSObject* object) 166 { 167 HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator iter = m_jsObjectToIDMap.find(object); 168 ASSERT(iter != m_jsObjectToIDMap.end()); 169 170 ASSERT(iter->second.second > 0); 171 iter->second.second = iter->second.second - 1; 172 if (!iter->second.second) { 173 m_idToJSObjectMap.remove(iter->second.first); 174 m_jsObjectToIDMap.remove(iter); 175 } 176 } 177 178 void NetscapePluginInstanceProxy::LocalObjectMap::clear() 179 { 180 m_idToJSObjectMap.clear(); 181 m_jsObjectToIDMap.clear(); 182 } 183 184 bool NetscapePluginInstanceProxy::LocalObjectMap::forget(uint32_t objectID) 185 { 186 if (objectID == HashTraits<uint32_t>::emptyValue() || HashTraits<uint32_t>::isDeletedValue(objectID)) { 187 LOG_ERROR("NetscapePluginInstanceProxy::LocalObjectMap::forget: local object id %u is not valid.", objectID); 188 return true; 189 } 190 191 HashMap<uint32_t, JSC::Strong<JSC::JSObject> >::iterator iter = m_idToJSObjectMap.find(objectID); 192 if (iter == m_idToJSObjectMap.end()) { 193 LOG_ERROR("NetscapePluginInstanceProxy::LocalObjectMap::forget: local object %u doesn't exist.", objectID); 194 return true; 195 } 196 197 HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator rIter = m_jsObjectToIDMap.find(iter->second.get()); 198 199 // If the object is being sent to plug-in right now, then it's not the time to forget. 200 if (rIter->second.second != 1) 201 return false; 202 203 m_jsObjectToIDMap.remove(rIter); 204 m_idToJSObjectMap.remove(iter); 205 return true; 206 } 207 208 static uint32_t pluginIDCounter; 209 210 bool NetscapePluginInstanceProxy::m_inDestroy; 211 212 #ifndef NDEBUG 213 static WTF::RefCountedLeakCounter netscapePluginInstanceProxyCounter("NetscapePluginInstanceProxy"); 214 #endif 215 216 NetscapePluginInstanceProxy::NetscapePluginInstanceProxy(NetscapePluginHostProxy* pluginHostProxy, WebHostedNetscapePluginView *pluginView, bool fullFramePlugin) 217 : m_pluginHostProxy(pluginHostProxy) 218 , m_pluginView(pluginView) 219 , m_requestTimer(this, &NetscapePluginInstanceProxy::requestTimerFired) 220 , m_currentURLRequestID(0) 221 , m_renderContextID(0) 222 , m_rendererType(UseSoftwareRenderer) 223 , m_waitingForReply(false) 224 , m_urlCheckCounter(0) 225 , m_pluginFunctionCallDepth(0) 226 , m_shouldStopSoon(false) 227 , m_currentRequestID(0) 228 , m_pluginIsWaitingForDraw(false) 229 { 230 ASSERT(m_pluginView); 231 232 if (fullFramePlugin) { 233 // For full frame plug-ins, the first requestID will always be the one for the already 234 // open stream. 235 ++m_currentURLRequestID; 236 } 237 238 // Assign a plug-in ID. 239 do { 240 m_pluginID = ++pluginIDCounter; 241 } while (pluginHostProxy->pluginInstance(m_pluginID) || !m_pluginID); 242 243 #ifndef NDEBUG 244 netscapePluginInstanceProxyCounter.increment(); 245 #endif 246 } 247 248 PassRefPtr<NetscapePluginInstanceProxy> NetscapePluginInstanceProxy::create(NetscapePluginHostProxy* pluginHostProxy, WebHostedNetscapePluginView *pluginView, bool fullFramePlugin) 249 { 250 RefPtr<NetscapePluginInstanceProxy> proxy = adoptRef(new NetscapePluginInstanceProxy(pluginHostProxy, pluginView, fullFramePlugin)); 251 pluginHostProxy->addPluginInstance(proxy.get()); 252 return proxy.release(); 253 } 254 255 NetscapePluginInstanceProxy::~NetscapePluginInstanceProxy() 256 { 257 ASSERT(!m_pluginHostProxy); 258 259 m_pluginID = 0; 260 deleteAllValues(m_replies); 261 262 #ifndef NDEBUG 263 netscapePluginInstanceProxyCounter.decrement(); 264 #endif 265 } 266 267 void NetscapePluginInstanceProxy::resize(NSRect size, NSRect clipRect) 268 { 269 uint32_t requestID = 0; 270 271 requestID = nextRequestID(); 272 273 _WKPHResizePluginInstance(m_pluginHostProxy->port(), m_pluginID, requestID, 274 size.origin.x, size.origin.y, size.size.width, size.size.height, 275 clipRect.origin.x, clipRect.origin.y, clipRect.size.width, clipRect.size.height); 276 277 waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); 278 } 279 280 void NetscapePluginInstanceProxy::stopAllStreams() 281 { 282 Vector<RefPtr<HostedNetscapePluginStream> > streamsCopy; 283 copyValuesToVector(m_streams, streamsCopy); 284 for (size_t i = 0; i < streamsCopy.size(); i++) 285 streamsCopy[i]->stop(); 286 } 287 288 void NetscapePluginInstanceProxy::cleanup() 289 { 290 stopAllStreams(); 291 292 m_requestTimer.stop(); 293 294 // Clear the object map, this will cause any outstanding JS objects that the plug-in had a reference to 295 // to go away when the next garbage collection takes place. 296 m_localObjects.clear(); 297 298 if (Frame* frame = core([m_pluginView webFrame])) 299 frame->script()->cleanupScriptObjectsForPlugin(m_pluginView); 300 301 ProxyInstanceSet instances; 302 instances.swap(m_instances); 303 304 // Invalidate all proxy instances. 305 ProxyInstanceSet::const_iterator end = instances.end(); 306 for (ProxyInstanceSet::const_iterator it = instances.begin(); it != end; ++it) 307 (*it)->invalidate(); 308 309 m_pluginView = nil; 310 m_manualStream = 0; 311 } 312 313 void NetscapePluginInstanceProxy::invalidate() 314 { 315 // If the plug-in host has died, the proxy will be null. 316 if (!m_pluginHostProxy) 317 return; 318 319 m_pluginHostProxy->removePluginInstance(this); 320 m_pluginHostProxy = 0; 321 } 322 323 void NetscapePluginInstanceProxy::destroy() 324 { 325 uint32_t requestID = nextRequestID(); 326 327 ASSERT(!m_inDestroy); 328 m_inDestroy = true; 329 330 FrameLoadMap::iterator end = m_pendingFrameLoads.end(); 331 for (FrameLoadMap::iterator it = m_pendingFrameLoads.begin(); it != end; ++it) 332 [(it->first) _setInternalLoadDelegate:nil]; 333 334 _WKPHDestroyPluginInstance(m_pluginHostProxy->port(), m_pluginID, requestID); 335 336 // If the plug-in host crashes while we're waiting for a reply, the last reference to the instance proxy 337 // will go away. Prevent this by protecting it here. 338 RefPtr<NetscapePluginInstanceProxy> protect(this); 339 340 // We don't care about the reply here - we just want to block until the plug-in instance has been torn down. 341 waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); 342 343 m_inDestroy = false; 344 345 cleanup(); 346 invalidate(); 347 } 348 349 void NetscapePluginInstanceProxy::setManualStream(PassRefPtr<HostedNetscapePluginStream> manualStream) 350 { 351 ASSERT(!m_manualStream); 352 353 m_manualStream = manualStream; 354 } 355 356 bool NetscapePluginInstanceProxy::cancelStreamLoad(uint32_t streamID, NPReason reason) 357 { 358 HostedNetscapePluginStream* stream = 0; 359 360 if (m_manualStream && streamID == 1) 361 stream = m_manualStream.get(); 362 else 363 stream = m_streams.get(streamID).get(); 364 365 if (!stream) 366 return false; 367 368 stream->cancelLoad(reason); 369 return true; 370 } 371 372 void NetscapePluginInstanceProxy::disconnectStream(HostedNetscapePluginStream* stream) 373 { 374 if (stream == m_manualStream) { 375 m_manualStream = 0; 376 return; 377 } 378 379 ASSERT(m_streams.get(stream->streamID()) == stream); 380 m_streams.remove(stream->streamID()); 381 } 382 383 void NetscapePluginInstanceProxy::pluginHostDied() 384 { 385 m_pluginHostProxy = 0; 386 387 [m_pluginView pluginHostDied]; 388 389 cleanup(); 390 } 391 392 void NetscapePluginInstanceProxy::focusChanged(bool hasFocus) 393 { 394 _WKPHPluginInstanceFocusChanged(m_pluginHostProxy->port(), m_pluginID, hasFocus); 395 } 396 397 void NetscapePluginInstanceProxy::windowFocusChanged(bool hasFocus) 398 { 399 _WKPHPluginInstanceWindowFocusChanged(m_pluginHostProxy->port(), m_pluginID, hasFocus); 400 } 401 402 void NetscapePluginInstanceProxy::windowFrameChanged(NSRect frame) 403 { 404 _WKPHPluginInstanceWindowFrameChanged(m_pluginHostProxy->port(), m_pluginID, frame.origin.x, frame.origin.y, frame.size.width, frame.size.height, 405 NSMaxY([[[NSScreen screens] objectAtIndex:0] frame])); 406 } 407 408 void NetscapePluginInstanceProxy::startTimers(bool throttleTimers) 409 { 410 _WKPHPluginInstanceStartTimers(m_pluginHostProxy->port(), m_pluginID, throttleTimers); 411 } 412 413 void NetscapePluginInstanceProxy::mouseEvent(NSView *pluginView, NSEvent *event, NPCocoaEventType type) 414 { 415 NSPoint screenPoint = [[event window] convertBaseToScreen:[event locationInWindow]]; 416 NSPoint pluginPoint = [pluginView convertPoint:[event locationInWindow] fromView:nil]; 417 418 int clickCount; 419 if (type == NPCocoaEventMouseEntered || type == NPCocoaEventMouseExited) 420 clickCount = 0; 421 else 422 clickCount = [event clickCount]; 423 424 425 _WKPHPluginInstanceMouseEvent(m_pluginHostProxy->port(), m_pluginID, 426 [event timestamp], 427 type, [event modifierFlags], 428 pluginPoint.x, pluginPoint.y, 429 screenPoint.x, screenPoint.y, 430 NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]), 431 [event buttonNumber], clickCount, 432 [event deltaX], [event deltaY], [event deltaZ]); 433 } 434 435 void NetscapePluginInstanceProxy::keyEvent(NSView *pluginView, NSEvent *event, NPCocoaEventType type) 436 { 437 NSData *charactersData = [[event characters] dataUsingEncoding:NSUTF8StringEncoding]; 438 NSData *charactersIgnoringModifiersData = [[event charactersIgnoringModifiers] dataUsingEncoding:NSUTF8StringEncoding]; 439 440 _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID, 441 [event timestamp], 442 type, [event modifierFlags], 443 const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length], 444 const_cast<char*>(reinterpret_cast<const char*>([charactersIgnoringModifiersData bytes])), [charactersIgnoringModifiersData length], 445 [event isARepeat], [event keyCode], WKGetNSEventKeyChar(event)); 446 } 447 448 void NetscapePluginInstanceProxy::syntheticKeyDownWithCommandModifier(int keyCode, char character) 449 { 450 NSData *charactersData = [NSData dataWithBytes:&character length:1]; 451 452 _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID, 453 [NSDate timeIntervalSinceReferenceDate], 454 NPCocoaEventKeyDown, NSCommandKeyMask, 455 const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length], 456 const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length], 457 false, keyCode, character); 458 } 459 460 void NetscapePluginInstanceProxy::flagsChanged(NSEvent *event) 461 { 462 _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID, 463 [event timestamp], NPCocoaEventFlagsChanged, 464 [event modifierFlags], 0, 0, 0, 0, false, [event keyCode], 0); 465 } 466 467 void NetscapePluginInstanceProxy::insertText(NSString *text) 468 { 469 NSData *textData = [text dataUsingEncoding:NSUTF8StringEncoding]; 470 471 _WKPHPluginInstanceInsertText(m_pluginHostProxy->port(), m_pluginID, 472 const_cast<char*>(reinterpret_cast<const char*>([textData bytes])), [textData length]); 473 } 474 475 bool NetscapePluginInstanceProxy::wheelEvent(NSView *pluginView, NSEvent *event) 476 { 477 NSPoint pluginPoint = [pluginView convertPoint:[event locationInWindow] fromView:nil]; 478 479 uint32_t requestID = nextRequestID(); 480 _WKPHPluginInstanceWheelEvent(m_pluginHostProxy->port(), m_pluginID, requestID, 481 [event timestamp], [event modifierFlags], 482 pluginPoint.x, pluginPoint.y, [event buttonNumber], 483 [event deltaX], [event deltaY], [event deltaZ]); 484 485 auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); 486 if (!reply.get() || !reply->m_result) 487 return false; 488 489 return true; 490 } 491 492 void NetscapePluginInstanceProxy::print(CGContextRef context, unsigned width, unsigned height) 493 { 494 uint32_t requestID = nextRequestID(); 495 _WKPHPluginInstancePrint(m_pluginHostProxy->port(), m_pluginID, requestID, width, height); 496 497 auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID); 498 if (!reply.get() || !reply->m_returnValue) 499 return; 500 501 RetainPtr<CGDataProvider> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(reply->m_result.get())); 502 RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB()); 503 RetainPtr<CGImageRef> image(AdoptCF, CGImageCreate(width, height, 8, 32, width * 4, colorSpace.get(), kCGImageAlphaFirst, dataProvider.get(), 0, false, kCGRenderingIntentDefault)); 504 505 // Flip the context and draw the image. 506 CGContextSaveGState(context); 507 CGContextTranslateCTM(context, 0.0, height); 508 CGContextScaleCTM(context, 1.0, -1.0); 509 510 CGContextDrawImage(context, CGRectMake(0, 0, width, height), image.get()); 511 512 CGContextRestoreGState(context); 513 } 514 515 void NetscapePluginInstanceProxy::snapshot(CGContextRef context, unsigned width, unsigned height) 516 { 517 uint32_t requestID = nextRequestID(); 518 _WKPHPluginInstanceSnapshot(m_pluginHostProxy->port(), m_pluginID, requestID, width, height); 519 520 auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID); 521 if (!reply.get() || !reply->m_returnValue) 522 return; 523 524 RetainPtr<CGDataProvider> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(reply->m_result.get())); 525 RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB()); 526 RetainPtr<CGImageRef> image(AdoptCF, CGImageCreate(width, height, 8, 32, width * 4, colorSpace.get(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, dataProvider.get(), 0, false, kCGRenderingIntentDefault)); 527 528 CGContextDrawImage(context, CGRectMake(0, 0, width, height), image.get()); 529 } 530 531 void NetscapePluginInstanceProxy::stopTimers() 532 { 533 _WKPHPluginInstanceStopTimers(m_pluginHostProxy->port(), m_pluginID); 534 } 535 536 void NetscapePluginInstanceProxy::status(const char* message) 537 { 538 RetainPtr<CFStringRef> status(AdoptCF, CFStringCreateWithCString(0, message ? message : "", kCFStringEncodingUTF8)); 539 if (!status) 540 return; 541 542 WebView *wv = [m_pluginView webView]; 543 [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status.get()]; 544 } 545 546 NPError NetscapePluginInstanceProxy::loadURL(const char* url, const char* target, const char* postData, uint32_t postLen, LoadURLFlags flags, uint32_t& streamID) 547 { 548 if (!url) 549 return NPERR_INVALID_PARAM; 550 551 NSMutableURLRequest *request = [m_pluginView requestWithURLCString:url]; 552 553 if (flags & IsPost) { 554 NSData *httpBody = nil; 555 556 if (flags & PostDataIsFile) { 557 // If we're posting a file, buf is either a file URL or a path to the file. 558 if (!postData) 559 return NPERR_INVALID_PARAM; 560 RetainPtr<CFStringRef> bufString(AdoptCF, CFStringCreateWithCString(kCFAllocatorDefault, postData, kCFStringEncodingWindowsLatin1)); 561 if (!bufString) 562 return NPERR_INVALID_PARAM; 563 564 NSURL *fileURL = [NSURL _web_URLWithDataAsString:(NSString *)bufString.get()]; 565 NSString *path; 566 if ([fileURL isFileURL]) 567 path = [fileURL path]; 568 else 569 path = (NSString *)bufString.get(); 570 httpBody = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]]; 571 if (!httpBody) 572 return NPERR_FILE_NOT_FOUND; 573 } else 574 httpBody = [NSData dataWithBytes:postData length:postLen]; 575 576 if (![httpBody length]) 577 return NPERR_INVALID_PARAM; 578 579 [request setHTTPMethod:@"POST"]; 580 581 if (flags & AllowHeadersInPostData) { 582 if ([httpBody _web_startsWithBlankLine]) 583 httpBody = [httpBody subdataWithRange:NSMakeRange(1, [httpBody length] - 1)]; 584 else { 585 NSInteger location = [httpBody _web_locationAfterFirstBlankLine]; 586 if (location != NSNotFound) { 587 // If the blank line is somewhere in the middle of postData, everything before is the header. 588 NSData *headerData = [httpBody subdataWithRange:NSMakeRange(0, location)]; 589 NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields]; 590 unsigned dataLength = [httpBody length] - location; 591 592 // Sometimes plugins like to set Content-Length themselves when they post, 593 // but CFNetwork does not like that. So we will remove the header 594 // and instead truncate the data to the requested length. 595 NSString *contentLength = [header objectForKey:@"Content-Length"]; 596 597 if (contentLength) 598 dataLength = min(static_cast<unsigned>([contentLength intValue]), dataLength); 599 [header removeObjectForKey:@"Content-Length"]; 600 601 if ([header count] > 0) 602 [request setAllHTTPHeaderFields:header]; 603 604 // Everything after the blank line is the actual content of the POST. 605 httpBody = [httpBody subdataWithRange:NSMakeRange(location, dataLength)]; 606 } 607 } 608 } 609 610 if (![httpBody length]) 611 return NPERR_INVALID_PARAM; 612 613 // Plug-ins expect to receive uncached data when doing a POST (3347134). 614 [request setCachePolicy:NSURLRequestReloadIgnoringCacheData]; 615 [request setHTTPBody:httpBody]; 616 } 617 618 return loadRequest(request, target, flags & AllowPopups, streamID); 619 } 620 621 void NetscapePluginInstanceProxy::performRequest(PluginRequest* pluginRequest) 622 { 623 ASSERT(m_pluginView); 624 625 NSURLRequest *request = pluginRequest->request(); 626 NSString *frameName = pluginRequest->frameName(); 627 WebFrame *frame = nil; 628 629 NSURL *URL = [request URL]; 630 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; 631 632 ASSERT(frameName || JSString); 633 if (frameName) { 634 // FIXME - need to get rid of this window creation which 635 // bypasses normal targeted link handling 636 frame = kit(core([m_pluginView webFrame])->loader()->findFrameForNavigation(frameName)); 637 if (!frame) { 638 WebView *currentWebView = [m_pluginView webView]; 639 NSDictionary *features = [[NSDictionary alloc] init]; 640 WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView 641 createWebViewWithRequest:nil 642 windowFeatures:features]; 643 [features release]; 644 645 if (!newWebView) { 646 _WKPHLoadURLNotify(m_pluginHostProxy->port(), m_pluginID, pluginRequest->requestID(), NPERR_GENERIC_ERROR); 647 return; 648 } 649 650 frame = [newWebView mainFrame]; 651 core(frame)->tree()->setName(frameName); 652 [[newWebView _UIDelegateForwarder] webViewShow:newWebView]; 653 } 654 } 655 656 if (JSString) { 657 ASSERT(!frame || [m_pluginView webFrame] == frame); 658 evaluateJavaScript(pluginRequest); 659 } else { 660 [frame loadRequest:request]; 661 662 // Check if another plug-in view or even this view is waiting for the frame to load. 663 // If it is, tell it that the load was cancelled because it will be anyway. 664 WebHostedNetscapePluginView *view = [frame _internalLoadDelegate]; 665 if (view != nil) { 666 ASSERT([view isKindOfClass:[WebHostedNetscapePluginView class]]); 667 [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK]; 668 } 669 m_pendingFrameLoads.set(frame, pluginRequest); 670 [frame _setInternalLoadDelegate:m_pluginView]; 671 } 672 673 } 674 675 void NetscapePluginInstanceProxy::webFrameDidFinishLoadWithReason(WebFrame* webFrame, NPReason reason) 676 { 677 FrameLoadMap::iterator it = m_pendingFrameLoads.find(webFrame); 678 ASSERT(it != m_pendingFrameLoads.end()); 679 680 PluginRequest* pluginRequest = it->second.get(); 681 _WKPHLoadURLNotify(m_pluginHostProxy->port(), m_pluginID, pluginRequest->requestID(), reason); 682 683 m_pendingFrameLoads.remove(it); 684 685 [webFrame _setInternalLoadDelegate:nil]; 686 } 687 688 void NetscapePluginInstanceProxy::evaluateJavaScript(PluginRequest* pluginRequest) 689 { 690 NSURL *URL = [pluginRequest->request() URL]; 691 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; 692 ASSERT(JSString); 693 694 NSString *result = [[m_pluginView webFrame] _stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:pluginRequest->allowPopups()]; 695 696 // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop. 697 if (!m_pluginHostProxy) 698 return; 699 700 if (pluginRequest->frameName() != nil) 701 return; 702 703 if ([result length] > 0) { 704 // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does. 705 NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding]; 706 707 RefPtr<HostedNetscapePluginStream> stream = HostedNetscapePluginStream::create(this, pluginRequest->requestID(), pluginRequest->request()); 708 m_streams.add(stream->streamID(), stream); 709 710 RetainPtr<NSURLResponse> response(AdoptNS, [[NSURLResponse alloc] initWithURL:URL 711 MIMEType:@"text/plain" 712 expectedContentLength:[JSData length] 713 textEncodingName:nil]); 714 stream->startStreamWithResponse(response.get()); 715 stream->didReceiveData(0, static_cast<const char*>([JSData bytes]), [JSData length]); 716 stream->didFinishLoading(0); 717 } 718 } 719 720 void NetscapePluginInstanceProxy::requestTimerFired(Timer<NetscapePluginInstanceProxy>*) 721 { 722 ASSERT(!m_pluginRequests.isEmpty()); 723 ASSERT(m_pluginView); 724 725 RefPtr<PluginRequest> request = m_pluginRequests.first(); 726 m_pluginRequests.removeFirst(); 727 728 if (!m_pluginRequests.isEmpty()) 729 m_requestTimer.startOneShot(0); 730 731 performRequest(request.get()); 732 } 733 734 NPError NetscapePluginInstanceProxy::loadRequest(NSURLRequest *request, const char* cTarget, bool allowPopups, uint32_t& requestID) 735 { 736 NSURL *URL = [request URL]; 737 738 if (!URL) 739 return NPERR_INVALID_URL; 740 741 // Don't allow requests to be loaded when the document loader is stopping all loaders. 742 DocumentLoader* documentLoader = [[m_pluginView dataSource] _documentLoader]; 743 if (!documentLoader || documentLoader->isStopping()) 744 return NPERR_GENERIC_ERROR; 745 746 NSString *target = nil; 747 if (cTarget) { 748 // Find the frame given the target string. 749 target = [NSString stringWithCString:cTarget encoding:NSISOLatin1StringEncoding]; 750 } 751 WebFrame *frame = [m_pluginView webFrame]; 752 753 // don't let a plugin start any loads if it is no longer part of a document that is being 754 // displayed unless the loads are in the same frame as the plugin. 755 if (documentLoader != core([m_pluginView webFrame])->loader()->activeDocumentLoader() && 756 (!cTarget || [frame findFrameNamed:target] != frame)) { 757 return NPERR_GENERIC_ERROR; 758 } 759 760 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; 761 if (JSString != nil) { 762 if (![[[m_pluginView webView] preferences] isJavaScriptEnabled]) { 763 // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does. 764 return NPERR_GENERIC_ERROR; 765 } 766 } else { 767 if (!core([m_pluginView webFrame])->document()->securityOrigin()->canDisplay(URL)) 768 return NPERR_GENERIC_ERROR; 769 } 770 771 // FIXME: Handle wraparound 772 requestID = ++m_currentURLRequestID; 773 774 if (cTarget || JSString) { 775 // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't 776 // want to potentially kill the plug-in inside of its URL request. 777 778 if (JSString && target && [frame findFrameNamed:target] != frame) { 779 // For security reasons, only allow JS requests to be made on the frame that contains the plug-in. 780 return NPERR_INVALID_PARAM; 781 } 782 783 RefPtr<PluginRequest> pluginRequest = PluginRequest::create(requestID, request, target, allowPopups); 784 m_pluginRequests.append(pluginRequest.release()); 785 m_requestTimer.startOneShot(0); 786 } else { 787 RefPtr<HostedNetscapePluginStream> stream = HostedNetscapePluginStream::create(this, requestID, request); 788 789 ASSERT(!m_streams.contains(requestID)); 790 m_streams.add(requestID, stream); 791 stream->start(); 792 } 793 794 return NPERR_NO_ERROR; 795 } 796 797 NetscapePluginInstanceProxy::Reply* NetscapePluginInstanceProxy::processRequestsAndWaitForReply(uint32_t requestID) 798 { 799 Reply* reply = 0; 800 801 ASSERT(m_pluginHostProxy); 802 while (!(reply = m_replies.take(requestID))) { 803 if (!m_pluginHostProxy->processRequests()) 804 return 0; 805 806 // The host proxy can be destroyed while executing a nested processRequests() call, in which case it's normal 807 // to get a success result, but be unable to keep looping. 808 if (!m_pluginHostProxy) 809 return 0; 810 } 811 812 ASSERT(reply); 813 return reply; 814 } 815 816 // NPRuntime support 817 bool NetscapePluginInstanceProxy::getWindowNPObject(uint32_t& objectID) 818 { 819 Frame* frame = core([m_pluginView webFrame]); 820 if (!frame) 821 return false; 822 823 if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript)) 824 objectID = 0; 825 else 826 objectID = m_localObjects.idForObject(*pluginWorld()->globalData(), frame->script()->windowShell(pluginWorld())->window()); 827 828 return true; 829 } 830 831 bool NetscapePluginInstanceProxy::getPluginElementNPObject(uint32_t& objectID) 832 { 833 Frame* frame = core([m_pluginView webFrame]); 834 if (!frame) 835 return false; 836 837 if (JSObject* object = frame->script()->jsObjectForPluginElement([m_pluginView element])) 838 objectID = m_localObjects.idForObject(*pluginWorld()->globalData(), object); 839 else 840 objectID = 0; 841 842 return true; 843 } 844 845 bool NetscapePluginInstanceProxy::forgetBrowserObjectID(uint32_t objectID) 846 { 847 return m_localObjects.forget(objectID); 848 } 849 850 bool NetscapePluginInstanceProxy::evaluate(uint32_t objectID, const String& script, data_t& resultData, mach_msg_type_number_t& resultLength, bool allowPopups) 851 { 852 resultData = 0; 853 resultLength = 0; 854 855 if (m_inDestroy) 856 return false; 857 858 if (!m_localObjects.contains(objectID)) { 859 LOG_ERROR("NetscapePluginInstanceProxy::evaluate: local object %u doesn't exist.", objectID); 860 return false; 861 } 862 863 Frame* frame = core([m_pluginView webFrame]); 864 if (!frame) 865 return false; 866 867 JSLock lock(SilenceAssertionsOnly); 868 869 Strong<JSGlobalObject> globalObject(*pluginWorld()->globalData(), frame->script()->globalObject(pluginWorld())); 870 ExecState* exec = globalObject->globalExec(); 871 872 bool oldAllowPopups = frame->script()->allowPopupsFromPlugin(); 873 frame->script()->setAllowPopupsFromPlugin(allowPopups); 874 875 globalObject->globalData().timeoutChecker.start(); 876 Completion completion = JSC::evaluate(exec, globalObject->globalScopeChain(), makeSource(script)); 877 globalObject->globalData().timeoutChecker.stop(); 878 ComplType type = completion.complType(); 879 880 frame->script()->setAllowPopupsFromPlugin(oldAllowPopups); 881 882 JSValue result; 883 if (type == Normal) 884 result = completion.value(); 885 886 if (!result) 887 result = jsUndefined(); 888 889 marshalValue(exec, result, resultData, resultLength); 890 exec->clearException(); 891 return true; 892 } 893 894 bool NetscapePluginInstanceProxy::invoke(uint32_t objectID, const Identifier& methodName, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength) 895 { 896 resultData = 0; 897 resultLength = 0; 898 899 if (m_inDestroy) 900 return false; 901 902 JSObject* object = m_localObjects.get(objectID); 903 if (!object) { 904 LOG_ERROR("NetscapePluginInstanceProxy::invoke: local object %u doesn't exist.", objectID); 905 return false; 906 } 907 908 Frame* frame = core([m_pluginView webFrame]); 909 if (!frame) 910 return false; 911 912 ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); 913 JSLock lock(SilenceAssertionsOnly); 914 JSValue function = object->get(exec, methodName); 915 CallData callData; 916 CallType callType = getCallData(function, callData); 917 if (callType == CallTypeNone) 918 return false; 919 920 MarkedArgumentBuffer argList; 921 demarshalValues(exec, argumentsData, argumentsLength, argList); 922 923 RefPtr<JSGlobalData> globalData = pluginWorld()->globalData(); 924 globalData->timeoutChecker.start(); 925 JSValue value = call(exec, function, callType, callData, object, argList); 926 globalData->timeoutChecker.stop(); 927 928 marshalValue(exec, value, resultData, resultLength); 929 exec->clearException(); 930 return true; 931 } 932 933 bool NetscapePluginInstanceProxy::invokeDefault(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength) 934 { 935 if (m_inDestroy) 936 return false; 937 938 JSObject* object = m_localObjects.get(objectID); 939 if (!object) { 940 LOG_ERROR("NetscapePluginInstanceProxy::invokeDefault: local object %u doesn't exist.", objectID); 941 return false; 942 } 943 944 Frame* frame = core([m_pluginView webFrame]); 945 if (!frame) 946 return false; 947 948 ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); 949 JSLock lock(SilenceAssertionsOnly); 950 CallData callData; 951 CallType callType = object->getCallData(callData); 952 if (callType == CallTypeNone) 953 return false; 954 955 MarkedArgumentBuffer argList; 956 demarshalValues(exec, argumentsData, argumentsLength, argList); 957 958 RefPtr<JSGlobalData> globalData = pluginWorld()->globalData(); 959 globalData->timeoutChecker.start(); 960 JSValue value = call(exec, object, callType, callData, object, argList); 961 globalData->timeoutChecker.stop(); 962 963 marshalValue(exec, value, resultData, resultLength); 964 exec->clearException(); 965 return true; 966 } 967 968 bool NetscapePluginInstanceProxy::construct(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength) 969 { 970 if (m_inDestroy) 971 return false; 972 973 JSObject* object = m_localObjects.get(objectID); 974 if (!object) { 975 LOG_ERROR("NetscapePluginInstanceProxy::construct: local object %u doesn't exist.", objectID); 976 return false; 977 } 978 979 Frame* frame = core([m_pluginView webFrame]); 980 if (!frame) 981 return false; 982 983 ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); 984 JSLock lock(SilenceAssertionsOnly); 985 986 ConstructData constructData; 987 ConstructType constructType = object->getConstructData(constructData); 988 if (constructType == ConstructTypeNone) 989 return false; 990 991 MarkedArgumentBuffer argList; 992 demarshalValues(exec, argumentsData, argumentsLength, argList); 993 994 RefPtr<JSGlobalData> globalData = pluginWorld()->globalData(); 995 globalData->timeoutChecker.start(); 996 JSValue value = JSC::construct(exec, object, constructType, constructData, argList); 997 globalData->timeoutChecker.stop(); 998 999 marshalValue(exec, value, resultData, resultLength); 1000 exec->clearException(); 1001 return true; 1002 } 1003 1004 bool NetscapePluginInstanceProxy::getProperty(uint32_t objectID, const Identifier& propertyName, data_t& resultData, mach_msg_type_number_t& resultLength) 1005 { 1006 if (m_inDestroy) 1007 return false; 1008 1009 JSObject* object = m_localObjects.get(objectID); 1010 if (!object) { 1011 LOG_ERROR("NetscapePluginInstanceProxy::getProperty: local object %u doesn't exist.", objectID); 1012 return false; 1013 } 1014 1015 Frame* frame = core([m_pluginView webFrame]); 1016 if (!frame) 1017 return false; 1018 1019 ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); 1020 JSLock lock(SilenceAssertionsOnly); 1021 JSValue value = object->get(exec, propertyName); 1022 1023 marshalValue(exec, value, resultData, resultLength); 1024 exec->clearException(); 1025 return true; 1026 } 1027 1028 bool NetscapePluginInstanceProxy::getProperty(uint32_t objectID, unsigned propertyName, data_t& resultData, mach_msg_type_number_t& resultLength) 1029 { 1030 JSObject* object = m_localObjects.get(objectID); 1031 if (!object) { 1032 LOG_ERROR("NetscapePluginInstanceProxy::getProperty: local object %u doesn't exist.", objectID); 1033 return false; 1034 } 1035 1036 Frame* frame = core([m_pluginView webFrame]); 1037 if (!frame) 1038 return false; 1039 1040 ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); 1041 JSLock lock(SilenceAssertionsOnly); 1042 JSValue value = object->get(exec, propertyName); 1043 1044 marshalValue(exec, value, resultData, resultLength); 1045 exec->clearException(); 1046 return true; 1047 } 1048 1049 bool NetscapePluginInstanceProxy::setProperty(uint32_t objectID, const Identifier& propertyName, data_t valueData, mach_msg_type_number_t valueLength) 1050 { 1051 if (m_inDestroy) 1052 return false; 1053 1054 JSObject* object = m_localObjects.get(objectID); 1055 if (!object) { 1056 LOG_ERROR("NetscapePluginInstanceProxy::setProperty: local object %u doesn't exist.", objectID); 1057 return false; 1058 } 1059 1060 Frame* frame = core([m_pluginView webFrame]); 1061 if (!frame) 1062 return false; 1063 1064 ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); 1065 JSLock lock(SilenceAssertionsOnly); 1066 1067 JSValue value = demarshalValue(exec, valueData, valueLength); 1068 PutPropertySlot slot; 1069 object->put(exec, propertyName, value, slot); 1070 1071 exec->clearException(); 1072 return true; 1073 } 1074 1075 bool NetscapePluginInstanceProxy::setProperty(uint32_t objectID, unsigned propertyName, data_t valueData, mach_msg_type_number_t valueLength) 1076 { 1077 if (m_inDestroy) 1078 return false; 1079 1080 JSObject* object = m_localObjects.get(objectID); 1081 if (!object) { 1082 LOG_ERROR("NetscapePluginInstanceProxy::setProperty: local object %u doesn't exist.", objectID); 1083 return false; 1084 } 1085 1086 Frame* frame = core([m_pluginView webFrame]); 1087 if (!frame) 1088 return false; 1089 1090 ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); 1091 JSLock lock(SilenceAssertionsOnly); 1092 1093 JSValue value = demarshalValue(exec, valueData, valueLength); 1094 object->put(exec, propertyName, value); 1095 1096 exec->clearException(); 1097 return true; 1098 } 1099 1100 bool NetscapePluginInstanceProxy::removeProperty(uint32_t objectID, const Identifier& propertyName) 1101 { 1102 if (m_inDestroy) 1103 return false; 1104 1105 JSObject* object = m_localObjects.get(objectID); 1106 if (!object) { 1107 LOG_ERROR("NetscapePluginInstanceProxy::removeProperty: local object %u doesn't exist.", objectID); 1108 return false; 1109 } 1110 1111 Frame* frame = core([m_pluginView webFrame]); 1112 if (!frame) 1113 return false; 1114 1115 ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); 1116 if (!object->hasProperty(exec, propertyName)) { 1117 exec->clearException(); 1118 return false; 1119 } 1120 1121 JSLock lock(SilenceAssertionsOnly); 1122 object->deleteProperty(exec, propertyName); 1123 exec->clearException(); 1124 return true; 1125 } 1126 1127 bool NetscapePluginInstanceProxy::removeProperty(uint32_t objectID, unsigned propertyName) 1128 { 1129 if (m_inDestroy) 1130 return false; 1131 1132 JSObject* object = m_localObjects.get(objectID); 1133 if (!object) { 1134 LOG_ERROR("NetscapePluginInstanceProxy::removeProperty: local object %u doesn't exist.", objectID); 1135 return false; 1136 } 1137 1138 Frame* frame = core([m_pluginView webFrame]); 1139 if (!frame) 1140 return false; 1141 1142 ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); 1143 if (!object->hasProperty(exec, propertyName)) { 1144 exec->clearException(); 1145 return false; 1146 } 1147 1148 JSLock lock(SilenceAssertionsOnly); 1149 object->deleteProperty(exec, propertyName); 1150 exec->clearException(); 1151 return true; 1152 } 1153 1154 bool NetscapePluginInstanceProxy::hasProperty(uint32_t objectID, const Identifier& propertyName) 1155 { 1156 if (m_inDestroy) 1157 return false; 1158 1159 JSObject* object = m_localObjects.get(objectID); 1160 if (!object) { 1161 LOG_ERROR("NetscapePluginInstanceProxy::hasProperty: local object %u doesn't exist.", objectID); 1162 return false; 1163 } 1164 1165 Frame* frame = core([m_pluginView webFrame]); 1166 if (!frame) 1167 return false; 1168 1169 ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); 1170 bool result = object->hasProperty(exec, propertyName); 1171 exec->clearException(); 1172 1173 return result; 1174 } 1175 1176 bool NetscapePluginInstanceProxy::hasProperty(uint32_t objectID, unsigned propertyName) 1177 { 1178 if (m_inDestroy) 1179 return false; 1180 1181 JSObject* object = m_localObjects.get(objectID); 1182 if (!object) { 1183 LOG_ERROR("NetscapePluginInstanceProxy::hasProperty: local object %u doesn't exist.", objectID); 1184 return false; 1185 } 1186 1187 Frame* frame = core([m_pluginView webFrame]); 1188 if (!frame) 1189 return false; 1190 1191 ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); 1192 bool result = object->hasProperty(exec, propertyName); 1193 exec->clearException(); 1194 1195 return result; 1196 } 1197 1198 bool NetscapePluginInstanceProxy::hasMethod(uint32_t objectID, const Identifier& methodName) 1199 { 1200 if (m_inDestroy) 1201 return false; 1202 1203 JSObject* object = m_localObjects.get(objectID); 1204 if (!object) { 1205 LOG_ERROR("NetscapePluginInstanceProxy::hasMethod: local object %u doesn't exist.", objectID); 1206 return false; 1207 } 1208 1209 Frame* frame = core([m_pluginView webFrame]); 1210 if (!frame) 1211 return false; 1212 1213 ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); 1214 JSLock lock(SilenceAssertionsOnly); 1215 JSValue func = object->get(exec, methodName); 1216 exec->clearException(); 1217 return !func.isUndefined(); 1218 } 1219 1220 bool NetscapePluginInstanceProxy::enumerate(uint32_t objectID, data_t& resultData, mach_msg_type_number_t& resultLength) 1221 { 1222 if (m_inDestroy) 1223 return false; 1224 1225 JSObject* object = m_localObjects.get(objectID); 1226 if (!object) { 1227 LOG_ERROR("NetscapePluginInstanceProxy::enumerate: local object %u doesn't exist.", objectID); 1228 return false; 1229 } 1230 1231 Frame* frame = core([m_pluginView webFrame]); 1232 if (!frame) 1233 return false; 1234 1235 ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); 1236 JSLock lock(SilenceAssertionsOnly); 1237 1238 PropertyNameArray propertyNames(exec); 1239 object->getPropertyNames(exec, propertyNames); 1240 1241 RetainPtr<NSMutableArray*> array(AdoptNS, [[NSMutableArray alloc] init]); 1242 for (unsigned i = 0; i < propertyNames.size(); i++) { 1243 uint64_t methodName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(propertyNames[i].ustring().utf8().data())); 1244 1245 [array.get() addObject:[NSNumber numberWithLongLong:methodName]]; 1246 } 1247 1248 NSData *data = [NSPropertyListSerialization dataFromPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:0]; 1249 ASSERT(data); 1250 1251 resultLength = [data length]; 1252 mig_allocate(reinterpret_cast<vm_address_t*>(&resultData), resultLength); 1253 1254 memcpy(resultData, [data bytes], resultLength); 1255 1256 exec->clearException(); 1257 1258 return true; 1259 } 1260 1261 void NetscapePluginInstanceProxy::addValueToArray(NSMutableArray *array, ExecState* exec, JSValue value) 1262 { 1263 JSLock lock(SilenceAssertionsOnly); 1264 1265 if (value.isString()) { 1266 [array addObject:[NSNumber numberWithInt:StringValueType]]; 1267 [array addObject:ustringToString(value.toString(exec))]; 1268 } else if (value.isNumber()) { 1269 [array addObject:[NSNumber numberWithInt:DoubleValueType]]; 1270 [array addObject:[NSNumber numberWithDouble:value.toNumber(exec)]]; 1271 } else if (value.isBoolean()) { 1272 [array addObject:[NSNumber numberWithInt:BoolValueType]]; 1273 [array addObject:[NSNumber numberWithBool:value.toBoolean(exec)]]; 1274 } else if (value.isNull()) 1275 [array addObject:[NSNumber numberWithInt:NullValueType]]; 1276 else if (value.isObject()) { 1277 JSObject* object = asObject(value); 1278 if (object->classInfo() == &ProxyRuntimeObject::s_info) { 1279 ProxyRuntimeObject* runtimeObject = static_cast<ProxyRuntimeObject*>(object); 1280 if (ProxyInstance* instance = runtimeObject->getInternalProxyInstance()) { 1281 [array addObject:[NSNumber numberWithInt:NPObjectValueType]]; 1282 [array addObject:[NSNumber numberWithInt:instance->objectID()]]; 1283 } 1284 } else { 1285 [array addObject:[NSNumber numberWithInt:JSObjectValueType]]; 1286 [array addObject:[NSNumber numberWithInt:m_localObjects.idForObject(exec->globalData(), object)]]; 1287 } 1288 } else 1289 [array addObject:[NSNumber numberWithInt:VoidValueType]]; 1290 } 1291 1292 void NetscapePluginInstanceProxy::marshalValue(ExecState* exec, JSValue value, data_t& resultData, mach_msg_type_number_t& resultLength) 1293 { 1294 RetainPtr<NSMutableArray*> array(AdoptNS, [[NSMutableArray alloc] init]); 1295 1296 addValueToArray(array.get(), exec, value); 1297 1298 RetainPtr<NSData *> data = [NSPropertyListSerialization dataFromPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:0]; 1299 ASSERT(data); 1300 1301 resultLength = [data.get() length]; 1302 mig_allocate(reinterpret_cast<vm_address_t*>(&resultData), resultLength); 1303 1304 memcpy(resultData, [data.get() bytes], resultLength); 1305 } 1306 1307 RetainPtr<NSData *> NetscapePluginInstanceProxy::marshalValues(ExecState* exec, const ArgList& args) 1308 { 1309 RetainPtr<NSMutableArray*> array(AdoptNS, [[NSMutableArray alloc] init]); 1310 1311 for (unsigned i = 0; i < args.size(); i++) 1312 addValueToArray(array.get(), exec, args.at(i)); 1313 1314 RetainPtr<NSData *> data = [NSPropertyListSerialization dataFromPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:0]; 1315 ASSERT(data); 1316 1317 return data; 1318 } 1319 1320 bool NetscapePluginInstanceProxy::demarshalValueFromArray(ExecState* exec, NSArray *array, NSUInteger& index, JSValue& result) 1321 { 1322 if (index == [array count]) 1323 return false; 1324 1325 int type = [[array objectAtIndex:index++] intValue]; 1326 switch (type) { 1327 case VoidValueType: 1328 result = jsUndefined(); 1329 return true; 1330 case NullValueType: 1331 result = jsNull(); 1332 return true; 1333 case BoolValueType: 1334 result = jsBoolean([[array objectAtIndex:index++] boolValue]); 1335 return true; 1336 case DoubleValueType: 1337 result = jsNumber([[array objectAtIndex:index++] doubleValue]); 1338 return true; 1339 case StringValueType: { 1340 NSString *string = [array objectAtIndex:index++]; 1341 1342 result = jsString(exec, String(string)); 1343 return true; 1344 } 1345 case JSObjectValueType: { 1346 uint32_t objectID = [[array objectAtIndex:index++] intValue]; 1347 1348 result = m_localObjects.get(objectID); 1349 ASSERT(result); 1350 return true; 1351 } 1352 case NPObjectValueType: { 1353 uint32_t objectID = [[array objectAtIndex:index++] intValue]; 1354 1355 Frame* frame = core([m_pluginView webFrame]); 1356 if (!frame) 1357 return false; 1358 1359 if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript)) 1360 return false; 1361 1362 RefPtr<RootObject> rootObject = frame->script()->createRootObject(m_pluginView); 1363 if (!rootObject) 1364 return false; 1365 1366 result = ProxyInstance::create(rootObject.release(), this, objectID)->createRuntimeObject(exec); 1367 return true; 1368 } 1369 default: 1370 ASSERT_NOT_REACHED(); 1371 return false; 1372 } 1373 } 1374 1375 JSValue NetscapePluginInstanceProxy::demarshalValue(ExecState* exec, const char* valueData, mach_msg_type_number_t valueLength) 1376 { 1377 RetainPtr<NSData*> data(AdoptNS, [[NSData alloc] initWithBytesNoCopy:(void*)valueData length:valueLength freeWhenDone:NO]); 1378 1379 RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:data.get() 1380 mutabilityOption:NSPropertyListImmutable 1381 format:0 1382 errorDescription:0]; 1383 NSUInteger position = 0; 1384 JSValue value; 1385 bool result = demarshalValueFromArray(exec, array.get(), position, value); 1386 ASSERT_UNUSED(result, result); 1387 1388 return value; 1389 } 1390 1391 void NetscapePluginInstanceProxy::demarshalValues(ExecState* exec, data_t valuesData, mach_msg_type_number_t valuesLength, MarkedArgumentBuffer& result) 1392 { 1393 RetainPtr<NSData*> data(AdoptNS, [[NSData alloc] initWithBytesNoCopy:valuesData length:valuesLength freeWhenDone:NO]); 1394 1395 RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:data.get() 1396 mutabilityOption:NSPropertyListImmutable 1397 format:0 1398 errorDescription:0]; 1399 NSUInteger position = 0; 1400 JSValue value; 1401 while (demarshalValueFromArray(exec, array.get(), position, value)) 1402 result.append(value); 1403 } 1404 1405 void NetscapePluginInstanceProxy::retainLocalObject(JSC::JSValue value) 1406 { 1407 if (!value.isObject() || value.inherits(&ProxyRuntimeObject::s_info)) 1408 return; 1409 1410 m_localObjects.retain(asObject(value)); 1411 } 1412 1413 void NetscapePluginInstanceProxy::releaseLocalObject(JSC::JSValue value) 1414 { 1415 if (!value.isObject() || value.inherits(&ProxyRuntimeObject::s_info)) 1416 return; 1417 1418 m_localObjects.release(asObject(value)); 1419 } 1420 1421 PassRefPtr<Instance> NetscapePluginInstanceProxy::createBindingsInstance(PassRefPtr<RootObject> rootObject) 1422 { 1423 uint32_t requestID = nextRequestID(); 1424 1425 if (_WKPHGetScriptableNPObject(m_pluginHostProxy->port(), m_pluginID, requestID) != KERN_SUCCESS) 1426 return 0; 1427 1428 auto_ptr<GetScriptableNPObjectReply> reply = waitForReply<GetScriptableNPObjectReply>(requestID); 1429 if (!reply.get()) 1430 return 0; 1431 1432 if (!reply->m_objectID) 1433 return 0; 1434 1435 // Since the reply was non-null, "this" is still a valid pointer. 1436 return ProxyInstance::create(rootObject, this, reply->m_objectID); 1437 } 1438 1439 void NetscapePluginInstanceProxy::addInstance(ProxyInstance* instance) 1440 { 1441 ASSERT(!m_instances.contains(instance)); 1442 1443 m_instances.add(instance); 1444 } 1445 1446 void NetscapePluginInstanceProxy::removeInstance(ProxyInstance* instance) 1447 { 1448 ASSERT(m_instances.contains(instance)); 1449 1450 m_instances.remove(instance); 1451 } 1452 1453 void NetscapePluginInstanceProxy::willCallPluginFunction() 1454 { 1455 m_pluginFunctionCallDepth++; 1456 } 1457 1458 void NetscapePluginInstanceProxy::didCallPluginFunction(bool& stopped) 1459 { 1460 ASSERT(m_pluginFunctionCallDepth > 0); 1461 m_pluginFunctionCallDepth--; 1462 1463 // If -stop was called while we were calling into a plug-in function, and we're no longer 1464 // inside a plug-in function, stop now. 1465 if (!m_pluginFunctionCallDepth && m_shouldStopSoon) { 1466 m_shouldStopSoon = false; 1467 [m_pluginView stop]; 1468 stopped = true; 1469 } 1470 } 1471 1472 bool NetscapePluginInstanceProxy::shouldStop() 1473 { 1474 if (m_pluginFunctionCallDepth) { 1475 m_shouldStopSoon = true; 1476 return false; 1477 } 1478 1479 return true; 1480 } 1481 1482 uint32_t NetscapePluginInstanceProxy::nextRequestID() 1483 { 1484 uint32_t requestID = ++m_currentRequestID; 1485 1486 // We don't want to return the HashMap empty/deleted "special keys" 1487 if (requestID == 0 || requestID == static_cast<uint32_t>(-1)) 1488 return nextRequestID(); 1489 1490 return requestID; 1491 } 1492 1493 void NetscapePluginInstanceProxy::invalidateRect(double x, double y, double width, double height) 1494 { 1495 ASSERT(m_pluginView); 1496 1497 m_pluginIsWaitingForDraw = true; 1498 [m_pluginView invalidatePluginContentRect:NSMakeRect(x, y, width, height)]; 1499 } 1500 1501 void NetscapePluginInstanceProxy::didDraw() 1502 { 1503 if (!m_pluginIsWaitingForDraw) 1504 return; 1505 1506 m_pluginIsWaitingForDraw = false; 1507 _WKPHPluginInstanceDidDraw(m_pluginHostProxy->port(), m_pluginID); 1508 } 1509 1510 bool NetscapePluginInstanceProxy::getCookies(data_t urlData, mach_msg_type_number_t urlLength, data_t& cookiesData, mach_msg_type_number_t& cookiesLength) 1511 { 1512 ASSERT(m_pluginView); 1513 1514 NSURL *url = [m_pluginView URLWithCString:urlData]; 1515 if (!url) 1516 return false; 1517 1518 if (Frame* frame = core([m_pluginView webFrame])) { 1519 String cookieString = cookies(frame->document(), url); 1520 WTF::CString cookieStringUTF8 = cookieString.utf8(); 1521 if (cookieStringUTF8.isNull()) 1522 return false; 1523 1524 cookiesLength = cookieStringUTF8.length(); 1525 mig_allocate(reinterpret_cast<vm_address_t*>(&cookiesData), cookiesLength); 1526 memcpy(cookiesData, cookieStringUTF8.data(), cookiesLength); 1527 1528 return true; 1529 } 1530 1531 return false; 1532 } 1533 1534 bool NetscapePluginInstanceProxy::setCookies(data_t urlData, mach_msg_type_number_t urlLength, data_t cookiesData, mach_msg_type_number_t cookiesLength) 1535 { 1536 ASSERT(m_pluginView); 1537 1538 NSURL *url = [m_pluginView URLWithCString:urlData]; 1539 if (!url) 1540 return false; 1541 1542 if (Frame* frame = core([m_pluginView webFrame])) { 1543 String cookieString = String::fromUTF8(cookiesData, cookiesLength); 1544 if (!cookieString) 1545 return false; 1546 1547 WebCore::setCookies(frame->document(), url, cookieString); 1548 return true; 1549 } 1550 1551 return false; 1552 } 1553 1554 bool NetscapePluginInstanceProxy::getProxy(data_t urlData, mach_msg_type_number_t urlLength, data_t& proxyData, mach_msg_type_number_t& proxyLength) 1555 { 1556 ASSERT(m_pluginView); 1557 1558 NSURL *url = [m_pluginView URLWithCString:urlData]; 1559 if (!url) 1560 return false; 1561 1562 Vector<ProxyServer> proxyServers = proxyServersForURL(url, 0); 1563 WTF::CString proxyStringUTF8 = toString(proxyServers).utf8(); 1564 1565 proxyLength = proxyStringUTF8.length(); 1566 mig_allocate(reinterpret_cast<vm_address_t*>(&proxyData), proxyLength); 1567 memcpy(proxyData, proxyStringUTF8.data(), proxyLength); 1568 1569 return true; 1570 } 1571 1572 bool NetscapePluginInstanceProxy::getAuthenticationInfo(data_t protocolData, data_t hostData, uint32_t port, data_t schemeData, data_t realmData, 1573 data_t& usernameData, mach_msg_type_number_t& usernameLength, data_t& passwordData, mach_msg_type_number_t& passwordLength) 1574 { 1575 WTF::CString username; 1576 WTF::CString password; 1577 1578 if (!WebKit::getAuthenticationInfo(protocolData, hostData, port, schemeData, realmData, username, password)) 1579 return false; 1580 1581 usernameLength = username.length(); 1582 mig_allocate(reinterpret_cast<vm_address_t*>(&usernameData), usernameLength); 1583 memcpy(usernameData, username.data(), usernameLength); 1584 1585 passwordLength = password.length(); 1586 mig_allocate(reinterpret_cast<vm_address_t*>(&passwordData), passwordLength); 1587 memcpy(passwordData, password.data(), passwordLength); 1588 1589 return true; 1590 } 1591 1592 bool NetscapePluginInstanceProxy::convertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, 1593 double& destX, double& destY, NPCoordinateSpace destSpace) 1594 { 1595 ASSERT(m_pluginView); 1596 1597 return [m_pluginView convertFromX:sourceX andY:sourceY space:sourceSpace toX:&destX andY:&destY space:destSpace]; 1598 } 1599 1600 uint32_t NetscapePluginInstanceProxy::checkIfAllowedToLoadURL(const char* url, const char* target) 1601 { 1602 uint32_t checkID; 1603 1604 // Assign a check ID 1605 do { 1606 checkID = ++m_urlCheckCounter; 1607 } while (m_urlChecks.contains(checkID) || !m_urlCheckCounter); 1608 1609 NSString *frameName = target ? [NSString stringWithCString:target encoding:NSISOLatin1StringEncoding] : nil; 1610 1611 NSNumber *contextInfo = [[NSNumber alloc] initWithUnsignedInt:checkID]; 1612 WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:[m_pluginView requestWithURLCString:url] 1613 target:frameName 1614 resultObject:m_pluginView 1615 selector:@selector(_containerCheckResult:contextInfo:) 1616 controller:m_pluginView 1617 contextInfo:contextInfo]; 1618 1619 [contextInfo release]; 1620 m_urlChecks.set(checkID, check); 1621 [check start]; 1622 1623 return checkID; 1624 } 1625 1626 void NetscapePluginInstanceProxy::cancelCheckIfAllowedToLoadURL(uint32_t checkID) 1627 { 1628 URLCheckMap::iterator it = m_urlChecks.find(checkID); 1629 if (it == m_urlChecks.end()) 1630 return; 1631 1632 WebPluginContainerCheck *check = it->second.get(); 1633 [check cancel]; 1634 m_urlChecks.remove(it); 1635 } 1636 1637 void NetscapePluginInstanceProxy::checkIfAllowedToLoadURLResult(uint32_t checkID, bool allowed) 1638 { 1639 _WKPHCheckIfAllowedToLoadURLResult(m_pluginHostProxy->port(), m_pluginID, checkID, allowed); 1640 } 1641 1642 void NetscapePluginInstanceProxy::resolveURL(const char* url, const char* target, data_t& resolvedURLData, mach_msg_type_number_t& resolvedURLLength) 1643 { 1644 ASSERT(m_pluginView); 1645 1646 WTF::CString resolvedURL = [m_pluginView resolvedURLStringForURL:url target:target]; 1647 1648 resolvedURLLength = resolvedURL.length(); 1649 mig_allocate(reinterpret_cast<vm_address_t*>(&resolvedURLData), resolvedURLLength); 1650 memcpy(resolvedURLData, resolvedURL.data(), resolvedURLLength); 1651 } 1652 1653 void NetscapePluginInstanceProxy::privateBrowsingModeDidChange(bool isPrivateBrowsingEnabled) 1654 { 1655 _WKPHPluginInstancePrivateBrowsingModeDidChange(m_pluginHostProxy->port(), m_pluginID, isPrivateBrowsingEnabled); 1656 } 1657 1658 static String& globalExceptionString() 1659 { 1660 DEFINE_STATIC_LOCAL(String, exceptionString, ()); 1661 return exceptionString; 1662 } 1663 1664 void NetscapePluginInstanceProxy::setGlobalException(const String& exception) 1665 { 1666 globalExceptionString() = exception; 1667 } 1668 1669 void NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(ExecState* exec) 1670 { 1671 if (globalExceptionString().isNull()) 1672 return; 1673 1674 { 1675 JSLock lock(SilenceAssertionsOnly); 1676 throwError(exec, createError(exec, stringToUString(globalExceptionString()))); 1677 } 1678 1679 globalExceptionString() = String(); 1680 } 1681 1682 } // namespace WebKit 1683 1684 #endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) 1685