1 /** 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * (C) 2000 Stefan Schimanski (1Stein (at) gmx.de) 5 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 */ 22 23 #include "config.h" 24 #include "core/html/HTMLPlugInElement.h" 25 26 #include "CSSPropertyNames.h" 27 #include "HTMLNames.h" 28 #include "bindings/v8/ScriptController.h" 29 #include "bindings/v8/npruntime_impl.h" 30 #include "core/dom/Document.h" 31 #include "core/dom/Event.h" 32 #include "core/page/EventHandler.h" 33 #include "core/page/Frame.h" 34 #include "core/platform/Widget.h" 35 #include "core/plugins/PluginView.h" 36 #include "core/rendering/RenderEmbeddedObject.h" 37 #include "core/rendering/RenderWidget.h" 38 #include "wtf/UnusedParam.h" 39 40 41 namespace WebCore { 42 43 using namespace HTMLNames; 44 45 HTMLPlugInElement::HTMLPlugInElement(const QualifiedName& tagName, Document* doc) 46 : HTMLFrameOwnerElement(tagName, doc) 47 , m_NPObject(0) 48 , m_isCapturingMouseEvents(false) 49 , m_inBeforeLoadEventHandler(false) 50 , m_displayState(Playing) 51 { 52 } 53 54 HTMLPlugInElement::~HTMLPlugInElement() 55 { 56 ASSERT(!m_instance); // cleared in detach() 57 58 if (m_NPObject) { 59 _NPN_ReleaseObject(m_NPObject); 60 m_NPObject = 0; 61 } 62 } 63 64 bool HTMLPlugInElement::canProcessDrag() const 65 { 66 const PluginView* plugin = pluginWidget() && pluginWidget()->isPluginView() ? static_cast<const PluginView*>(pluginWidget()) : 0; 67 return plugin ? plugin->canProcessDrag() : false; 68 } 69 70 bool HTMLPlugInElement::willRespondToMouseClickEvents() 71 { 72 if (isDisabledFormControl()) 73 return false; 74 RenderObject* r = renderer(); 75 if (!r) 76 return false; 77 if (!r->isEmbeddedObject() && !r->isWidget()) 78 return false; 79 return true; 80 } 81 82 void HTMLPlugInElement::detach(const AttachContext& context) 83 { 84 m_instance.clear(); 85 86 if (m_isCapturingMouseEvents) { 87 if (Frame* frame = document()->frame()) 88 frame->eventHandler()->setCapturingMouseEventsNode(0); 89 m_isCapturingMouseEvents = false; 90 } 91 92 if (m_NPObject) { 93 _NPN_ReleaseObject(m_NPObject); 94 m_NPObject = 0; 95 } 96 97 HTMLFrameOwnerElement::detach(context); 98 } 99 100 void HTMLPlugInElement::resetInstance() 101 { 102 m_instance.clear(); 103 } 104 105 PassScriptInstance HTMLPlugInElement::getInstance() 106 { 107 Frame* frame = document()->frame(); 108 if (!frame) 109 return 0; 110 111 // If the host dynamically turns off JavaScript (or Java) we will still return 112 // the cached allocated Bindings::Instance. Not supporting this edge-case is OK. 113 if (m_instance) 114 return m_instance; 115 116 if (Widget* widget = pluginWidget()) 117 m_instance = frame->script()->createScriptInstanceForWidget(widget); 118 119 return m_instance; 120 } 121 122 bool HTMLPlugInElement::dispatchBeforeLoadEvent(const String& sourceURL) 123 { 124 // FIXME: Our current plug-in loading design can't guarantee the following 125 // assertion is true, since plug-in loading can be initiated during layout, 126 // and synchronous layout can be initiated in a beforeload event handler! 127 // See <http://webkit.org/b/71264>. 128 // ASSERT(!m_inBeforeLoadEventHandler); 129 m_inBeforeLoadEventHandler = true; 130 bool beforeLoadAllowedLoad = HTMLFrameOwnerElement::dispatchBeforeLoadEvent(sourceURL); 131 m_inBeforeLoadEventHandler = false; 132 return beforeLoadAllowedLoad; 133 } 134 135 Widget* HTMLPlugInElement::pluginWidget() const 136 { 137 if (m_inBeforeLoadEventHandler) { 138 // The plug-in hasn't loaded yet, and it makes no sense to try to load if beforeload handler happened to touch the plug-in element. 139 // That would recursively call beforeload for the same element. 140 return 0; 141 } 142 143 RenderWidget* renderWidget = renderWidgetForJSBindings(); 144 if (!renderWidget) 145 return 0; 146 147 return renderWidget->widget(); 148 } 149 150 bool HTMLPlugInElement::isPresentationAttribute(const QualifiedName& name) const 151 { 152 if (name == widthAttr || name == heightAttr || name == vspaceAttr || name == hspaceAttr || name == alignAttr) 153 return true; 154 return HTMLFrameOwnerElement::isPresentationAttribute(name); 155 } 156 157 void HTMLPlugInElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style) 158 { 159 if (name == widthAttr) 160 addHTMLLengthToStyle(style, CSSPropertyWidth, value); 161 else if (name == heightAttr) 162 addHTMLLengthToStyle(style, CSSPropertyHeight, value); 163 else if (name == vspaceAttr) { 164 addHTMLLengthToStyle(style, CSSPropertyMarginTop, value); 165 addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value); 166 } else if (name == hspaceAttr) { 167 addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value); 168 addHTMLLengthToStyle(style, CSSPropertyMarginRight, value); 169 } else if (name == alignAttr) 170 applyAlignmentAttributeToStyle(value, style); 171 else 172 HTMLFrameOwnerElement::collectStyleForPresentationAttribute(name, value, style); 173 } 174 175 void HTMLPlugInElement::defaultEventHandler(Event* event) 176 { 177 // Firefox seems to use a fake event listener to dispatch events to plug-in (tested with mouse events only). 178 // This is observable via different order of events - in Firefox, event listeners specified in HTML attributes fires first, then an event 179 // gets dispatched to plug-in, and only then other event listeners fire. Hopefully, this difference does not matter in practice. 180 181 // FIXME: Mouse down and scroll events are passed down to plug-in via custom code in EventHandler; these code paths should be united. 182 183 RenderObject* r = renderer(); 184 if (r && r->isEmbeddedObject()) { 185 if (toRenderEmbeddedObject(r)->showsUnavailablePluginIndicator()) 186 return; 187 188 if (displayState() < Playing) 189 return; 190 } 191 192 if (!r || !r->isWidget()) 193 return; 194 RefPtr<Widget> widget = toRenderWidget(r)->widget(); 195 if (!widget) 196 return; 197 widget->handleEvent(event); 198 if (event->defaultHandled()) 199 return; 200 HTMLFrameOwnerElement::defaultEventHandler(event); 201 } 202 203 bool HTMLPlugInElement::isKeyboardFocusable() const 204 { 205 if (!document()->page()) 206 return false; 207 208 const PluginView* plugin = pluginWidget() && pluginWidget()->isPluginView() ? static_cast<const PluginView*>(pluginWidget()) : 0; 209 if (plugin) 210 return plugin->supportsKeyboardFocus(); 211 212 return false; 213 } 214 215 bool HTMLPlugInElement::isPluginElement() const 216 { 217 return true; 218 } 219 220 bool HTMLPlugInElement::rendererIsFocusable() const 221 { 222 if (HTMLFrameOwnerElement::supportsFocus() && HTMLFrameOwnerElement::rendererIsFocusable()) 223 return true; 224 225 if (useFallbackContent() || !renderer() || !renderer()->isEmbeddedObject()) 226 return false; 227 return !toRenderEmbeddedObject(renderer())->showsUnavailablePluginIndicator(); 228 } 229 230 NPObject* HTMLPlugInElement::getNPObject() 231 { 232 ASSERT(document()->frame()); 233 if (!m_NPObject) 234 m_NPObject = document()->frame()->script()->createScriptObjectForPluginElement(this); 235 return m_NPObject; 236 } 237 238 } 239