| /* |
| * Copyright (C) 2008-2019 Apple Inc. All Rights Reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) |
| |
| #import "NetscapePluginInstanceProxy.h" |
| |
| #import "HostedNetscapePluginStream.h" |
| #import "NetscapePluginHostProxy.h" |
| #import "ProxyInstance.h" |
| #import "ProxyRuntimeObject.h" |
| #import "WebDataSourceInternal.h" |
| #import "WebFrameInternal.h" |
| #import "WebHostedNetscapePluginView.h" |
| #import "WebKitNSStringExtras.h" |
| #import "WebNSDataExtras.h" |
| #import "WebNSURLExtras.h" |
| #import "WebPluginRequest.h" |
| #import "WebUIDelegate.h" |
| #import "WebUIDelegatePrivate.h" |
| #import "WebViewInternal.h" |
| #import <JavaScriptCore/CatchScope.h> |
| #import <JavaScriptCore/Completion.h> |
| #import <JavaScriptCore/Error.h> |
| #import <JavaScriptCore/JSGlobalObjectInlines.h> |
| #import <JavaScriptCore/JSLock.h> |
| #import <JavaScriptCore/PropertyNameArray.h> |
| #import <JavaScriptCore/SourceCode.h> |
| #import <JavaScriptCore/StrongInlines.h> |
| #import <WebCore/CookieJar.h> |
| #import <WebCore/Document.h> |
| #import <WebCore/DocumentLoader.h> |
| #import <WebCore/Frame.h> |
| #import <WebCore/FrameLoader.h> |
| #import <WebCore/FrameTree.h> |
| #import <WebCore/Page.h> |
| #import <WebCore/PlatformEventFactoryMac.h> |
| #import <WebCore/ProxyServer.h> |
| #import <WebCore/ScriptController.h> |
| #import <WebCore/SecurityOrigin.h> |
| #import <WebCore/UserGestureIndicator.h> |
| #import <WebCore/npruntime_impl.h> |
| #import <WebCore/runtime_object.h> |
| #import <mach/mach.h> |
| #import <utility> |
| #import <wtf/NeverDestroyed.h> |
| #import <wtf/RefCountedLeakCounter.h> |
| #import <wtf/URL.h> |
| #import <wtf/text/CString.h> |
| |
| extern "C" { |
| #import "WebKitPluginClientServer.h" |
| #import "WebKitPluginHost.h" |
| } |
| |
| using namespace JSC; |
| using namespace JSC::Bindings; |
| using namespace WebCore; |
| |
| namespace WebKit { |
| |
| class NetscapePluginInstanceProxy::PluginRequest : public RefCounted<NetscapePluginInstanceProxy::PluginRequest> { |
| public: |
| static Ref<PluginRequest> create(uint32_t requestID, NSURLRequest* request, NSString* frameName, bool allowPopups) |
| { |
| return adoptRef(*new PluginRequest(requestID, request, frameName, allowPopups)); |
| } |
| |
| uint32_t requestID() const { return m_requestID; } |
| NSURLRequest* request() const { return m_request.get(); } |
| NSString* frameName() const { return m_frameName.get(); } |
| bool allowPopups() const { return m_allowPopups; } |
| |
| private: |
| PluginRequest(uint32_t requestID, NSURLRequest* request, NSString* frameName, bool allowPopups) |
| : m_requestID(requestID) |
| , m_request(request) |
| , m_frameName(frameName) |
| , m_allowPopups(allowPopups) |
| { |
| } |
| |
| uint32_t m_requestID; |
| RetainPtr<NSURLRequest> m_request; |
| RetainPtr<NSString> m_frameName; |
| bool m_allowPopups; |
| }; |
| |
| NetscapePluginInstanceProxy::LocalObjectMap::LocalObjectMap() |
| : m_objectIDCounter(0) |
| { |
| } |
| |
| NetscapePluginInstanceProxy::LocalObjectMap::~LocalObjectMap() |
| { |
| } |
| |
| inline bool NetscapePluginInstanceProxy::LocalObjectMap::contains(uint32_t objectID) const |
| { |
| return m_idToJSObjectMap.contains(objectID); |
| } |
| |
| inline JSC::JSObject* NetscapePluginInstanceProxy::LocalObjectMap::get(uint32_t objectID) const |
| { |
| if (objectID == HashTraits<uint32_t>::emptyValue() || HashTraits<uint32_t>::isDeletedValue(objectID)) |
| return nullptr; |
| |
| return m_idToJSObjectMap.get(objectID).get(); |
| } |
| |
| uint32_t NetscapePluginInstanceProxy::LocalObjectMap::idForObject(VM& vm, JSObject* object) |
| { |
| // This method creates objects with refcount of 1, but doesn't increase refcount when returning |
| // found objects. This extra count accounts for the main "reference" kept by plugin process. |
| |
| // To avoid excessive IPC, plugin process doesn't send each NPObject release/retain call to |
| // Safari. It only sends one when the last reference is removed, and it can destroy the proxy |
| // NPObject. |
| |
| // However, the browser may be sending the same object out to plug-in as a function call |
| // argument at the same time - neither side can know what the other one is doing. So, |
| // is to make PCForgetBrowserObject call return a boolean result, making it possible for |
| // the browser to make plugin host keep the proxy with zero refcount for a little longer. |
| |
| uint32_t objectID = 0; |
| |
| HashMap<JSC::JSObject*, std::pair<uint32_t, uint32_t>>::iterator iter = m_jsObjectToIDMap.find(object); |
| if (iter != m_jsObjectToIDMap.end()) |
| return iter->value.first; |
| |
| do { |
| objectID = ++m_objectIDCounter; |
| } while (!m_objectIDCounter || m_objectIDCounter == static_cast<uint32_t>(-1) || m_idToJSObjectMap.contains(objectID)); |
| |
| m_idToJSObjectMap.set(objectID, Strong<JSObject>(vm, object)); |
| m_jsObjectToIDMap.set(object, std::make_pair(objectID, 1)); |
| |
| return objectID; |
| } |
| |
| void NetscapePluginInstanceProxy::LocalObjectMap::retain(JSC::JSObject* object) |
| { |
| HashMap<JSC::JSObject*, std::pair<uint32_t, uint32_t>>::iterator iter = m_jsObjectToIDMap.find(object); |
| ASSERT(iter != m_jsObjectToIDMap.end()); |
| |
| iter->value.second = iter->value.second + 1; |
| } |
| |
| void NetscapePluginInstanceProxy::LocalObjectMap::release(JSC::JSObject* object) |
| { |
| HashMap<JSC::JSObject*, std::pair<uint32_t, uint32_t>>::iterator iter = m_jsObjectToIDMap.find(object); |
| ASSERT(iter != m_jsObjectToIDMap.end()); |
| |
| ASSERT(iter->value.second > 0); |
| iter->value.second = iter->value.second - 1; |
| if (!iter->value.second) { |
| m_idToJSObjectMap.remove(iter->value.first); |
| m_jsObjectToIDMap.remove(iter); |
| } |
| } |
| |
| void NetscapePluginInstanceProxy::LocalObjectMap::clear() |
| { |
| m_idToJSObjectMap.clear(); |
| m_jsObjectToIDMap.clear(); |
| } |
| |
| bool NetscapePluginInstanceProxy::LocalObjectMap::forget(uint32_t objectID) |
| { |
| if (objectID == HashTraits<uint32_t>::emptyValue() || HashTraits<uint32_t>::isDeletedValue(objectID)) { |
| LOG_ERROR("NetscapePluginInstanceProxy::LocalObjectMap::forget: local object id %u is not valid.", objectID); |
| return true; |
| } |
| |
| HashMap<uint32_t, JSC::Strong<JSC::JSObject>>::iterator iter = m_idToJSObjectMap.find(objectID); |
| if (iter == m_idToJSObjectMap.end()) { |
| LOG_ERROR("NetscapePluginInstanceProxy::LocalObjectMap::forget: local object %u doesn't exist.", objectID); |
| return true; |
| } |
| |
| HashMap<JSC::JSObject*, std::pair<uint32_t, uint32_t>>::iterator rIter = m_jsObjectToIDMap.find(iter->value.get()); |
| |
| // If the object is being sent to plug-in right now, then it's not the time to forget. |
| if (rIter->value.second != 1) |
| return false; |
| |
| m_jsObjectToIDMap.remove(rIter); |
| m_idToJSObjectMap.remove(iter); |
| return true; |
| } |
| |
| static uint32_t pluginIDCounter; |
| |
| bool NetscapePluginInstanceProxy::m_inDestroy; |
| |
| DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, netscapePluginInstanceProxyCounter, ("NetscapePluginInstanceProxy")); |
| |
| NetscapePluginInstanceProxy::NetscapePluginInstanceProxy(NetscapePluginHostProxy* pluginHostProxy, WebHostedNetscapePluginView *pluginView, bool fullFramePlugin) |
| : m_pluginHostProxy(pluginHostProxy) |
| , m_pluginView(pluginView) |
| , m_requestTimer(*this, &NetscapePluginInstanceProxy::requestTimerFired) |
| , m_currentURLRequestID(0) |
| , m_renderContextID(0) |
| , m_rendererType(UseSoftwareRenderer) |
| , m_waitingForReply(false) |
| , m_pluginFunctionCallDepth(0) |
| , m_shouldStopSoon(false) |
| , m_currentRequestID(0) |
| , m_pluginIsWaitingForDraw(false) |
| { |
| ASSERT(m_pluginView); |
| |
| if (fullFramePlugin) { |
| // For full frame plug-ins, the first requestID will always be the one for the already |
| // open stream. |
| ++m_currentURLRequestID; |
| } |
| |
| // Assign a plug-in ID. |
| do { |
| m_pluginID = ++pluginIDCounter; |
| } while (pluginHostProxy->pluginInstance(m_pluginID) || !m_pluginID); |
| |
| #ifndef NDEBUG |
| netscapePluginInstanceProxyCounter.increment(); |
| #endif |
| } |
| |
| Ref<NetscapePluginInstanceProxy> NetscapePluginInstanceProxy::create(NetscapePluginHostProxy* pluginHostProxy, WebHostedNetscapePluginView *pluginView, bool fullFramePlugin) |
| { |
| auto proxy = adoptRef(*new NetscapePluginInstanceProxy(pluginHostProxy, pluginView, fullFramePlugin)); |
| pluginHostProxy->addPluginInstance(proxy.ptr()); |
| return proxy; |
| } |
| |
| NetscapePluginInstanceProxy::~NetscapePluginInstanceProxy() |
| { |
| ASSERT(!m_pluginHostProxy); |
| |
| m_pluginID = 0; |
| |
| #ifndef NDEBUG |
| netscapePluginInstanceProxyCounter.decrement(); |
| #endif |
| } |
| |
| void NetscapePluginInstanceProxy::resize(NSRect size, NSRect clipRect) |
| { |
| uint32_t requestID = 0; |
| |
| requestID = nextRequestID(); |
| |
| _WKPHResizePluginInstance(m_pluginHostProxy->port(), m_pluginID, requestID, |
| size.origin.x, size.origin.y, size.size.width, size.size.height, |
| clipRect.origin.x, clipRect.origin.y, clipRect.size.width, clipRect.size.height); |
| |
| waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); |
| } |
| |
| |
| void NetscapePluginInstanceProxy::setShouldHostLayersInWindowServer(bool shouldHostLayersInWindowServer) |
| { |
| _WKPHPluginShouldHostLayersInWindowServerChanged(m_pluginHostProxy->port(), m_pluginID, shouldHostLayersInWindowServer); |
| } |
| |
| void NetscapePluginInstanceProxy::layerHostingModeChanged(bool hostsLayersInWindowServer, uint32_t renderContextID) |
| { |
| setRenderContextID(renderContextID); |
| |
| [m_pluginView setHostsLayersInWindowServer:hostsLayersInWindowServer]; |
| } |
| |
| void NetscapePluginInstanceProxy::stopAllStreams() |
| { |
| for (auto& stream : copyToVector(m_streams.values())) |
| stream->stop(); |
| } |
| |
| void NetscapePluginInstanceProxy::cleanup() |
| { |
| stopAllStreams(); |
| |
| m_requestTimer.stop(); |
| |
| // Clear the object map, this will cause any outstanding JS objects that the plug-in had a reference to |
| // to go away when the next garbage collection takes place. |
| m_localObjects.clear(); |
| |
| if (Frame* frame = core([m_pluginView webFrame])) |
| frame->script().cleanupScriptObjectsForPlugin((__bridge void*)m_pluginView); |
| |
| ProxyInstanceSet instances; |
| instances.swap(m_instances); |
| |
| // Invalidate all proxy instances. |
| ProxyInstanceSet::const_iterator end = instances.end(); |
| for (ProxyInstanceSet::const_iterator it = instances.begin(); it != end; ++it) |
| (*it)->invalidate(); |
| |
| m_pluginView = nil; |
| m_manualStream = nullptr; |
| } |
| |
| void NetscapePluginInstanceProxy::invalidate() |
| { |
| // If the plug-in host has died, the proxy will be null. |
| if (!m_pluginHostProxy) |
| return; |
| |
| m_pluginHostProxy->removePluginInstance(this); |
| m_pluginHostProxy = nullptr; |
| } |
| |
| void NetscapePluginInstanceProxy::destroy() |
| { |
| uint32_t requestID = nextRequestID(); |
| |
| ASSERT(!m_inDestroy); |
| m_inDestroy = true; |
| |
| FrameLoadMap::iterator end = m_pendingFrameLoads.end(); |
| for (FrameLoadMap::iterator it = m_pendingFrameLoads.begin(); it != end; ++it) |
| [(it->key) _setInternalLoadDelegate:nil]; |
| |
| _WKPHDestroyPluginInstance(m_pluginHostProxy->port(), m_pluginID, requestID); |
| |
| // If the plug-in host crashes while we're waiting for a reply, the last reference to the instance proxy |
| // will go away. Prevent this by protecting it here. |
| Ref<NetscapePluginInstanceProxy> protect(*this); |
| |
| // We don't care about the reply here - we just want to block until the plug-in instance has been torn down. |
| waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); |
| |
| m_inDestroy = false; |
| |
| cleanup(); |
| invalidate(); |
| } |
| |
| void NetscapePluginInstanceProxy::setManualStream(Ref<HostedNetscapePluginStream>&& manualStream) |
| { |
| ASSERT(!m_manualStream); |
| |
| m_manualStream = WTFMove(manualStream); |
| } |
| |
| bool NetscapePluginInstanceProxy::cancelStreamLoad(uint32_t streamID, NPReason reason) |
| { |
| HostedNetscapePluginStream* stream; |
| |
| if (m_manualStream && streamID == 1) |
| stream = m_manualStream.get(); |
| else |
| stream = m_streams.get(streamID); |
| |
| if (!stream) |
| return false; |
| |
| stream->cancelLoad(reason); |
| return true; |
| } |
| |
| void NetscapePluginInstanceProxy::disconnectStream(HostedNetscapePluginStream* stream) |
| { |
| if (stream == m_manualStream) { |
| m_manualStream = nullptr; |
| return; |
| } |
| |
| ASSERT(m_streams.get(stream->streamID()) == stream); |
| m_streams.remove(stream->streamID()); |
| } |
| |
| void NetscapePluginInstanceProxy::pluginHostDied() |
| { |
| m_pluginHostProxy = nullptr; |
| |
| [m_pluginView pluginHostDied]; |
| |
| cleanup(); |
| } |
| |
| void NetscapePluginInstanceProxy::focusChanged(bool hasFocus) |
| { |
| _WKPHPluginInstanceFocusChanged(m_pluginHostProxy->port(), m_pluginID, hasFocus); |
| } |
| |
| void NetscapePluginInstanceProxy::windowFocusChanged(bool hasFocus) |
| { |
| _WKPHPluginInstanceWindowFocusChanged(m_pluginHostProxy->port(), m_pluginID, hasFocus); |
| } |
| |
| void NetscapePluginInstanceProxy::windowFrameChanged(NSRect frame) |
| { |
| _WKPHPluginInstanceWindowFrameChanged(m_pluginHostProxy->port(), m_pluginID, frame.origin.x, frame.origin.y, frame.size.width, frame.size.height, |
| NSMaxY([[[NSScreen screens] objectAtIndex:0] frame])); |
| } |
| |
| void NetscapePluginInstanceProxy::startTimers(bool throttleTimers) |
| { |
| _WKPHPluginInstanceStartTimers(m_pluginHostProxy->port(), m_pluginID, throttleTimers); |
| } |
| |
| void NetscapePluginInstanceProxy::mouseEvent(NSView *pluginView, NSEvent *event, NPCocoaEventType type) |
| { |
| ALLOW_DEPRECATED_DECLARATIONS_BEGIN |
| NSPoint screenPoint = [[event window] convertBaseToScreen:[event locationInWindow]]; |
| ALLOW_DEPRECATED_DECLARATIONS_END |
| NSPoint pluginPoint = [pluginView convertPoint:[event locationInWindow] fromView:nil]; |
| |
| int clickCount; |
| if (type == NPCocoaEventMouseEntered || type == NPCocoaEventMouseExited) |
| clickCount = 0; |
| else |
| clickCount = [event clickCount]; |
| |
| |
| _WKPHPluginInstanceMouseEvent(m_pluginHostProxy->port(), m_pluginID, |
| [event timestamp], |
| type, [event modifierFlags], |
| pluginPoint.x, pluginPoint.y, |
| screenPoint.x, screenPoint.y, |
| NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]), |
| [event buttonNumber], clickCount, |
| [event deltaX], [event deltaY], [event deltaZ]); |
| } |
| |
| void NetscapePluginInstanceProxy::keyEvent(NSView *pluginView, NSEvent *event, NPCocoaEventType type) |
| { |
| NSData *charactersData = [[event characters] dataUsingEncoding:NSUTF8StringEncoding]; |
| NSData *charactersIgnoringModifiersData = [[event charactersIgnoringModifiers] dataUsingEncoding:NSUTF8StringEncoding]; |
| |
| _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID, |
| [event timestamp], |
| type, [event modifierFlags], |
| const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length], |
| const_cast<char*>(reinterpret_cast<const char*>([charactersIgnoringModifiersData bytes])), [charactersIgnoringModifiersData length], |
| [event isARepeat], [event keyCode], keyCharForEvent(event)); |
| } |
| |
| void NetscapePluginInstanceProxy::syntheticKeyDownWithCommandModifier(int keyCode, char character) |
| { |
| NSData *charactersData = [NSData dataWithBytes:&character length:1]; |
| |
| _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID, |
| [NSDate timeIntervalSinceReferenceDate], |
| NPCocoaEventKeyDown, NSEventModifierFlagCommand, |
| const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length], |
| const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length], |
| false, keyCode, character); |
| } |
| |
| void NetscapePluginInstanceProxy::flagsChanged(NSEvent *event) |
| { |
| _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID, |
| [event timestamp], NPCocoaEventFlagsChanged, |
| [event modifierFlags], 0, 0, 0, 0, false, [event keyCode], 0); |
| } |
| |
| void NetscapePluginInstanceProxy::insertText(NSString *text) |
| { |
| NSData *textData = [text dataUsingEncoding:NSUTF8StringEncoding]; |
| |
| _WKPHPluginInstanceInsertText(m_pluginHostProxy->port(), m_pluginID, |
| const_cast<char*>(reinterpret_cast<const char*>([textData bytes])), [textData length]); |
| } |
| |
| bool NetscapePluginInstanceProxy::wheelEvent(NSView *pluginView, NSEvent *event) |
| { |
| NSPoint pluginPoint = [pluginView convertPoint:[event locationInWindow] fromView:nil]; |
| |
| uint32_t requestID = nextRequestID(); |
| _WKPHPluginInstanceWheelEvent(m_pluginHostProxy->port(), m_pluginID, requestID, |
| [event timestamp], [event modifierFlags], |
| pluginPoint.x, pluginPoint.y, [event buttonNumber], |
| [event deltaX], [event deltaY], [event deltaZ]); |
| |
| auto reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); |
| return reply && reply->m_result; |
| } |
| |
| void NetscapePluginInstanceProxy::print(CGContextRef context, unsigned width, unsigned height) |
| { |
| uint32_t requestID = nextRequestID(); |
| _WKPHPluginInstancePrint(m_pluginHostProxy->port(), m_pluginID, requestID, width, height); |
| |
| auto reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID); |
| if (!reply || !reply->m_returnValue) |
| return; |
| |
| RetainPtr<CGDataProvider> dataProvider = adoptCF(CGDataProviderCreateWithCFData(reply->m_result.get())); |
| RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB()); |
| RetainPtr<CGImageRef> image = adoptCF(CGImageCreate(width, height, 8, 32, width * 4, colorSpace.get(), kCGImageAlphaFirst, dataProvider.get(), 0, false, kCGRenderingIntentDefault)); |
| |
| // Flip the context and draw the image. |
| CGContextSaveGState(context); |
| CGContextTranslateCTM(context, 0.0, height); |
| CGContextScaleCTM(context, 1.0, -1.0); |
| |
| CGContextDrawImage(context, CGRectMake(0, 0, width, height), image.get()); |
| |
| CGContextRestoreGState(context); |
| } |
| |
| void NetscapePluginInstanceProxy::snapshot(CGContextRef context, unsigned width, unsigned height) |
| { |
| uint32_t requestID = nextRequestID(); |
| _WKPHPluginInstanceSnapshot(m_pluginHostProxy->port(), m_pluginID, requestID, width, height); |
| |
| auto reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID); |
| if (!reply || !reply->m_returnValue) |
| return; |
| |
| RetainPtr<CGDataProvider> dataProvider = adoptCF(CGDataProviderCreateWithCFData(reply->m_result.get())); |
| RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB()); |
| RetainPtr<CGImageRef> image = adoptCF(CGImageCreate(width, height, 8, 32, width * 4, colorSpace.get(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, dataProvider.get(), 0, false, kCGRenderingIntentDefault)); |
| |
| CGContextDrawImage(context, CGRectMake(0, 0, width, height), image.get()); |
| } |
| |
| void NetscapePluginInstanceProxy::stopTimers() |
| { |
| _WKPHPluginInstanceStopTimers(m_pluginHostProxy->port(), m_pluginID); |
| } |
| |
| void NetscapePluginInstanceProxy::status(const char* message) |
| { |
| RetainPtr<CFStringRef> status = adoptCF(CFStringCreateWithCString(0, message ? message : "", kCFStringEncodingUTF8)); |
| if (!status) |
| return; |
| |
| WebView *wv = [m_pluginView webView]; |
| [[wv _UIDelegateForwarder] webView:wv setStatusText:(__bridge NSString *)status.get()]; |
| } |
| |
| NPError NetscapePluginInstanceProxy::loadURL(const char* url, const char* target, const char* postData, uint32_t postLen, LoadURLFlags flags, uint32_t& streamID) |
| { |
| if (!url) |
| return NPERR_INVALID_PARAM; |
| |
| NSMutableURLRequest *request = [m_pluginView requestWithURLCString:url]; |
| |
| if (flags & IsPost) { |
| NSData *httpBody = nil; |
| |
| if (flags & PostDataIsFile) { |
| // If we're posting a file, buf is either a file URL or a path to the file. |
| if (!postData) |
| return NPERR_INVALID_PARAM; |
| RetainPtr<CFStringRef> bufString = adoptCF(CFStringCreateWithCString(kCFAllocatorDefault, postData, kCFStringEncodingWindowsLatin1)); |
| if (!bufString) |
| return NPERR_INVALID_PARAM; |
| |
| NSURL *fileURL = [NSURL _web_URLWithDataAsString:(__bridge NSString *)bufString.get()]; |
| NSString *path; |
| if ([fileURL isFileURL]) |
| path = [fileURL path]; |
| else |
| path = (__bridge NSString *)bufString.get(); |
| httpBody = [NSData dataWithContentsOfFile:path]; |
| if (!httpBody) |
| return NPERR_FILE_NOT_FOUND; |
| } else |
| httpBody = [NSData dataWithBytes:postData length:postLen]; |
| |
| if (![httpBody length]) |
| return NPERR_INVALID_PARAM; |
| |
| [request setHTTPMethod:@"POST"]; |
| |
| if (flags & AllowHeadersInPostData) { |
| if ([httpBody _web_startsWithBlankLine]) |
| httpBody = [httpBody subdataWithRange:NSMakeRange(1, [httpBody length] - 1)]; |
| else { |
| NSInteger location = [httpBody _web_locationAfterFirstBlankLine]; |
| if (location != NSNotFound) { |
| // If the blank line is somewhere in the middle of postData, everything before is the header. |
| NSData *headerData = [httpBody subdataWithRange:NSMakeRange(0, location)]; |
| NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields]; |
| unsigned dataLength = [httpBody length] - location; |
| |
| // Sometimes plugins like to set Content-Length themselves when they post, |
| // but CFNetwork does not like that. So we will remove the header |
| // and instead truncate the data to the requested length. |
| NSString *contentLength = [header objectForKey:@"Content-Length"]; |
| |
| if (contentLength) |
| dataLength = std::min(static_cast<unsigned>([contentLength intValue]), dataLength); |
| [header removeObjectForKey:@"Content-Length"]; |
| |
| if ([header count] > 0) |
| [request setAllHTTPHeaderFields:header]; |
| |
| // Everything after the blank line is the actual content of the POST. |
| httpBody = [httpBody subdataWithRange:NSMakeRange(location, dataLength)]; |
| } |
| } |
| } |
| |
| if (![httpBody length]) |
| return NPERR_INVALID_PARAM; |
| |
| // Plug-ins expect to receive uncached data when doing a POST (3347134). |
| [request setCachePolicy:NSURLRequestReloadIgnoringCacheData]; |
| [request setHTTPBody:httpBody]; |
| } |
| |
| return loadRequest(request, target, flags & AllowPopups, streamID); |
| } |
| |
| void NetscapePluginInstanceProxy::performRequest(PluginRequest* pluginRequest) |
| { |
| // Loading the request can cause the instance proxy to go away, so protect it. |
| Ref<NetscapePluginInstanceProxy> protect(*this); |
| |
| ASSERT(m_pluginView); |
| |
| NSURLRequest *request = pluginRequest->request(); |
| NSString *frameName = pluginRequest->frameName(); |
| WebFrame *frame = nil; |
| |
| NSURL *URL = [request URL]; |
| NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; |
| |
| ASSERT(frameName || JSString); |
| if (frameName) { |
| // FIXME - need to get rid of this window creation which |
| // bypasses normal targeted link handling |
| frame = kit(core([m_pluginView webFrame])->loader().findFrameForNavigation(frameName)); |
| if (!frame) { |
| WebView *currentWebView = [m_pluginView webView]; |
| NSDictionary *features = [[NSDictionary alloc] init]; |
| WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView |
| createWebViewWithRequest:nil |
| windowFeatures:features]; |
| [features release]; |
| |
| if (!newWebView) { |
| _WKPHLoadURLNotify(m_pluginHostProxy->port(), m_pluginID, pluginRequest->requestID(), NPERR_GENERIC_ERROR); |
| return; |
| } |
| |
| frame = [newWebView mainFrame]; |
| core(frame)->tree().setName(frameName); |
| [[newWebView _UIDelegateForwarder] webViewShow:newWebView]; |
| } |
| } |
| |
| if (JSString) { |
| ASSERT(!frame || [m_pluginView webFrame] == frame); |
| evaluateJavaScript(pluginRequest); |
| } else { |
| [frame loadRequest:request]; |
| |
| // Check if another plug-in view or even this view is waiting for the frame to load. |
| // If it is, tell it that the load was cancelled because it will be anyway. |
| WebHostedNetscapePluginView *view = [frame _internalLoadDelegate]; |
| if (view != nil) { |
| ASSERT([view isKindOfClass:[WebHostedNetscapePluginView class]]); |
| [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK]; |
| } |
| m_pendingFrameLoads.set(frame, pluginRequest); |
| [frame _setInternalLoadDelegate:m_pluginView]; |
| } |
| |
| } |
| |
| void NetscapePluginInstanceProxy::webFrameDidFinishLoadWithReason(WebFrame* webFrame, NPReason reason) |
| { |
| FrameLoadMap::iterator it = m_pendingFrameLoads.find(webFrame); |
| ASSERT(it != m_pendingFrameLoads.end()); |
| |
| PluginRequest* pluginRequest = it->value.get(); |
| _WKPHLoadURLNotify(m_pluginHostProxy->port(), m_pluginID, pluginRequest->requestID(), reason); |
| |
| m_pendingFrameLoads.remove(it); |
| |
| [webFrame _setInternalLoadDelegate:nil]; |
| } |
| |
| void NetscapePluginInstanceProxy::evaluateJavaScript(PluginRequest* pluginRequest) |
| { |
| NSURL *URL = [pluginRequest->request() URL]; |
| NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; |
| ASSERT(JSString); |
| |
| Ref<NetscapePluginInstanceProxy> protect(*this); // Executing arbitrary JavaScript can destroy the proxy. |
| |
| NSString *result = [[m_pluginView webFrame] _stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:pluginRequest->allowPopups()]; |
| |
| // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop. |
| if (!m_pluginHostProxy) |
| return; |
| |
| if (pluginRequest->frameName() != nil) |
| return; |
| |
| if ([result length] > 0) { |
| // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does. |
| NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding]; |
| |
| auto stream = HostedNetscapePluginStream::create(this, pluginRequest->requestID(), pluginRequest->request()); |
| m_streams.add(stream->streamID(), stream.copyRef()); |
| |
| RetainPtr<NSURLResponse> response = adoptNS([[NSURLResponse alloc] initWithURL:URL |
| MIMEType:@"text/plain" |
| expectedContentLength:[JSData length] |
| textEncodingName:nil]); |
| stream->startStreamWithResponse(response.get()); |
| stream->didReceiveData(0, static_cast<const char*>([JSData bytes]), [JSData length]); |
| stream->didFinishLoading(0); |
| } |
| } |
| |
| void NetscapePluginInstanceProxy::requestTimerFired() |
| { |
| ASSERT(!m_pluginRequests.isEmpty()); |
| ASSERT(m_pluginView); |
| |
| RefPtr<PluginRequest> request = m_pluginRequests.first(); |
| m_pluginRequests.removeFirst(); |
| |
| if (!m_pluginRequests.isEmpty()) |
| m_requestTimer.startOneShot(0_s); |
| |
| performRequest(request.get()); |
| } |
| |
| NPError NetscapePluginInstanceProxy::loadRequest(NSURLRequest *request, const char* cTarget, bool allowPopups, uint32_t& requestID) |
| { |
| NSURL *URL = [request URL]; |
| |
| if (!URL) |
| return NPERR_INVALID_URL; |
| |
| // Don't allow requests to be loaded when the document loader is stopping all loaders. |
| DocumentLoader* documentLoader = [[m_pluginView dataSource] _documentLoader]; |
| if (!documentLoader || documentLoader->isStopping()) |
| return NPERR_GENERIC_ERROR; |
| |
| NSString *target = nil; |
| if (cTarget) { |
| // Find the frame given the target string. |
| target = [NSString stringWithCString:cTarget encoding:NSISOLatin1StringEncoding]; |
| } |
| WebFrame *frame = [m_pluginView webFrame]; |
| |
| // don't let a plugin start any loads if it is no longer part of a document that is being |
| // displayed unless the loads are in the same frame as the plugin. |
| if (documentLoader != core([m_pluginView webFrame])->loader().activeDocumentLoader() && |
| (!cTarget || [frame findFrameNamed:target] != frame)) { |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; |
| if (JSString != nil) { |
| if (![[[m_pluginView webView] preferences] isJavaScriptEnabled]) { |
| // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does. |
| return NPERR_GENERIC_ERROR; |
| } |
| } else { |
| if (!core([m_pluginView webFrame])->document()->securityOrigin().canDisplay(URL)) |
| return NPERR_GENERIC_ERROR; |
| } |
| |
| // FIXME: Handle wraparound |
| requestID = ++m_currentURLRequestID; |
| |
| if (cTarget || JSString) { |
| // Make when targeting a frame or evaluating a JS string, perform the request after a delay because we don't |
| // want to potentially kill the plug-in inside of its URL request. |
| |
| if (JSString && target && [frame findFrameNamed:target] != frame) { |
| // For security reasons, only allow JS requests to be made on the frame that contains the plug-in. |
| return NPERR_INVALID_PARAM; |
| } |
| |
| auto pluginRequest = PluginRequest::create(requestID, request, target, allowPopups); |
| m_pluginRequests.append(WTFMove(pluginRequest)); |
| m_requestTimer.startOneShot(0_s); |
| } else { |
| auto stream = HostedNetscapePluginStream::create(this, requestID, request); |
| |
| ASSERT(!m_streams.contains(requestID)); |
| m_streams.add(requestID, stream.copyRef()); |
| stream->start(); |
| } |
| |
| return NPERR_NO_ERROR; |
| } |
| |
| std::unique_ptr<NetscapePluginInstanceProxy::Reply> NetscapePluginInstanceProxy::processRequestsAndWaitForReply(uint32_t requestID) |
| { |
| ASSERT(m_pluginHostProxy); |
| |
| std::unique_ptr<Reply> reply; |
| |
| while (!(reply = m_replies.take(requestID))) { |
| if (!m_pluginHostProxy->processRequests()) |
| return nullptr; |
| |
| // The host proxy can be destroyed while executing a nested processRequests() call, in which case it's normal |
| // to get a success result, but be unable to keep looping. |
| if (!m_pluginHostProxy) |
| return nullptr; |
| } |
| |
| return reply; |
| } |
| |
| // NPRuntime support |
| bool NetscapePluginInstanceProxy::getWindowNPObject(uint32_t& objectID) |
| { |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| if (!frame->script().canExecuteScripts(NotAboutToExecuteScript)) |
| objectID = 0; |
| else |
| objectID = m_localObjects.idForObject(pluginWorld().vm(), frame->windowProxy().jsWindowProxy(pluginWorld())->window()); |
| |
| return true; |
| } |
| |
| bool NetscapePluginInstanceProxy::getPluginElementNPObject(uint32_t& objectID) |
| { |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| if (JSObject* object = frame->script().jsObjectForPluginElement([m_pluginView element])) |
| objectID = m_localObjects.idForObject(pluginWorld().vm(), object); |
| else |
| objectID = 0; |
| |
| return true; |
| } |
| |
| bool NetscapePluginInstanceProxy::forgetBrowserObjectID(uint32_t objectID) |
| { |
| return m_localObjects.forget(objectID); |
| } |
| |
| bool NetscapePluginInstanceProxy::evaluate(uint32_t objectID, const String& script, data_t& resultData, mach_msg_type_number_t& resultLength, bool allowPopups) |
| { |
| resultData = nullptr; |
| resultLength = 0; |
| |
| if (m_inDestroy) |
| return false; |
| |
| if (!m_localObjects.contains(objectID)) { |
| LOG_ERROR("NetscapePluginInstanceProxy::evaluate: local object %u doesn't exist.", objectID); |
| return false; |
| } |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| VM& vm = pluginWorld().vm(); |
| JSLockHolder lock(vm); |
| auto scope = DECLARE_CATCH_SCOPE(vm); |
| |
| Strong<JSGlobalObject> globalObject(vm, frame->script().globalObject(pluginWorld())); |
| |
| UserGestureIndicator gestureIndicator(allowPopups ? Optional<ProcessingUserGestureState>(ProcessingUserGesture) : WTF::nullopt); |
| |
| JSValue result = JSC::evaluate(globalObject.get(), JSC::makeSource(script, { })); |
| |
| marshalValue(globalObject.get(), result, resultData, resultLength); |
| scope.clearException(); |
| return true; |
| } |
| |
| 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) |
| { |
| resultData = nullptr; |
| resultLength = 0; |
| |
| if (m_inDestroy) |
| return false; |
| |
| JSObject* object = m_localObjects.get(objectID); |
| if (!object) { |
| LOG_ERROR("NetscapePluginInstanceProxy::invoke: local object %u doesn't exist.", objectID); |
| return false; |
| } |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| VM& vm = pluginWorld().vm(); |
| JSLockHolder lock(vm); |
| auto scope = DECLARE_CATCH_SCOPE(vm); |
| |
| JSGlobalObject* lexicalGlobalObject = frame->script().globalObject(pluginWorld()); |
| JSValue function = object->get(lexicalGlobalObject, methodName); |
| CallData callData; |
| CallType callType = getCallData(vm, function, callData); |
| if (callType == CallType::None) |
| return false; |
| |
| MarkedArgumentBuffer argList; |
| demarshalValues(lexicalGlobalObject, argumentsData, argumentsLength, argList); |
| RELEASE_ASSERT(!argList.hasOverflowed()); |
| |
| JSValue value = call(lexicalGlobalObject, function, callType, callData, object, argList); |
| |
| marshalValue(lexicalGlobalObject, value, resultData, resultLength); |
| scope.clearException(); |
| return true; |
| } |
| |
| bool NetscapePluginInstanceProxy::invokeDefault(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength) |
| { |
| if (m_inDestroy) |
| return false; |
| |
| JSObject* object = m_localObjects.get(objectID); |
| if (!object) { |
| LOG_ERROR("NetscapePluginInstanceProxy::invokeDefault: local object %u doesn't exist.", objectID); |
| return false; |
| } |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| VM& vm = pluginWorld().vm(); |
| JSLockHolder lock(vm); |
| auto scope = DECLARE_CATCH_SCOPE(vm); |
| |
| JSGlobalObject* lexicalGlobalObject = frame->script().globalObject(pluginWorld()); |
| CallData callData; |
| CallType callType = object->methodTable(vm)->getCallData(object, callData); |
| if (callType == CallType::None) |
| return false; |
| |
| MarkedArgumentBuffer argList; |
| demarshalValues(lexicalGlobalObject, argumentsData, argumentsLength, argList); |
| RELEASE_ASSERT(!argList.hasOverflowed()); |
| |
| JSValue value = call(lexicalGlobalObject, object, callType, callData, object, argList); |
| |
| marshalValue(lexicalGlobalObject, value, resultData, resultLength); |
| scope.clearException(); |
| return true; |
| } |
| |
| bool NetscapePluginInstanceProxy::construct(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength) |
| { |
| if (m_inDestroy) |
| return false; |
| |
| JSObject* object = m_localObjects.get(objectID); |
| if (!object) { |
| LOG_ERROR("NetscapePluginInstanceProxy::construct: local object %u doesn't exist.", objectID); |
| return false; |
| } |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| VM& vm = pluginWorld().vm(); |
| JSLockHolder lock(vm); |
| auto scope = DECLARE_CATCH_SCOPE(vm); |
| |
| JSGlobalObject* lexicalGlobalObject = frame->script().globalObject(pluginWorld()); |
| |
| ConstructData constructData; |
| ConstructType constructType = object->methodTable(vm)->getConstructData(object, constructData); |
| if (constructType == ConstructType::None) |
| return false; |
| |
| MarkedArgumentBuffer argList; |
| demarshalValues(lexicalGlobalObject, argumentsData, argumentsLength, argList); |
| RELEASE_ASSERT(!argList.hasOverflowed()); |
| |
| JSValue value = JSC::construct(lexicalGlobalObject, object, constructType, constructData, argList); |
| |
| marshalValue(lexicalGlobalObject, value, resultData, resultLength); |
| scope.clearException(); |
| return true; |
| } |
| |
| bool NetscapePluginInstanceProxy::getProperty(uint32_t objectID, const Identifier& propertyName, data_t& resultData, mach_msg_type_number_t& resultLength) |
| { |
| if (m_inDestroy) |
| return false; |
| |
| JSObject* object = m_localObjects.get(objectID); |
| if (!object) { |
| LOG_ERROR("NetscapePluginInstanceProxy::getProperty: local object %u doesn't exist.", objectID); |
| return false; |
| } |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| VM& vm = pluginWorld().vm(); |
| JSLockHolder lock(vm); |
| auto scope = DECLARE_CATCH_SCOPE(vm); |
| |
| JSGlobalObject* lexicalGlobalObject = frame->script().globalObject(pluginWorld()); |
| JSValue value = object->get(lexicalGlobalObject, propertyName); |
| |
| marshalValue(lexicalGlobalObject, value, resultData, resultLength); |
| scope.clearException(); |
| return true; |
| } |
| |
| bool NetscapePluginInstanceProxy::getProperty(uint32_t objectID, unsigned propertyName, data_t& resultData, mach_msg_type_number_t& resultLength) |
| { |
| JSObject* object = m_localObjects.get(objectID); |
| if (!object) { |
| LOG_ERROR("NetscapePluginInstanceProxy::getProperty: local object %u doesn't exist.", objectID); |
| return false; |
| } |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| VM& vm = pluginWorld().vm(); |
| JSLockHolder lock(vm); |
| auto scope = DECLARE_CATCH_SCOPE(vm); |
| |
| JSGlobalObject* lexicalGlobalObject = frame->script().globalObject(pluginWorld()); |
| JSValue value = object->get(lexicalGlobalObject, propertyName); |
| |
| marshalValue(lexicalGlobalObject, value, resultData, resultLength); |
| scope.clearException(); |
| return true; |
| } |
| |
| bool NetscapePluginInstanceProxy::setProperty(uint32_t objectID, const Identifier& propertyName, data_t valueData, mach_msg_type_number_t valueLength) |
| { |
| if (m_inDestroy) |
| return false; |
| |
| JSObject* object = m_localObjects.get(objectID); |
| if (!object) { |
| LOG_ERROR("NetscapePluginInstanceProxy::setProperty: local object %u doesn't exist.", objectID); |
| return false; |
| } |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| VM& vm = pluginWorld().vm(); |
| JSLockHolder lock(vm); |
| auto scope = DECLARE_CATCH_SCOPE(vm); |
| |
| JSGlobalObject* lexicalGlobalObject = frame->script().globalObject(pluginWorld()); |
| |
| JSValue value = demarshalValue(lexicalGlobalObject, valueData, valueLength); |
| PutPropertySlot slot(object); |
| object->methodTable(vm)->put(object, lexicalGlobalObject, propertyName, value, slot); |
| |
| scope.clearException(); |
| return true; |
| } |
| |
| bool NetscapePluginInstanceProxy::setProperty(uint32_t objectID, unsigned propertyName, data_t valueData, mach_msg_type_number_t valueLength) |
| { |
| if (m_inDestroy) |
| return false; |
| |
| JSObject* object = m_localObjects.get(objectID); |
| if (!object) { |
| LOG_ERROR("NetscapePluginInstanceProxy::setProperty: local object %u doesn't exist.", objectID); |
| return false; |
| } |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| VM& vm = pluginWorld().vm(); |
| JSLockHolder lock(vm); |
| auto scope = DECLARE_CATCH_SCOPE(vm); |
| |
| JSGlobalObject* lexicalGlobalObject = frame->script().globalObject(pluginWorld()); |
| |
| JSValue value = demarshalValue(lexicalGlobalObject, valueData, valueLength); |
| object->methodTable(vm)->putByIndex(object, lexicalGlobalObject, propertyName, value, false); |
| |
| scope.clearException(); |
| return true; |
| } |
| |
| bool NetscapePluginInstanceProxy::removeProperty(uint32_t objectID, const Identifier& propertyName) |
| { |
| if (m_inDestroy) |
| return false; |
| |
| JSObject* object = m_localObjects.get(objectID); |
| if (!object) { |
| LOG_ERROR("NetscapePluginInstanceProxy::removeProperty: local object %u doesn't exist.", objectID); |
| return false; |
| } |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| VM& vm = pluginWorld().vm(); |
| JSLockHolder lock(vm); |
| auto scope = DECLARE_CATCH_SCOPE(vm); |
| |
| JSGlobalObject* lexicalGlobalObject = frame->script().globalObject(pluginWorld()); |
| if (!object->hasProperty(lexicalGlobalObject, propertyName)) { |
| scope.clearException(); |
| return false; |
| } |
| |
| JSCell::deleteProperty(object, lexicalGlobalObject, propertyName); |
| scope.clearException(); |
| return true; |
| } |
| |
| bool NetscapePluginInstanceProxy::removeProperty(uint32_t objectID, unsigned propertyName) |
| { |
| if (m_inDestroy) |
| return false; |
| |
| JSObject* object = m_localObjects.get(objectID); |
| if (!object) { |
| LOG_ERROR("NetscapePluginInstanceProxy::removeProperty: local object %u doesn't exist.", objectID); |
| return false; |
| } |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| VM& vm = pluginWorld().vm(); |
| JSLockHolder lock(vm); |
| auto scope = DECLARE_CATCH_SCOPE(vm); |
| |
| JSGlobalObject* lexicalGlobalObject = frame->script().globalObject(pluginWorld()); |
| if (!object->hasProperty(lexicalGlobalObject, propertyName)) { |
| scope.clearException(); |
| return false; |
| } |
| |
| object->methodTable(vm)->deletePropertyByIndex(object, lexicalGlobalObject, propertyName); |
| scope.clearException(); |
| return true; |
| } |
| |
| bool NetscapePluginInstanceProxy::hasProperty(uint32_t objectID, const Identifier& propertyName) |
| { |
| if (m_inDestroy) |
| return false; |
| |
| JSObject* object = m_localObjects.get(objectID); |
| if (!object) { |
| LOG_ERROR("NetscapePluginInstanceProxy::hasProperty: local object %u doesn't exist.", objectID); |
| return false; |
| } |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| VM& vm = pluginWorld().vm(); |
| JSLockHolder lock(vm); |
| auto scope = DECLARE_CATCH_SCOPE(vm); |
| |
| JSGlobalObject* lexicalGlobalObject = frame->script().globalObject(pluginWorld()); |
| bool result = object->hasProperty(lexicalGlobalObject, propertyName); |
| scope.clearException(); |
| |
| return result; |
| } |
| |
| bool NetscapePluginInstanceProxy::hasProperty(uint32_t objectID, unsigned propertyName) |
| { |
| if (m_inDestroy) |
| return false; |
| |
| JSObject* object = m_localObjects.get(objectID); |
| if (!object) { |
| LOG_ERROR("NetscapePluginInstanceProxy::hasProperty: local object %u doesn't exist.", objectID); |
| return false; |
| } |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| VM& vm = pluginWorld().vm(); |
| JSLockHolder lock(vm); |
| auto scope = DECLARE_CATCH_SCOPE(vm); |
| |
| JSGlobalObject* lexicalGlobalObject = frame->script().globalObject(pluginWorld()); |
| bool result = object->hasProperty(lexicalGlobalObject, propertyName); |
| scope.clearException(); |
| |
| return result; |
| } |
| |
| bool NetscapePluginInstanceProxy::hasMethod(uint32_t objectID, const Identifier& methodName) |
| { |
| if (m_inDestroy) |
| return false; |
| |
| JSObject* object = m_localObjects.get(objectID); |
| if (!object) { |
| LOG_ERROR("NetscapePluginInstanceProxy::hasMethod: local object %u doesn't exist.", objectID); |
| return false; |
| } |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| VM& vm = pluginWorld().vm(); |
| JSLockHolder lock(vm); |
| auto scope = DECLARE_CATCH_SCOPE(vm); |
| |
| JSGlobalObject* lexicalGlobalObject = frame->script().globalObject(pluginWorld()); |
| JSValue func = object->get(lexicalGlobalObject, methodName); |
| scope.clearException(); |
| return !func.isUndefined(); |
| } |
| |
| bool NetscapePluginInstanceProxy::enumerate(uint32_t objectID, data_t& resultData, mach_msg_type_number_t& resultLength) |
| { |
| if (m_inDestroy) |
| return false; |
| |
| JSObject* object = m_localObjects.get(objectID); |
| if (!object) { |
| LOG_ERROR("NetscapePluginInstanceProxy::enumerate: local object %u doesn't exist.", objectID); |
| return false; |
| } |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| VM& vm = pluginWorld().vm(); |
| JSLockHolder lock(vm); |
| auto scope = DECLARE_CATCH_SCOPE(vm); |
| |
| JSGlobalObject* lexicalGlobalObject = frame->script().globalObject(pluginWorld()); |
| |
| PropertyNameArray propertyNames(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude); |
| object->methodTable(vm)->getPropertyNames(object, lexicalGlobalObject, propertyNames, EnumerationMode()); |
| |
| RetainPtr<NSMutableArray> array = adoptNS([[NSMutableArray alloc] init]); |
| for (unsigned i = 0; i < propertyNames.size(); i++) { |
| uint64_t methodName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(propertyNames[i].string().utf8().data())); |
| |
| [array.get() addObject:[NSNumber numberWithLongLong:methodName]]; |
| } |
| |
| NSData *data = [NSPropertyListSerialization dataWithPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 options:0 error:nullptr]; |
| ASSERT(data); |
| |
| resultLength = [data length]; |
| mig_allocate(reinterpret_cast<vm_address_t*>(&resultData), resultLength); |
| |
| memcpy(resultData, [data bytes], resultLength); |
| |
| scope.clearException(); |
| |
| return true; |
| } |
| |
| static bool getObjectID(NetscapePluginInstanceProxy* pluginInstanceProxy, JSObject* object, uint64_t& objectID) |
| { |
| JSC::VM& vm = object->vm(); |
| if (object->classInfo(vm) != ProxyRuntimeObject::info()) |
| return false; |
| |
| ProxyRuntimeObject* runtimeObject = static_cast<ProxyRuntimeObject*>(object); |
| ProxyInstance* instance = runtimeObject->getInternalProxyInstance(); |
| if (!instance) |
| return false; |
| |
| if (instance->instanceProxy() != pluginInstanceProxy) |
| return false; |
| |
| objectID = instance->objectID(); |
| return true; |
| } |
| |
| void NetscapePluginInstanceProxy::addValueToArray(NSMutableArray *array, JSGlobalObject* lexicalGlobalObject, JSValue value) |
| { |
| VM& vm = lexicalGlobalObject->vm(); |
| JSLockHolder lock(vm); |
| |
| if (value.isString()) { |
| [array addObject:[NSNumber numberWithInt:StringValueType]]; |
| [array addObject:asString(value)->value(lexicalGlobalObject)]; |
| } else if (value.isNumber()) { |
| [array addObject:[NSNumber numberWithInt:DoubleValueType]]; |
| [array addObject:[NSNumber numberWithDouble:value.toNumber(lexicalGlobalObject)]]; |
| } else if (value.isBoolean()) { |
| [array addObject:[NSNumber numberWithInt:BoolValueType]]; |
| [array addObject:[NSNumber numberWithBool:value.toBoolean(lexicalGlobalObject)]]; |
| } else if (value.isNull()) |
| [array addObject:[NSNumber numberWithInt:NullValueType]]; |
| else if (value.isObject()) { |
| JSObject* object = asObject(value); |
| uint64_t objectID; |
| if (getObjectID(this, object, objectID)) { |
| [array addObject:[NSNumber numberWithInt:NPObjectValueType]]; |
| [array addObject:[NSNumber numberWithInt:objectID]]; |
| } else { |
| [array addObject:[NSNumber numberWithInt:JSObjectValueType]]; |
| [array addObject:[NSNumber numberWithInt:m_localObjects.idForObject(vm, object)]]; |
| } |
| } else |
| [array addObject:[NSNumber numberWithInt:VoidValueType]]; |
| } |
| |
| void NetscapePluginInstanceProxy::marshalValue(JSGlobalObject* lexicalGlobalObject, JSValue value, data_t& resultData, mach_msg_type_number_t& resultLength) |
| { |
| RetainPtr<NSMutableArray> array = adoptNS([[NSMutableArray alloc] init]); |
| |
| addValueToArray(array.get(), lexicalGlobalObject, value); |
| |
| NSData *data = [NSPropertyListSerialization dataWithPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 options:0 error:nullptr]; |
| ASSERT(data); |
| |
| resultLength = data.length; |
| mig_allocate(reinterpret_cast<vm_address_t*>(&resultData), resultLength); |
| |
| memcpy(resultData, data.bytes, resultLength); |
| } |
| |
| RetainPtr<NSData> NetscapePluginInstanceProxy::marshalValues(JSGlobalObject* lexicalGlobalObject, const ArgList& args) |
| { |
| RetainPtr<NSMutableArray> array = adoptNS([[NSMutableArray alloc] init]); |
| |
| for (unsigned i = 0; i < args.size(); i++) |
| addValueToArray(array.get(), lexicalGlobalObject, args.at(i)); |
| |
| NSData *data = [NSPropertyListSerialization dataWithPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 options:0 error:nullptr]; |
| ASSERT(data); |
| |
| return data; |
| } |
| |
| bool NetscapePluginInstanceProxy::demarshalValueFromArray(JSGlobalObject* lexicalGlobalObject, NSArray *array, NSUInteger& index, JSValue& result) |
| { |
| if (index == [array count]) |
| return false; |
| |
| int type = [[array objectAtIndex:index++] intValue]; |
| switch (type) { |
| case VoidValueType: |
| result = jsUndefined(); |
| return true; |
| case NullValueType: |
| result = jsNull(); |
| return true; |
| case BoolValueType: |
| result = jsBoolean([[array objectAtIndex:index++] boolValue]); |
| return true; |
| case DoubleValueType: |
| result = jsNumber([[array objectAtIndex:index++] doubleValue]); |
| return true; |
| case StringValueType: { |
| NSString *string = [array objectAtIndex:index++]; |
| |
| result = jsString(lexicalGlobalObject->vm(), String(string)); |
| return true; |
| } |
| case JSObjectValueType: { |
| uint32_t objectID = [[array objectAtIndex:index++] intValue]; |
| |
| result = m_localObjects.get(objectID); |
| ASSERT(result); |
| return true; |
| } |
| case NPObjectValueType: { |
| uint32_t objectID = [[array objectAtIndex:index++] intValue]; |
| |
| Frame* frame = core([m_pluginView webFrame]); |
| if (!frame) |
| return false; |
| |
| if (!frame->script().canExecuteScripts(NotAboutToExecuteScript)) |
| return false; |
| |
| auto rootObject = frame->script().createRootObject((__bridge void*)m_pluginView); |
| result = ProxyInstance::create(WTFMove(rootObject), this, objectID)->createRuntimeObject(lexicalGlobalObject); |
| return true; |
| } |
| default: |
| ASSERT_NOT_REACHED(); |
| return false; |
| } |
| } |
| |
| JSValue NetscapePluginInstanceProxy::demarshalValue(JSGlobalObject* lexicalGlobalObject, const char* valueData, mach_msg_type_number_t valueLength) |
| { |
| RetainPtr<NSData> data = adoptNS([[NSData alloc] initWithBytesNoCopy:(void*)valueData length:valueLength freeWhenDone:NO]); |
| |
| NSArray *array = [NSPropertyListSerialization propertyListWithData:data.get() options:NSPropertyListImmutable format:nullptr error:nullptr]; |
| |
| NSUInteger position = 0; |
| JSValue value; |
| bool result = demarshalValueFromArray(lexicalGlobalObject, array, position, value); |
| ASSERT_UNUSED(result, result); |
| |
| return value; |
| } |
| |
| void NetscapePluginInstanceProxy::demarshalValues(JSGlobalObject* lexicalGlobalObject, data_t valuesData, mach_msg_type_number_t valuesLength, MarkedArgumentBuffer& result) |
| { |
| RetainPtr<NSData> data = adoptNS([[NSData alloc] initWithBytesNoCopy:valuesData length:valuesLength freeWhenDone:NO]); |
| |
| NSArray *array = [NSPropertyListSerialization propertyListWithData:data.get() options:NSPropertyListImmutable format:nullptr error:nullptr]; |
| |
| NSUInteger position = 0; |
| JSValue value; |
| while (demarshalValueFromArray(lexicalGlobalObject, array, position, value)) |
| result.append(value); |
| } |
| |
| void NetscapePluginInstanceProxy::retainLocalObject(JSC::JSValue value) |
| { |
| if (!value.isObject() || value.inherits<ProxyRuntimeObject>(value.getObject()->vm())) |
| return; |
| |
| m_localObjects.retain(asObject(value)); |
| } |
| |
| void NetscapePluginInstanceProxy::releaseLocalObject(JSC::JSValue value) |
| { |
| if (!value.isObject() || value.inherits<ProxyRuntimeObject>(value.getObject()->vm())) |
| return; |
| |
| m_localObjects.release(asObject(value)); |
| } |
| |
| RefPtr<Instance> NetscapePluginInstanceProxy::createBindingsInstance(Ref<RootObject>&& rootObject) |
| { |
| uint32_t requestID = nextRequestID(); |
| |
| if (_WKPHGetScriptableNPObject(m_pluginHostProxy->port(), m_pluginID, requestID) != KERN_SUCCESS) |
| return nullptr; |
| |
| auto reply = waitForReply<GetScriptableNPObjectReply>(requestID); |
| if (!reply) |
| return nullptr; |
| |
| if (!reply->m_objectID) |
| return nullptr; |
| |
| // Since the reply was non-null, "this" is still a valid pointer. |
| return ProxyInstance::create(WTFMove(rootObject), this, reply->m_objectID); |
| } |
| |
| void NetscapePluginInstanceProxy::addInstance(ProxyInstance* instance) |
| { |
| ASSERT(!m_instances.contains(instance)); |
| |
| m_instances.add(instance); |
| } |
| |
| void NetscapePluginInstanceProxy::removeInstance(ProxyInstance* instance) |
| { |
| ASSERT(m_instances.contains(instance)); |
| |
| m_instances.remove(instance); |
| } |
| |
| void NetscapePluginInstanceProxy::willCallPluginFunction() |
| { |
| m_pluginFunctionCallDepth++; |
| } |
| |
| void NetscapePluginInstanceProxy::didCallPluginFunction(bool& stopped) |
| { |
| ASSERT(m_pluginFunctionCallDepth > 0); |
| m_pluginFunctionCallDepth--; |
| |
| // If -stop was called while we were calling into a plug-in function, and we're no longer |
| // inside a plug-in function, stop now. |
| if (!m_pluginFunctionCallDepth && m_shouldStopSoon) { |
| m_shouldStopSoon = false; |
| [m_pluginView stop]; |
| stopped = true; |
| } |
| } |
| |
| bool NetscapePluginInstanceProxy::shouldStop() |
| { |
| if (m_pluginFunctionCallDepth) { |
| m_shouldStopSoon = true; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| uint32_t NetscapePluginInstanceProxy::nextRequestID() |
| { |
| uint32_t requestID = ++m_currentRequestID; |
| |
| // We don't want to return the HashMap empty/deleted "special keys" |
| if (requestID == 0 || requestID == static_cast<uint32_t>(-1)) |
| return nextRequestID(); |
| |
| return requestID; |
| } |
| |
| void NetscapePluginInstanceProxy::invalidateRect(double x, double y, double width, double height) |
| { |
| ASSERT(m_pluginView); |
| |
| m_pluginIsWaitingForDraw = true; |
| [m_pluginView invalidatePluginContentRect:NSMakeRect(x, y, width, height)]; |
| } |
| |
| void NetscapePluginInstanceProxy::didDraw() |
| { |
| if (!m_pluginIsWaitingForDraw) |
| return; |
| |
| m_pluginIsWaitingForDraw = false; |
| _WKPHPluginInstanceDidDraw(m_pluginHostProxy->port(), m_pluginID); |
| } |
| |
| bool NetscapePluginInstanceProxy::getCookies(data_t urlData, mach_msg_type_number_t urlLength, data_t& cookiesData, mach_msg_type_number_t& cookiesLength) |
| { |
| ASSERT(m_pluginView); |
| |
| NSURL *url = [m_pluginView URLWithCString:urlData]; |
| if (!url) |
| return false; |
| |
| if (Frame* frame = core([m_pluginView webFrame])) { |
| auto* document = frame->document(); |
| if (!document) |
| return false; |
| |
| auto* page = document->page(); |
| if (!page) |
| return false; |
| |
| String cookieString = page->cookieJar().cookies(*document, url); |
| WTF::CString cookieStringUTF8 = cookieString.utf8(); |
| if (cookieStringUTF8.isNull()) |
| return false; |
| |
| cookiesLength = cookieStringUTF8.length(); |
| mig_allocate(reinterpret_cast<vm_address_t*>(&cookiesData), cookiesLength); |
| memcpy(cookiesData, cookieStringUTF8.data(), cookiesLength); |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool NetscapePluginInstanceProxy::setCookies(data_t urlData, mach_msg_type_number_t urlLength, data_t cookiesData, mach_msg_type_number_t cookiesLength) |
| { |
| ASSERT(m_pluginView); |
| |
| NSURL *url = [m_pluginView URLWithCString:urlData]; |
| if (!url) |
| return false; |
| |
| if (Frame* frame = core([m_pluginView webFrame])) { |
| String cookieString = String::fromUTF8(cookiesData, cookiesLength); |
| if (!cookieString) |
| return false; |
| |
| auto* document = frame->document(); |
| if (!document) |
| return false; |
| |
| auto* page = document->page(); |
| if (!page) |
| return false; |
| |
| page->cookieJar().setCookies(*document, url, cookieString); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool NetscapePluginInstanceProxy::getProxy(data_t urlData, mach_msg_type_number_t urlLength, data_t& proxyData, mach_msg_type_number_t& proxyLength) |
| { |
| ASSERT(m_pluginView); |
| |
| NSURL *url = [m_pluginView URLWithCString:urlData]; |
| if (!url) |
| return false; |
| |
| Vector<ProxyServer> proxyServers = proxyServersForURL(url); |
| WTF::CString proxyStringUTF8 = toString(proxyServers).utf8(); |
| |
| proxyLength = proxyStringUTF8.length(); |
| mig_allocate(reinterpret_cast<vm_address_t*>(&proxyData), proxyLength); |
| memcpy(proxyData, proxyStringUTF8.data(), proxyLength); |
| |
| return true; |
| } |
| |
| bool NetscapePluginInstanceProxy::getAuthenticationInfo(data_t protocolData, data_t hostData, uint32_t port, data_t schemeData, data_t realmData, |
| data_t& usernameData, mach_msg_type_number_t& usernameLength, data_t& passwordData, mach_msg_type_number_t& passwordLength) |
| { |
| WTF::CString username; |
| WTF::CString password; |
| |
| if (!WebKit::getAuthenticationInfo(protocolData, hostData, port, schemeData, realmData, username, password)) |
| return false; |
| |
| usernameLength = username.length(); |
| mig_allocate(reinterpret_cast<vm_address_t*>(&usernameData), usernameLength); |
| memcpy(usernameData, username.data(), usernameLength); |
| |
| passwordLength = password.length(); |
| mig_allocate(reinterpret_cast<vm_address_t*>(&passwordData), passwordLength); |
| memcpy(passwordData, password.data(), passwordLength); |
| |
| return true; |
| } |
| |
| bool NetscapePluginInstanceProxy::convertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, |
| double& destX, double& destY, NPCoordinateSpace destSpace) |
| { |
| ASSERT(m_pluginView); |
| |
| return [m_pluginView convertFromX:sourceX andY:sourceY space:sourceSpace toX:&destX andY:&destY space:destSpace]; |
| } |
| |
| void NetscapePluginInstanceProxy::privateBrowsingModeDidChange(bool isPrivateBrowsingEnabled) |
| { |
| _WKPHPluginInstancePrivateBrowsingModeDidChange(m_pluginHostProxy->port(), m_pluginID, isPrivateBrowsingEnabled); |
| } |
| |
| static String& globalExceptionString() |
| { |
| static NeverDestroyed<String> exceptionString; |
| return exceptionString; |
| } |
| |
| void NetscapePluginInstanceProxy::setGlobalException(const String& exception) |
| { |
| globalExceptionString() = exception; |
| } |
| |
| void NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(JSGlobalObject* lexicalGlobalObject) |
| { |
| VM& vm = lexicalGlobalObject->vm(); |
| auto scope = DECLARE_THROW_SCOPE(vm); |
| |
| if (globalExceptionString().isNull()) |
| return; |
| |
| { |
| JSLockHolder lock(vm); |
| throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, globalExceptionString())); |
| } |
| |
| globalExceptionString() = String(); |
| } |
| |
| } // namespace WebKit |
| |
| #endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) |