/* * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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. */ #include "config.h" #include "PluginView.h" #include "NPRuntimeUtilities.h" #include "Plugin.h" #include "ShareableBitmap.h" #include "WebEvent.h" #include "WebPage.h" #include "WebPageProxyMessages.h" #include "WebProcess.h" #include <WebCore/Chrome.h> #include <WebCore/CookieJar.h> #include <WebCore/DocumentLoader.h> #include <WebCore/Event.h> #include <WebCore/FocusController.h> #include <WebCore/FrameLoadRequest.h> #include <WebCore/FrameLoaderClient.h> #include <WebCore/FrameView.h> #include <WebCore/GraphicsContext.h> #include <WebCore/HTMLPlugInElement.h> #include <WebCore/HostWindow.h> #include <WebCore/NetscapePlugInStreamLoader.h> #include <WebCore/NetworkingContext.h> #include <WebCore/ProxyServer.h> #include <WebCore/RenderEmbeddedObject.h> #include <WebCore/RenderLayer.h> #include <WebCore/ResourceLoadScheduler.h> #include <WebCore/ScrollView.h> #include <WebCore/Settings.h> using namespace JSC; using namespace WebCore; namespace WebKit { class PluginView::URLRequest : public RefCounted<URLRequest> { public: static PassRefPtr<PluginView::URLRequest> create(uint64_t requestID, const FrameLoadRequest& request, bool allowPopups) { return adoptRef(new URLRequest(requestID, request, allowPopups)); } uint64_t requestID() const { return m_requestID; } const String& target() const { return m_request.frameName(); } const ResourceRequest & request() const { return m_request.resourceRequest(); } bool allowPopups() const { return m_allowPopups; } private: URLRequest(uint64_t requestID, const FrameLoadRequest& request, bool allowPopups) : m_requestID(requestID) , m_request(request) , m_allowPopups(allowPopups) { } uint64_t m_requestID; FrameLoadRequest m_request; bool m_allowPopups; }; class PluginView::Stream : public RefCounted<PluginView::Stream>, NetscapePlugInStreamLoaderClient { public: static PassRefPtr<Stream> create(PluginView* pluginView, uint64_t streamID, const ResourceRequest& request) { return adoptRef(new Stream(pluginView, streamID, request)); } ~Stream(); void start(); void cancel(); uint64_t streamID() const { return m_streamID; } private: Stream(PluginView* pluginView, uint64_t streamID, const ResourceRequest& request) : m_pluginView(pluginView) , m_streamID(streamID) , m_request(request) , m_streamWasCancelled(false) { } // NetscapePluginStreamLoaderClient virtual void didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse&); virtual void didReceiveData(NetscapePlugInStreamLoader*, const char*, int); virtual void didFail(NetscapePlugInStreamLoader*, const ResourceError&); virtual void didFinishLoading(NetscapePlugInStreamLoader*); PluginView* m_pluginView; uint64_t m_streamID; const ResourceRequest m_request; // True if the stream was explicitly cancelled by calling cancel(). // (As opposed to being cancelled by the user hitting the stop button for example. bool m_streamWasCancelled; RefPtr<NetscapePlugInStreamLoader> m_loader; }; PluginView::Stream::~Stream() { ASSERT(!m_pluginView); } void PluginView::Stream::start() { ASSERT(!m_loader); Frame* frame = m_pluginView->m_pluginElement->document()->frame(); ASSERT(frame); m_loader = resourceLoadScheduler()->schedulePluginStreamLoad(frame, this, m_request); } void PluginView::Stream::cancel() { ASSERT(m_loader); m_streamWasCancelled = true; m_loader->cancel(m_loader->cancelledError()); m_loader = 0; } static String buildHTTPHeaders(const ResourceResponse& response, long long& expectedContentLength) { if (!response.isHTTP()) return String(); Vector<UChar> stringBuilder; String separator(": "); String statusLine = String::format("HTTP %d ", response.httpStatusCode()); stringBuilder.append(statusLine.characters(), statusLine.length()); stringBuilder.append(response.httpStatusText().characters(), response.httpStatusText().length()); stringBuilder.append('\n'); HTTPHeaderMap::const_iterator end = response.httpHeaderFields().end(); for (HTTPHeaderMap::const_iterator it = response.httpHeaderFields().begin(); it != end; ++it) { stringBuilder.append(it->first.characters(), it->first.length()); stringBuilder.append(separator.characters(), separator.length()); stringBuilder.append(it->second.characters(), it->second.length()); stringBuilder.append('\n'); } String headers = String::adopt(stringBuilder); // If the content is encoded (most likely compressed), then don't send its length to the plugin, // which is only interested in the decoded length, not yet known at the moment. // <rdar://problem/4470599> tracks a request for -[NSURLResponse expectedContentLength] to incorporate this logic. String contentEncoding = response.httpHeaderField("Content-Encoding"); if (!contentEncoding.isNull() && contentEncoding != "identity") expectedContentLength = -1; return headers; } void PluginView::Stream::didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse& response) { // Compute the stream related data from the resource response. const KURL& responseURL = response.url(); const String& mimeType = response.mimeType(); long long expectedContentLength = response.expectedContentLength(); String headers = buildHTTPHeaders(response, expectedContentLength); uint32_t streamLength = 0; if (expectedContentLength > 0) streamLength = expectedContentLength; m_pluginView->m_plugin->streamDidReceiveResponse(m_streamID, responseURL, streamLength, response.lastModifiedDate(), mimeType, headers); } void PluginView::Stream::didReceiveData(NetscapePlugInStreamLoader*, const char* bytes, int length) { m_pluginView->m_plugin->streamDidReceiveData(m_streamID, bytes, length); } void PluginView::Stream::didFail(NetscapePlugInStreamLoader*, const ResourceError& error) { // Calling streamDidFail could cause us to be deleted, so we hold on to a reference here. RefPtr<Stream> protect(this); // We only want to call streamDidFail if the stream was not explicitly cancelled by the plug-in. if (!m_streamWasCancelled) m_pluginView->m_plugin->streamDidFail(m_streamID, error.isCancellation()); m_pluginView->removeStream(this); m_pluginView = 0; } void PluginView::Stream::didFinishLoading(NetscapePlugInStreamLoader*) { // Calling streamDidFinishLoading could cause us to be deleted, so we hold on to a reference here. RefPtr<Stream> protectStream(this); // Protect the plug-in while we're calling into it. NPRuntimeObjectMap::PluginProtector pluginProtector(&m_pluginView->m_npRuntimeObjectMap); m_pluginView->m_plugin->streamDidFinishLoading(m_streamID); m_pluginView->removeStream(this); m_pluginView = 0; } static inline WebPage* webPage(HTMLPlugInElement* pluginElement) { Frame* frame = pluginElement->document()->frame(); ASSERT(frame); WebPage* webPage = static_cast<WebFrameLoaderClient*>(frame->loader()->client())->webFrame()->page(); ASSERT(webPage); return webPage; } PassRefPtr<PluginView> PluginView::create(PassRefPtr<HTMLPlugInElement> pluginElement, PassRefPtr<Plugin> plugin, const Plugin::Parameters& parameters) { return adoptRef(new PluginView(pluginElement, plugin, parameters)); } PluginView::PluginView(PassRefPtr<HTMLPlugInElement> pluginElement, PassRefPtr<Plugin> plugin, const Plugin::Parameters& parameters) : PluginViewBase(0) , m_pluginElement(pluginElement) , m_plugin(plugin) , m_webPage(webPage(m_pluginElement.get())) , m_parameters(parameters) , m_isInitialized(false) , m_isWaitingUntilMediaCanStart(false) , m_isBeingDestroyed(false) , m_pendingURLRequestsTimer(RunLoop::main(), this, &PluginView::pendingURLRequestsTimerFired) , m_npRuntimeObjectMap(this) , m_manualStreamState(StreamStateInitial) { #if PLATFORM(MAC) m_webPage->addPluginView(this); #endif } PluginView::~PluginView() { #if PLATFORM(MAC) m_webPage->removePluginView(this); #endif ASSERT(!m_isBeingDestroyed); if (m_isWaitingUntilMediaCanStart) m_pluginElement->document()->removeMediaCanStartListener(this); // Cancel all pending frame loads. FrameLoadMap::iterator end = m_pendingFrameLoads.end(); for (FrameLoadMap::iterator it = m_pendingFrameLoads.begin(), end = m_pendingFrameLoads.end(); it != end; ++it) it->first->setLoadListener(0); if (m_plugin && m_isInitialized) { m_isBeingDestroyed = true; m_plugin->destroy(); m_isBeingDestroyed = false; } // Invalidate the object map. m_npRuntimeObjectMap.invalidate(); cancelAllStreams(); // Null out the plug-in element explicitly so we'll crash earlier if we try to use // the plug-in view after it's been destroyed. m_pluginElement = nullptr; } Frame* PluginView::frame() { return m_pluginElement->document()->frame(); } void PluginView::manualLoadDidReceiveResponse(const ResourceResponse& response) { // The plug-in can be null here if it failed to initialize. if (!m_plugin) return; if (!m_isInitialized) { ASSERT(m_manualStreamState == StreamStateInitial); m_manualStreamState = StreamStateHasReceivedResponse; m_manualStreamResponse = response; return; } // Compute the stream related data from the resource response. const KURL& responseURL = response.url(); const String& mimeType = response.mimeType(); long long expectedContentLength = response.expectedContentLength(); String headers = buildHTTPHeaders(response, expectedContentLength); uint32_t streamLength = 0; if (expectedContentLength > 0) streamLength = expectedContentLength; m_plugin->manualStreamDidReceiveResponse(responseURL, streamLength, response.lastModifiedDate(), mimeType, headers); } void PluginView::manualLoadDidReceiveData(const char* bytes, int length) { // The plug-in can be null here if it failed to initialize. if (!m_plugin) return; if (!m_isInitialized) { ASSERT(m_manualStreamState == StreamStateHasReceivedResponse); if (!m_manualStreamData) m_manualStreamData = SharedBuffer::create(); m_manualStreamData->append(bytes, length); return; } m_plugin->manualStreamDidReceiveData(bytes, length); } void PluginView::manualLoadDidFinishLoading() { // The plug-in can be null here if it failed to initialize. if (!m_plugin) return; if (!m_isInitialized) { ASSERT(m_manualStreamState == StreamStateHasReceivedResponse); m_manualStreamState = StreamStateFinished; return; } m_plugin->manualStreamDidFinishLoading(); } void PluginView::manualLoadDidFail(const ResourceError& error) { // The plug-in can be null here if it failed to initialize. if (!m_plugin) return; if (!m_isInitialized) { m_manualStreamState = StreamStateFinished; m_manualStreamError = error; m_manualStreamData = nullptr; return; } m_plugin->manualStreamDidFail(error.isCancellation()); } #if PLATFORM(MAC) void PluginView::setWindowIsVisible(bool windowIsVisible) { if (!m_plugin) return; // FIXME: Implement. } void PluginView::setWindowIsFocused(bool windowIsFocused) { if (!m_isInitialized || !m_plugin) return; m_plugin->windowFocusChanged(windowIsFocused); } void PluginView::windowAndViewFramesChanged(const IntRect& windowFrameInScreenCoordinates, const IntRect& viewFrameInWindowCoordinates) { if (!m_isInitialized || !m_plugin) return; m_plugin->windowAndViewFramesChanged(windowFrameInScreenCoordinates, viewFrameInWindowCoordinates); } bool PluginView::sendComplexTextInput(uint64_t pluginComplexTextInputIdentifier, const String& textInput) { if (!m_plugin) return false; if (m_plugin->pluginComplexTextInputIdentifier() != pluginComplexTextInputIdentifier) return false; m_plugin->sendComplexTextInput(textInput); return true; } #endif void PluginView::initializePlugin() { if (m_isInitialized) return; if (!m_plugin) { // We've already tried and failed to initialize the plug-in. return; } if (Frame* frame = m_pluginElement->document()->frame()) { if (Page* page = frame->page()) { // We shouldn't initialize the plug-in right now, add a listener. if (!page->canStartMedia()) { if (m_isWaitingUntilMediaCanStart) return; m_isWaitingUntilMediaCanStart = true; m_pluginElement->document()->addMediaCanStartListener(this); return; } } } if (!m_plugin->initialize(this, m_parameters)) { // We failed to initialize the plug-in. m_plugin = 0; return; } m_isInitialized = true; viewGeometryDidChange(); redeliverManualStream(); #if PLATFORM(MAC) if (m_plugin->pluginLayer()) { if (frame()) { frame()->view()->enterCompositingMode(); m_pluginElement->setNeedsStyleRecalc(SyntheticStyleChange); } } windowAndViewFramesChanged(m_webPage->windowFrameInScreenCoordinates(), m_webPage->viewFrameInWindowCoordinates()); setWindowIsVisible(m_webPage->windowIsVisible()); setWindowIsFocused(m_webPage->windowIsFocused()); #endif } #if PLATFORM(MAC) PlatformLayer* PluginView::platformLayer() const { // The plug-in can be null here if it failed to initialize. if (!m_isInitialized || !m_plugin) return 0; return m_plugin->pluginLayer(); } #endif JSObject* PluginView::scriptObject(JSGlobalObject* globalObject) { // The plug-in can be null here if it failed to initialize. if (!m_isInitialized || !m_plugin) return 0; NPObject* scriptableNPObject = m_plugin->pluginScriptableNPObject(); if (!scriptableNPObject) return 0; JSObject* jsObject = m_npRuntimeObjectMap.getOrCreateJSObject(globalObject, scriptableNPObject); releaseNPObject(scriptableNPObject); return jsObject; } void PluginView::privateBrowsingStateChanged(bool privateBrowsingEnabled) { // The plug-in can be null here if it failed to initialize. if (!m_isInitialized || !m_plugin) return; m_plugin->privateBrowsingStateChanged(privateBrowsingEnabled); } void PluginView::setFrameRect(const WebCore::IntRect& rect) { Widget::setFrameRect(rect); viewGeometryDidChange(); } void PluginView::setBoundsSize(const WebCore::IntSize& size) { Widget::setBoundsSize(size); m_boundsSize = size; viewGeometryDidChange(); } void PluginView::paint(GraphicsContext* context, const IntRect& dirtyRect) { if (context->paintingDisabled() || !m_plugin || !m_isInitialized) return; IntRect dirtyRectInWindowCoordinates = parent()->contentsToWindow(dirtyRect); IntRect paintRectInWindowCoordinates = intersection(dirtyRectInWindowCoordinates, clipRectInWindowCoordinates()); if (paintRectInWindowCoordinates.isEmpty()) return; if (m_snapshot) m_snapshot->paint(*context, frameRect().location(), m_snapshot->bounds()); else { // The plugin is given a frame rect which is parent()->contentsToWindow(frameRect()), // and un-translates by the its origin when painting. The current CTM reflects // this widget's frame is its parent (the document), so we have to offset the CTM by // the document's window coordinates. IntPoint documentOriginInWindowCoordinates = parent()->contentsToWindow(IntPoint()); context->save(); context->translate(-documentOriginInWindowCoordinates.x(), -documentOriginInWindowCoordinates.y()); m_plugin->paint(context, paintRectInWindowCoordinates); context->restore(); } } void PluginView::frameRectsChanged() { Widget::frameRectsChanged(); viewGeometryDidChange(); } void PluginView::setParent(ScrollView* scrollView) { Widget::setParent(scrollView); if (scrollView) initializePlugin(); } void PluginView::handleEvent(Event* event) { if (!m_isInitialized || !m_plugin) return; const WebEvent* currentEvent = WebPage::currentEvent(); if (!currentEvent) return; bool didHandleEvent = false; if ((event->type() == eventNames().mousemoveEvent && currentEvent->type() == WebEvent::MouseMove) || (event->type() == eventNames().mousedownEvent && currentEvent->type() == WebEvent::MouseDown) || (event->type() == eventNames().mouseupEvent && currentEvent->type() == WebEvent::MouseUp)) { // We have a mouse event. if (currentEvent->type() == WebEvent::MouseDown) focusPluginElement(); didHandleEvent = m_plugin->handleMouseEvent(static_cast<const WebMouseEvent&>(*currentEvent)); } else if (event->type() == eventNames().mousewheelEvent && currentEvent->type() == WebEvent::Wheel) { // We have a wheel event. didHandleEvent = m_plugin->handleWheelEvent(static_cast<const WebWheelEvent&>(*currentEvent)); } else if (event->type() == eventNames().mouseoverEvent && currentEvent->type() == WebEvent::MouseMove) { // We have a mouse enter event. didHandleEvent = m_plugin->handleMouseEnterEvent(static_cast<const WebMouseEvent&>(*currentEvent)); } else if (event->type() == eventNames().mouseoutEvent && currentEvent->type() == WebEvent::MouseMove) { // We have a mouse leave event. didHandleEvent = m_plugin->handleMouseLeaveEvent(static_cast<const WebMouseEvent&>(*currentEvent)); } else if ((event->type() == eventNames().keydownEvent && currentEvent->type() == WebEvent::KeyDown) || (event->type() == eventNames().keyupEvent && currentEvent->type() == WebEvent::KeyUp)) { // We have a keyboard event. didHandleEvent = m_plugin->handleKeyboardEvent(static_cast<const WebKeyboardEvent&>(*currentEvent)); } if (didHandleEvent) event->setDefaultHandled(); } void PluginView::notifyWidget(WidgetNotification notification) { switch (notification) { case WillPaintFlattened: if (m_plugin && m_isInitialized) m_snapshot = m_plugin->snapshot(); break; case DidPaintFlattened: m_snapshot = nullptr; break; } } void PluginView::viewGeometryDidChange() { if (!m_isInitialized || !m_plugin || !parent()) return; // Get the frame rect in window coordinates. IntRect frameRectInWindowCoordinates = parent()->contentsToWindow(frameRect()); frameRectInWindowCoordinates.setSize(m_boundsSize); m_plugin->geometryDidChange(frameRectInWindowCoordinates, clipRectInWindowCoordinates()); } IntRect PluginView::clipRectInWindowCoordinates() const { ASSERT(parent()); // Get the frame rect in window coordinates. IntRect frameRectInWindowCoordinates = parent()->contentsToWindow(frameRect()); frameRectInWindowCoordinates.setSize(m_boundsSize); // Get the window clip rect for the enclosing layer (in window coordinates). RenderLayer* layer = m_pluginElement->renderer()->enclosingLayer(); FrameView* parentView = m_pluginElement->document()->frame()->view(); IntRect windowClipRect = parentView->windowClipRectForLayer(layer, true); // Intersect the two rects to get the view clip rect in window coordinates. return intersection(frameRectInWindowCoordinates, windowClipRect); } void PluginView::focusPluginElement() { ASSERT(frame()); if (Page* page = frame()->page()) page->focusController()->setFocusedFrame(frame()); frame()->document()->setFocusedNode(m_pluginElement); } void PluginView::pendingURLRequestsTimerFired() { ASSERT(!m_pendingURLRequests.isEmpty()); RefPtr<URLRequest> urlRequest = m_pendingURLRequests.takeFirst(); // If there are more requests to perform, reschedule the timer. if (!m_pendingURLRequests.isEmpty()) m_pendingURLRequestsTimer.startOneShot(0); performURLRequest(urlRequest.get()); } void PluginView::performURLRequest(URLRequest* request) { // First, check if this is a javascript: url. if (protocolIsJavaScript(request->request().url())) { performJavaScriptURLRequest(request); return; } if (!request->target().isNull()) { performFrameLoadURLRequest(request); return; } // This request is to load a URL and create a stream. RefPtr<Stream> stream = PluginView::Stream::create(this, request->requestID(), request->request()); addStream(stream.get()); stream->start(); } void PluginView::performFrameLoadURLRequest(URLRequest* request) { ASSERT(!request->target().isNull()); Frame* frame = m_pluginElement->document()->frame(); if (!frame) return; if (!m_pluginElement->document()->securityOrigin()->canDisplay(request->request().url())) { // We can't load the request, send back a reply to the plug-in. m_plugin->frameDidFail(request->requestID(), false); return; } // First, try to find a target frame. Frame* targetFrame = frame->loader()->findFrameForNavigation(request->target()); if (!targetFrame) { // We did not find a target frame. Ask our frame to load the page. This may or may not create a popup window. frame->loader()->load(request->request(), request->target(), false); // FIXME: We don't know whether the window was successfully created here so we just assume that it worked. // It's better than not telling the plug-in anything. m_plugin->frameDidFinishLoading(request->requestID()); return; } // Now ask the frame to load the request. targetFrame->loader()->load(request->request(), false); WebFrame* targetWebFrame = static_cast<WebFrameLoaderClient*>(targetFrame->loader()->client())->webFrame(); if (WebFrame::LoadListener* loadListener = targetWebFrame->loadListener()) { // 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. loadListener->didFailLoad(targetWebFrame, true); } m_pendingFrameLoads.set(targetWebFrame, request); targetWebFrame->setLoadListener(this); } void PluginView::performJavaScriptURLRequest(URLRequest* request) { ASSERT(protocolIsJavaScript(request->request().url())); RefPtr<Frame> frame = m_pluginElement->document()->frame(); if (!frame) return; String jsString = decodeURLEscapeSequences(request->request().url().string().substring(sizeof("javascript:") - 1)); if (!request->target().isNull()) { // For security reasons, only allow JS requests to be made on the frame that contains the plug-in. if (frame->tree()->find(request->target()) != frame) { // Let the plug-in know that its frame load failed. m_plugin->frameDidFail(request->requestID(), false); return; } } // Evaluate the JavaScript code. Note that running JavaScript here could cause the plug-in to be destroyed, so we // grab references to the plug-in here. RefPtr<Plugin> plugin = m_plugin; bool oldAllowPopups = frame->script()->allowPopupsFromPlugin(); frame->script()->setAllowPopupsFromPlugin(request->allowPopups()); ScriptValue result = frame->script()->executeScript(jsString); frame->script()->setAllowPopupsFromPlugin(oldAllowPopups); // Check if evaluating the JavaScript destroyed the plug-in. if (!plugin->controller()) return; ScriptState* scriptState = frame->script()->globalObject(pluginWorld())->globalExec(); String resultString; result.getString(scriptState, resultString); if (!request->target().isNull()) { // Just send back whether the frame load succeeded or not. if (resultString.isNull()) m_plugin->frameDidFail(request->requestID(), false); else m_plugin->frameDidFinishLoading(request->requestID()); return; } // Send the result back to the plug-in. plugin->didEvaluateJavaScript(request->requestID(), decodeURLEscapeSequences(request->request().url()), resultString); } void PluginView::addStream(Stream* stream) { ASSERT(!m_streams.contains(stream->streamID())); m_streams.set(stream->streamID(), stream); } void PluginView::removeStream(Stream* stream) { ASSERT(m_streams.get(stream->streamID()) == stream); m_streams.remove(stream->streamID()); } void PluginView::cancelAllStreams() { Vector<RefPtr<Stream> > streams; copyValuesToVector(m_streams, streams); for (size_t i = 0; i < streams.size(); ++i) streams[i]->cancel(); // Cancelling a stream removes it from the m_streams map, so if we cancel all streams the map should be empty. ASSERT(m_streams.isEmpty()); } void PluginView::redeliverManualStream() { if (m_manualStreamState == StreamStateInitial) { // Nothing to do. return; } if (m_manualStreamState == StreamStateFailed) { manualLoadDidFail(m_manualStreamError); return; } // Deliver the response. manualLoadDidReceiveResponse(m_manualStreamResponse); // Deliver the data. if (m_manualStreamData) { const char* data; unsigned position = 0; while (unsigned length = m_manualStreamData->getSomeData(data, position)) { manualLoadDidReceiveData(data, length); position += length; } m_manualStreamData = nullptr; } if (m_manualStreamState == StreamStateFinished) manualLoadDidFinishLoading(); } void PluginView::invalidateRect(const IntRect& dirtyRect) { if (!parent() || !m_plugin || !m_isInitialized) return; #if PLATFORM(MAC) if (m_plugin->pluginLayer()) return; #endif IntRect dirtyRectInWindowCoordinates = convertToContainingWindow(dirtyRect); parent()->hostWindow()->invalidateContentsAndWindow(intersection(dirtyRectInWindowCoordinates, clipRectInWindowCoordinates()), false); } void PluginView::setFocus(bool hasFocus) { Widget::setFocus(hasFocus); if (!m_isInitialized || !m_plugin) return; m_plugin->setFocus(hasFocus); } void PluginView::mediaCanStart() { ASSERT(m_isWaitingUntilMediaCanStart); m_isWaitingUntilMediaCanStart = false; initializePlugin(); } void PluginView::invalidate(const IntRect& dirtyRect) { invalidateRect(dirtyRect); } String PluginView::userAgent() { Frame* frame = m_pluginElement->document()->frame(); if (!frame) return String(); return frame->loader()->client()->userAgent(KURL()); } void PluginView::loadURL(uint64_t requestID, const String& method, const String& urlString, const String& target, const HTTPHeaderMap& headerFields, const Vector<uint8_t>& httpBody, bool allowPopups) { FrameLoadRequest frameLoadRequest(m_pluginElement->document()->securityOrigin()); frameLoadRequest.resourceRequest().setHTTPMethod(method); frameLoadRequest.resourceRequest().setURL(m_pluginElement->document()->completeURL(urlString)); frameLoadRequest.resourceRequest().addHTTPHeaderFields(headerFields); frameLoadRequest.resourceRequest().setHTTPBody(FormData::create(httpBody.data(), httpBody.size())); frameLoadRequest.setFrameName(target); m_pendingURLRequests.append(URLRequest::create(requestID, frameLoadRequest, allowPopups)); m_pendingURLRequestsTimer.startOneShot(0); } void PluginView::cancelStreamLoad(uint64_t streamID) { // Keep a reference to the stream. Stream::cancel might remove the stream from the map, and thus // releasing its last reference. RefPtr<Stream> stream = m_streams.get(streamID).get(); if (!stream) return; // Cancelling the stream here will remove it from the map. stream->cancel(); ASSERT(!m_streams.contains(streamID)); } void PluginView::cancelManualStreamLoad() { if (!frame()) return; DocumentLoader* documentLoader = frame()->loader()->activeDocumentLoader(); ASSERT(documentLoader); if (documentLoader->isLoadingMainResource()) documentLoader->cancelMainResourceLoad(frame()->loader()->cancelledError(m_parameters.url)); } NPObject* PluginView::windowScriptNPObject() { if (!frame()) return 0; // FIXME: Handle JavaScript being disabled. ASSERT(frame()->script()->canExecuteScripts(NotAboutToExecuteScript)); return m_npRuntimeObjectMap.getOrCreateNPObject(*pluginWorld()->globalData(), frame()->script()->windowShell(pluginWorld())->window()); } NPObject* PluginView::pluginElementNPObject() { if (!frame()) return 0; // FIXME: Handle JavaScript being disabled. JSObject* object = frame()->script()->jsObjectForPluginElement(m_pluginElement.get()); ASSERT(object); return m_npRuntimeObjectMap.getOrCreateNPObject(*pluginWorld()->globalData(), object); } bool PluginView::evaluate(NPObject* npObject, const String& scriptString, NPVariant* result, bool allowPopups) { RefPtr<Frame> frame = m_pluginElement->document()->frame(); if (!frame) return false; bool oldAllowPopups = frame->script()->allowPopupsFromPlugin(); frame->script()->setAllowPopupsFromPlugin(allowPopups); // Calling evaluate will run JavaScript that can potentially remove the plug-in element, so we need to // protect the plug-in view from destruction. NPRuntimeObjectMap::PluginProtector pluginProtector(&m_npRuntimeObjectMap); bool returnValue = m_npRuntimeObjectMap.evaluate(npObject, scriptString, result); frame->script()->setAllowPopupsFromPlugin(oldAllowPopups); return returnValue; } void PluginView::setStatusbarText(const String& statusbarText) { if (!frame()) return; Page* page = frame()->page(); if (!page) return; page->chrome()->setStatusbarText(frame(), statusbarText); } bool PluginView::isAcceleratedCompositingEnabled() { if (!frame()) return false; Settings* settings = frame()->settings(); if (!settings) return false; return settings->acceleratedCompositingEnabled(); } void PluginView::pluginProcessCrashed() { if (!m_pluginElement->renderer()) return; // FIXME: The renderer could also be a RenderApplet, we should handle that. if (!m_pluginElement->renderer()->isEmbeddedObject()) return; RenderEmbeddedObject* renderer = toRenderEmbeddedObject(m_pluginElement->renderer()); renderer->setShowsCrashedPluginIndicator(); invalidateRect(frameRect()); } #if PLATFORM(WIN) HWND PluginView::nativeParentWindow() { return m_webPage->nativeWindow(); } #endif #if PLATFORM(MAC) void PluginView::setComplexTextInputEnabled(bool complexTextInputEnabled) { m_webPage->send(Messages::WebPageProxy::SetComplexTextInputEnabled(m_plugin->pluginComplexTextInputIdentifier(), complexTextInputEnabled)); } mach_port_t PluginView::compositingRenderServerPort() { return WebProcess::shared().compositingRenderServerPort(); } #endif String PluginView::proxiesForURL(const String& urlString) { const FrameLoader* frameLoader = frame() ? frame()->loader() : 0; const NetworkingContext* context = frameLoader ? frameLoader->networkingContext() : 0; Vector<ProxyServer> proxyServers = proxyServersForURL(KURL(KURL(), urlString), context); return toString(proxyServers); } String PluginView::cookiesForURL(const String& urlString) { return cookies(m_pluginElement->document(), KURL(KURL(), urlString)); } void PluginView::setCookiesForURL(const String& urlString, const String& cookieString) { setCookies(m_pluginElement->document(), KURL(KURL(), urlString), cookieString); } bool PluginView::isPrivateBrowsingEnabled() { // If we can't get the real setting, we'll assume that private browsing is enabled. if (!frame()) return true; Settings* settings = frame()->settings(); if (!settings) return true; return settings->privateBrowsingEnabled(); } void PluginView::protectPluginFromDestruction() { if (!m_isBeingDestroyed) ref(); } void PluginView::unprotectPluginFromDestruction() { if (!m_isBeingDestroyed) deref(); } void PluginView::didFinishLoad(WebFrame* webFrame) { RefPtr<URLRequest> request = m_pendingFrameLoads.take(webFrame); ASSERT(request); webFrame->setLoadListener(0); m_plugin->frameDidFinishLoading(request->requestID()); } void PluginView::didFailLoad(WebFrame* webFrame, bool wasCancelled) { RefPtr<URLRequest> request = m_pendingFrameLoads.take(webFrame); ASSERT(request); webFrame->setLoadListener(0); m_plugin->frameDidFail(request->requestID(), wasCancelled); } } // namespace WebKit