9baf002cc983d9c19ff47f902152a8e3af4a60ef
[WebKit-https.git] / Source / WebCore / html / HTMLPlugInImageElement.cpp
1 /*
2  * Copyright (C) 2008, 2011, 2012 Apple Inc. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  *
19  */
20
21 #include "config.h"
22 #include "HTMLPlugInImageElement.h"
23
24 #include "Chrome.h"
25 #include "ChromeClient.h"
26 #include "Frame.h"
27 #include "FrameLoader.h"
28 #include "FrameLoaderClient.h"
29 #include "FrameView.h"
30 #include "HTMLDivElement.h"
31 #include "HTMLImageLoader.h"
32 #include "Image.h"
33 #include "LocalizedStrings.h"
34 #include "Logging.h"
35 #include "MouseEvent.h"
36 #include "NodeList.h"
37 #include "NodeRenderStyle.h"
38 #include "NodeRenderingContext.h"
39 #include "Page.h"
40 #include "PlugInClient.h"
41 #include "PluginViewBase.h"
42 #include "RenderEmbeddedObject.h"
43 #include "RenderImage.h"
44 #include "RenderSnapshottedPlugIn.h"
45 #include "SchemeRegistry.h"
46 #include "ScriptController.h"
47 #include "SecurityOrigin.h"
48 #include "Settings.h"
49 #include "ShadowRoot.h"
50 #include "StyleResolver.h"
51 #include "Text.h"
52 #include <wtf/CurrentTime.h>
53
54 namespace WebCore {
55
56 using namespace HTMLNames;
57
58 typedef Vector<RefPtr<HTMLPlugInImageElement> > HTMLPlugInImageElementList;
59
60 static const int sizingTinyDimensionThreshold = 40;
61 static const int sizingSmallWidthThreshold = 250;
62 static const int sizingMediumWidthThreshold = 450;
63 static const int sizingMediumHeightThreshold = 300;
64 static const float sizingFullPageAreaRatioThreshold = 0.96;
65 static const float autostartSoonAfterUserGestureThreshold = 5.0;
66
67 // This delay should not exceed the snapshot delay in PluginView.cpp
68 static const double simulatedMouseClickTimerDelay = .75;
69 static const double removeSnapshotTimerDelay = 1.5;
70
71 HTMLPlugInImageElement::HTMLPlugInImageElement(const QualifiedName& tagName, Document* document, bool createdByParser, PreferPlugInsForImagesOption preferPlugInsForImagesOption)
72     : HTMLPlugInElement(tagName, document)
73     // m_needsWidgetUpdate(!createdByParser) allows HTMLObjectElement to delay
74     // widget updates until after all children are parsed.  For HTMLEmbedElement
75     // this delay is unnecessary, but it is simpler to make both classes share
76     // the same codepath in this class.
77     , m_needsWidgetUpdate(!createdByParser)
78     , m_shouldPreferPlugInsForImages(preferPlugInsForImagesOption == ShouldPreferPlugInsForImages)
79     , m_needsDocumentActivationCallbacks(false)
80     , m_simulatedMouseClickTimer(this, &HTMLPlugInImageElement::simulatedMouseClickTimerFired, simulatedMouseClickTimerDelay)
81     , m_swapRendererTimer(this, &HTMLPlugInImageElement::swapRendererTimerFired)
82     , m_removeSnapshotTimer(this, &HTMLPlugInImageElement::removeSnapshotTimerFired)
83     , m_createdDuringUserGesture(ScriptController::processingUserGesture())
84     , m_restartedPlugin(false)
85 {
86     setHasCustomStyleCallbacks();
87 }
88
89 HTMLPlugInImageElement::~HTMLPlugInImageElement()
90 {
91     if (m_needsDocumentActivationCallbacks)
92         document()->unregisterForPageCacheSuspensionCallbacks(this);
93 }
94
95 void HTMLPlugInImageElement::setDisplayState(DisplayState state)
96 {
97 #if PLATFORM(MAC)
98     if (state == RestartingWithPendingMouseClick || state == Restarting) {
99         m_restartedPlugin = true;
100         if (displayState() == DisplayingSnapshot)
101             m_removeSnapshotTimer.startOneShot(removeSnapshotTimerDelay);
102     }
103 #endif
104
105     HTMLPlugInElement::setDisplayState(state);
106
107     if (state == DisplayingSnapshot)
108         m_swapRendererTimer.startOneShot(0);
109 }
110
111 RenderEmbeddedObject* HTMLPlugInImageElement::renderEmbeddedObject() const
112 {
113     // HTMLObjectElement and HTMLEmbedElement may return arbitrary renderers
114     // when using fallback content.
115     if (!renderer() || !renderer()->isEmbeddedObject())
116         return 0;
117     return toRenderEmbeddedObject(renderer());
118 }
119
120 bool HTMLPlugInImageElement::isImageType()
121 {
122     if (m_serviceType.isEmpty() && protocolIs(m_url, "data"))
123         m_serviceType = mimeTypeFromDataURL(m_url);
124
125     if (Frame* frame = document()->frame()) {
126         KURL completedURL = document()->completeURL(m_url);
127         return frame->loader()->client()->objectContentType(completedURL, m_serviceType, shouldPreferPlugInsForImages()) == ObjectContentImage;
128     }
129
130     return Image::supportsType(m_serviceType);
131 }
132
133 // We don't use m_url, as it may not be the final URL that the object loads,
134 // depending on <param> values.
135 bool HTMLPlugInImageElement::allowedToLoadFrameURL(const String& url)
136 {
137     KURL completeURL = document()->completeURL(url);
138
139     if (contentFrame() && protocolIsJavaScript(completeURL)
140         && !document()->securityOrigin()->canAccess(contentDocument()->securityOrigin()))
141         return false;
142
143     return document()->frame()->isURLAllowed(completeURL);
144 }
145
146 // We don't use m_url, or m_serviceType as they may not be the final values
147 // that <object> uses depending on <param> values.
148 bool HTMLPlugInImageElement::wouldLoadAsNetscapePlugin(const String& url, const String& serviceType)
149 {
150     ASSERT(document());
151     ASSERT(document()->frame());
152     KURL completedURL;
153     if (!url.isEmpty())
154         completedURL = document()->completeURL(url);
155
156     FrameLoader* frameLoader = document()->frame()->loader();
157     ASSERT(frameLoader);
158     if (frameLoader->client()->objectContentType(completedURL, serviceType, shouldPreferPlugInsForImages()) == ObjectContentNetscapePlugin)
159         return true;
160     return false;
161 }
162
163 RenderObject* HTMLPlugInImageElement::createRenderer(RenderArena* arena, RenderStyle* style)
164 {
165     // Once a PlugIn Element creates its renderer, it needs to be told when the Document goes
166     // inactive or reactivates so it can clear the renderer before going into the page cache.
167     if (!m_needsDocumentActivationCallbacks) {
168         m_needsDocumentActivationCallbacks = true;
169         document()->registerForPageCacheSuspensionCallbacks(this);
170     }
171
172     if (displayState() == DisplayingSnapshot) {
173         RenderSnapshottedPlugIn* renderSnapshottedPlugIn = new (arena) RenderSnapshottedPlugIn(this);
174         renderSnapshottedPlugIn->updateSnapshot(m_snapshotImage);
175         return renderSnapshottedPlugIn;
176     }
177
178     // Fallback content breaks the DOM->Renderer class relationship of this
179     // class and all superclasses because createObject won't necessarily
180     // return a RenderEmbeddedObject, RenderPart or even RenderWidget.
181     if (useFallbackContent())
182         return RenderObject::createObject(this, style);
183
184     if (isImageType()) {
185         RenderImage* image = new (arena) RenderImage(this);
186         image->setImageResource(RenderImageResource::create());
187         return image;
188     }
189
190     return new (arena) RenderEmbeddedObject(this);
191 }
192
193 bool HTMLPlugInImageElement::willRecalcStyle(StyleChange)
194 {
195     // FIXME: Why is this necessary?  Manual re-attach is almost always wrong.
196     if (!useFallbackContent() && needsWidgetUpdate() && renderer() && !isImageType() && (displayState() != DisplayingSnapshot))
197         reattach();
198     return true;
199 }
200
201 void HTMLPlugInImageElement::attach()
202 {
203     PostAttachCallbackDisabler disabler(this);
204
205     bool isImage = isImageType();
206
207     if (!isImage)
208         queuePostAttachCallback(&HTMLPlugInImageElement::updateWidgetCallback, this);
209
210     HTMLPlugInElement::attach();
211
212     if (isImage && renderer() && !useFallbackContent()) {
213         if (!m_imageLoader)
214             m_imageLoader = adoptPtr(new HTMLImageLoader(this));
215         m_imageLoader->updateFromElement();
216     }
217 }
218
219 void HTMLPlugInImageElement::detach()
220 {
221     // FIXME: Because of the insanity that is HTMLPlugInImageElement::recalcStyle,
222     // we can end up detaching during an attach() call, before we even have a
223     // renderer.  In that case, don't mark the widget for update.
224     if (attached() && renderer() && !useFallbackContent())
225         // Update the widget the next time we attach (detaching destroys the plugin).
226         setNeedsWidgetUpdate(true);
227     HTMLPlugInElement::detach();
228 }
229
230 void HTMLPlugInImageElement::updateWidgetIfNecessary()
231 {
232     document()->updateStyleIfNeeded();
233
234     if (!needsWidgetUpdate() || useFallbackContent() || isImageType())
235         return;
236
237     if (!renderEmbeddedObject() || renderEmbeddedObject()->showsUnavailablePluginIndicator())
238         return;
239
240     updateWidget(CreateOnlyNonNetscapePlugins);
241 }
242
243 void HTMLPlugInImageElement::finishParsingChildren()
244 {
245     HTMLPlugInElement::finishParsingChildren();
246     if (useFallbackContent())
247         return;
248
249     setNeedsWidgetUpdate(true);
250     if (inDocument())
251         setNeedsStyleRecalc();
252 }
253
254 void HTMLPlugInImageElement::didMoveToNewDocument(Document* oldDocument)
255 {
256     if (m_needsDocumentActivationCallbacks) {
257         if (oldDocument)
258             oldDocument->unregisterForPageCacheSuspensionCallbacks(this);
259         document()->registerForPageCacheSuspensionCallbacks(this);
260     }
261
262     if (m_imageLoader)
263         m_imageLoader->elementDidMoveToNewDocument();
264     HTMLPlugInElement::didMoveToNewDocument(oldDocument);
265 }
266
267 void HTMLPlugInImageElement::documentWillSuspendForPageCache()
268 {
269     if (RenderStyle* renderStyle = this->renderStyle()) {
270         m_customStyleForPageCache = RenderStyle::clone(renderStyle);
271         m_customStyleForPageCache->setDisplay(NONE);
272         recalcStyle(Force);
273     }
274
275     HTMLPlugInElement::documentWillSuspendForPageCache();
276 }
277
278 void HTMLPlugInImageElement::documentDidResumeFromPageCache()
279 {
280     if (m_customStyleForPageCache) {
281         m_customStyleForPageCache = 0;
282         recalcStyle(Force);
283     }
284
285     HTMLPlugInElement::documentDidResumeFromPageCache();
286 }
287
288 PassRefPtr<RenderStyle> HTMLPlugInImageElement::customStyleForRenderer()
289 {
290     if (!m_customStyleForPageCache)
291         return document()->styleResolver()->styleForElement(this);
292     return m_customStyleForPageCache;
293 }
294
295 void HTMLPlugInImageElement::updateWidgetCallback(Node* n, unsigned)
296 {
297     static_cast<HTMLPlugInImageElement*>(n)->updateWidgetIfNecessary();
298 }
299
300 void HTMLPlugInImageElement::updateSnapshot(PassRefPtr<Image> image)
301 {
302     if (displayState() > DisplayingSnapshot)
303         return;
304
305     m_snapshotImage = image;
306
307     if (renderer()->isSnapshottedPlugIn()) {
308         toRenderSnapshottedPlugIn(renderer())->updateSnapshot(image);
309         return;
310     }
311
312     if (renderer()->isEmbeddedObject())
313         renderer()->repaint();
314 }
315
316 static AtomicString classNameForShadowRoot(const Node* node)
317 {
318     DEFINE_STATIC_LOCAL(const AtomicString, plugInTinySizeClassName, ("tiny", AtomicString::ConstructFromLiteral));
319     DEFINE_STATIC_LOCAL(const AtomicString, plugInSmallSizeClassName, ("small", AtomicString::ConstructFromLiteral));
320     DEFINE_STATIC_LOCAL(const AtomicString, plugInMediumSizeClassName, ("medium", AtomicString::ConstructFromLiteral));
321     DEFINE_STATIC_LOCAL(const AtomicString, plugInLargeSizeClassName, ("large", AtomicString::ConstructFromLiteral));
322
323     RenderBox* renderBox = static_cast<RenderBox*>(node->renderer());
324     LayoutUnit width = renderBox->contentWidth();
325     LayoutUnit height = renderBox->contentHeight();
326
327     if (width < sizingTinyDimensionThreshold || height < sizingTinyDimensionThreshold)
328         return plugInTinySizeClassName;
329
330     if (width < sizingSmallWidthThreshold)
331         return plugInSmallSizeClassName;
332
333     if (width < sizingMediumWidthThreshold || height < sizingMediumHeightThreshold)
334         return plugInMediumSizeClassName;
335
336     return plugInLargeSizeClassName;
337 }
338
339 void HTMLPlugInImageElement::updateSnapshotInfo()
340 {
341     ShadowRoot* root = userAgentShadowRoot();
342     if (!root)
343         return;
344
345     Element* shadowContainer = toElement(root->firstChild());
346     shadowContainer->setAttribute(classAttr, classNameForShadowRoot(this));
347 }
348
349 void HTMLPlugInImageElement::didAddUserAgentShadowRoot(ShadowRoot* root)
350 {
351     Document* doc = document();
352
353     m_shadowContainer = HTMLDivElement::create(doc);
354     m_shadowContainer->setPseudo(AtomicString("-webkit-snapshotted-plugin-content", AtomicString::ConstructFromLiteral));
355
356     RefPtr<Element> container = HTMLDivElement::create(doc);
357     container->setAttribute(classAttr, AtomicString("snapshot-container", AtomicString::ConstructFromLiteral));
358
359     RefPtr<Element> overlay = HTMLDivElement::create(doc);
360     overlay->setAttribute(classAttr, AtomicString("snapshot-overlay", AtomicString::ConstructFromLiteral));
361     container->appendChild(overlay, ASSERT_NO_EXCEPTION);
362
363     m_snapshotLabel = HTMLDivElement::create(doc);
364     m_snapshotLabel->setAttribute(classAttr, AtomicString("snapshot-label", AtomicString::ConstructFromLiteral));
365
366     String titleText = snapshottedPlugInLabelTitle();
367     String subtitleText = snapshottedPlugInLabelSubtitle();
368     if (document()->page()) {
369         String clientTitleText = document()->page()->chrome()->client()->plugInStartLabelTitle();
370         if (!clientTitleText.isEmpty())
371             titleText = clientTitleText;
372         String clientSubtitleText = document()->page()->chrome()->client()->plugInStartLabelSubtitle();
373         if (!clientSubtitleText.isEmpty())
374             subtitleText = clientSubtitleText;
375     }
376
377     RefPtr<Element> title = HTMLDivElement::create(doc);
378     title->setAttribute(classAttr, AtomicString("snapshot-title", AtomicString::ConstructFromLiteral));
379     title->appendChild(doc->createTextNode(titleText), ASSERT_NO_EXCEPTION);
380     m_snapshotLabel->appendChild(title, ASSERT_NO_EXCEPTION);
381
382     RefPtr<Element> subTitle = HTMLDivElement::create(doc);
383     subTitle->setAttribute(classAttr, AtomicString("snapshot-subtitle", AtomicString::ConstructFromLiteral));
384     subTitle->appendChild(doc->createTextNode(subtitleText), ASSERT_NO_EXCEPTION);
385     m_snapshotLabel->appendChild(subTitle, ASSERT_NO_EXCEPTION);
386
387     container->appendChild(m_snapshotLabel, ASSERT_NO_EXCEPTION);
388
389     // Make this into a button for accessibility clients.
390     String combinedText = titleText;
391     if (!combinedText.isEmpty() && !subtitleText.isEmpty())
392         combinedText.append(" ");
393     combinedText.append(subtitleText);
394     container->setAttribute(aria_labelAttr, combinedText);
395     container->setAttribute(roleAttr, "button");
396
397     m_shadowContainer->appendChild(container, ASSERT_NO_EXCEPTION);
398     root->appendChild(m_shadowContainer, ASSERT_NO_EXCEPTION);
399 }
400
401 bool HTMLPlugInImageElement::partOfSnapshotLabel(Node* node)
402 {
403     return node && (node == m_snapshotLabel.get() || node->isDescendantOf(m_snapshotLabel.get()));
404 }
405
406 void HTMLPlugInImageElement::swapRendererTimerFired(Timer<HTMLPlugInImageElement>*)
407 {
408     ASSERT(displayState() == DisplayingSnapshot);
409     if (userAgentShadowRoot())
410         return;
411
412     // Create a shadow root, which will trigger the code to add a snapshot container
413     // and reattach, thus making a new Renderer.
414     ensureUserAgentShadowRoot();
415 }
416
417 void HTMLPlugInImageElement::removeSnapshotTimerFired(Timer<HTMLPlugInImageElement>*)
418 {
419     m_snapshotImage = nullptr;
420     m_restartedPlugin = false;
421     if (renderer())
422         renderer()->repaint();
423 }
424
425 static void addPlugInsFromNodeListMatchingPlugInOrigin(HTMLPlugInImageElementList& plugInList, PassRefPtr<NodeList> collection, const String& plugInOrigin, const String& mimeType)
426 {
427     for (unsigned i = 0, length = collection->length(); i < length; i++) {
428         Node* node = collection->item(i);
429         if (node->isPluginElement()) {
430             HTMLPlugInElement* plugInElement = toHTMLPlugInElement(node);
431             if (plugInElement->isPlugInImageElement() && plugInElement->displayState() <= HTMLPlugInElement::DisplayingSnapshot) {
432                 HTMLPlugInImageElement* plugInImageElement = toHTMLPlugInImageElement(node);
433                 const KURL& loadedURL = plugInImageElement->loadedUrl();
434                 String otherMimeType = plugInImageElement->loadedMimeType();
435                 if (plugInOrigin == loadedURL.host() && mimeType == otherMimeType)
436                     plugInList.append(plugInImageElement);
437             }
438         }
439     }
440 }
441
442 void HTMLPlugInImageElement::restartSimilarPlugIns()
443 {
444     // Restart any other snapshotted plugins in the page with the same origin. Note that they
445     // may be in different frames, so traverse from the top of the document.
446
447     String plugInOrigin = m_loadedUrl.host();
448     String mimeType = loadedMimeType();
449     HTMLPlugInImageElementList pluginsNeedingRestart;
450
451     if (!document()->page())
452         return;
453
454     for (Frame* frame = document()->page()->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
455         if (frame->loader()->subframeLoader()->containsPlugins()) {
456             if (!frame->document())
457                 continue;
458
459             RefPtr<NodeList> plugins = frame->document()->getElementsByTagName(embedTag.localName());
460             if (plugins)
461                 addPlugInsFromNodeListMatchingPlugInOrigin(pluginsNeedingRestart, plugins, plugInOrigin, mimeType);
462
463             plugins = frame->document()->getElementsByTagName(objectTag.localName());
464             if (plugins)
465                 addPlugInsFromNodeListMatchingPlugInOrigin(pluginsNeedingRestart, plugins, plugInOrigin, mimeType);
466         }
467     }
468
469     for (size_t i = 0, length = pluginsNeedingRestart.size(); i < length; i++) {
470         pluginsNeedingRestart[i]->setDisplayState(Playing);
471         pluginsNeedingRestart[i]->restartSnapshottedPlugIn();
472     }
473 }
474
475 void HTMLPlugInImageElement::userDidClickSnapshot(PassRefPtr<MouseEvent> event, bool forwardEvent)
476 {
477     if (forwardEvent)
478         m_pendingClickEventFromSnapshot = event;
479
480     String plugInOrigin = m_loadedUrl.host();
481     if (document()->page() && !SchemeRegistry::shouldTreatURLSchemeAsLocal(document()->page()->mainFrame()->document()->baseURL().protocol()) && document()->page()->settings()->autostartOriginPlugInSnapshottingEnabled())
482         document()->page()->plugInClient()->didStartFromOrigin(document()->page()->mainFrame()->document()->baseURL().host(), plugInOrigin, loadedMimeType());
483
484     restartSnapshottedPlugIn();
485     restartSimilarPlugIns();
486 }
487
488 void HTMLPlugInImageElement::setIsPrimarySnapshottedPlugIn(bool isPrimarySnapshottedPlugIn)
489 {
490     if (!document()->page() || !document()->page()->settings()->primaryPlugInSnapshotDetectionEnabled() || document()->page()->settings()->snapshotAllPlugIns())
491         return;
492
493     if (isPrimarySnapshottedPlugIn) {
494         restartSnapshottedPlugIn();
495         restartSimilarPlugIns();
496     }
497 }
498
499 void HTMLPlugInImageElement::restartSnapshottedPlugIn()
500 {
501     if (displayState() != RestartingWithPendingMouseClick)
502         setDisplayState(Restarting);
503
504     reattach();
505 }
506
507 void HTMLPlugInImageElement::dispatchPendingMouseClick()
508 {
509     ASSERT(!m_simulatedMouseClickTimer.isActive());
510     m_simulatedMouseClickTimer.restart();
511 }
512
513 void HTMLPlugInImageElement::simulatedMouseClickTimerFired(DeferrableOneShotTimer<HTMLPlugInImageElement>*)
514 {
515     ASSERT(displayState() == RestartingWithPendingMouseClick);
516     ASSERT(m_pendingClickEventFromSnapshot);
517
518     dispatchSimulatedClick(m_pendingClickEventFromSnapshot.get(), SendMouseOverUpDownEvents, DoNotShowPressedLook);
519
520     setDisplayState(Playing);
521     m_pendingClickEventFromSnapshot = nullptr;
522 }
523
524 void HTMLPlugInImageElement::subframeLoaderWillCreatePlugIn(const KURL& url)
525 {
526     LOG(Plugins, "%p Plug-in URL: %s", this, m_url.utf8().data());
527     LOG(Plugins, "   Loaded URL: %s", url.string().utf8().data());
528
529     m_loadedUrl = url;
530
531     if (!document()->page()
532         || !document()->page()->settings()->plugInSnapshottingEnabled())
533         return;
534
535     if (displayState() == Restarting) {
536         setDisplayState(Playing);
537         LOG(Plugins, "%p Plug-in is explicitly restarting", this);
538         return;
539     }
540
541     if (displayState() == RestartingWithPendingMouseClick) {
542         LOG(Plugins, "%p Plug-in is explicitly restarting but also waiting for a click", this);
543         return;
544     }
545
546     bool inMainFrame = document()->frame() == document()->page()->mainFrame();
547
548     if (document()->isPluginDocument() && inMainFrame) {
549         LOG(Plugins, "%p Plug-in document in main frame", this);
550         return;
551     }
552
553     if (ScriptController::processingUserGesture()) {
554         LOG(Plugins, "%p Script is currently processing user gesture, set to play", this);
555         return;
556     }
557
558     if (m_createdDuringUserGesture) {
559         LOG(Plugins, "%p Plug-in was created when processing user gesture, set to play", this);
560         return;
561     }
562
563     double lastKnownUserGestureTimestamp = document()->lastHandledUserGestureTimestamp();
564     if (!inMainFrame && document()->page()->mainFrame() && document()->page()->mainFrame()->document())
565         lastKnownUserGestureTimestamp = std::max(lastKnownUserGestureTimestamp, document()->page()->mainFrame()->document()->lastHandledUserGestureTimestamp());
566     if (currentTime() - lastKnownUserGestureTimestamp < autostartSoonAfterUserGestureThreshold) {
567         LOG(Plugins, "%p Plug-in was created shortly after a user gesture, set to play", this);
568         return;
569     }
570
571     if (document()->page()->settings()->snapshotAllPlugIns()) {
572         LOG(Plugins, "%p Plug-in forced to snapshot by user preference", this);
573         setDisplayState(WaitingForSnapshot);
574         return;
575     }
576
577     RenderBox* renderEmbeddedObject = toRenderBox(renderer());
578     Length styleWidth = renderEmbeddedObject->style()->width();
579     Length styleHeight = renderEmbeddedObject->style()->height();
580     LayoutRect contentBoxRect = renderEmbeddedObject->contentBoxRect();
581     int contentWidth = contentBoxRect.width();
582     int contentHeight = contentBoxRect.height();
583     int contentArea = contentWidth * contentHeight;
584     IntSize visibleViewSize = document()->frame()->view()->visibleSize();
585     int visibleArea = visibleViewSize.width() * visibleViewSize.height();
586
587     if (inMainFrame && styleWidth.isPercent() && (styleWidth.percent() == 100)
588         && styleHeight.isPercent() && (styleHeight.percent() == 100)
589         && (static_cast<float>(contentArea) / visibleArea > sizingFullPageAreaRatioThreshold)) {
590         LOG(Plugins, "%p Plug-in is top level full page, set to play", this);
591         return;
592     }
593
594     if (contentWidth <= sizingTinyDimensionThreshold || contentHeight <= sizingTinyDimensionThreshold) {
595         LOG(Plugins, "%p Plug-in is very small %dx%d, set to play", this, contentWidth, contentHeight);
596         return;
597     }
598
599     if (!document()->page()->plugInClient()) {
600         setDisplayState(WaitingForSnapshot);
601         return;
602     }
603
604     if (document()->page()->settings()->autostartOriginPlugInSnapshottingEnabled() && document()->page()->plugInClient()->shouldAutoStartFromOrigin(document()->page()->mainFrame()->document()->baseURL().host(), url.host(), loadedMimeType())) {
605         LOG(Plugins, "%p Plug-in from (%s, %s) is marked to auto-start, set to play", this, document()->page()->mainFrame()->document()->baseURL().host().utf8().data(), url.host().utf8().data());
606         return;
607     }
608
609     LOG(Plugins, "%p Plug-in from (%s, %s) is not auto-start, sized at %dx%d, set to wait for snapshot", this, document()->page()->mainFrame()->document()->baseURL().host().utf8().data(), url.host().utf8().data(), contentWidth, contentHeight);
610     setDisplayState(WaitingForSnapshot);
611 }
612
613 void HTMLPlugInImageElement::subframeLoaderDidCreatePlugIn(const Widget* widget)
614 {
615     if (!widget->isPluginViewBase()
616         || !static_cast<const PluginViewBase*>(widget)->shouldAlwaysAutoStart())
617         return;
618
619     LOG(Plugins, "%p Plug-in should auto-start, set to play", this);
620     setDisplayState(Playing);
621 }
622
623 } // namespace WebCore