Remove excessive headers from WebCore/{editing,fileapi,history,html,loader,page}
[WebKit-https.git] / Source / WebCore / html / HTMLPlugInImageElement.cpp
1 /*
2  * Copyright (C) 2008-2017 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 "CommonVM.h"
27 #include "ContentSecurityPolicy.h"
28 #include "EventNames.h"
29 #include "FrameLoaderClient.h"
30 #include "HTMLImageLoader.h"
31 #include "JSDOMConvertBoolean.h"
32 #include "JSDOMConvertInterface.h"
33 #include "JSDOMConvertStrings.h"
34 #include "JSShadowRoot.h"
35 #include "LocalizedStrings.h"
36 #include "Logging.h"
37 #include "MainFrame.h"
38 #include "MouseEvent.h"
39 #include "Page.h"
40 #include "PlatformMouseEvent.h"
41 #include "PlugInClient.h"
42 #include "PluginViewBase.h"
43 #include "RenderImage.h"
44 #include "RenderSnapshottedPlugIn.h"
45 #include "RenderTreeUpdater.h"
46 #include "SchemeRegistry.h"
47 #include "ScriptController.h"
48 #include "SecurityOrigin.h"
49 #include "Settings.h"
50 #include "ShadowRoot.h"
51 #include "StyleTreeResolver.h"
52 #include "SubframeLoader.h"
53 #include "TypedElementDescendantIterator.h"
54
55 namespace WebCore {
56
57 static const int sizingTinyDimensionThreshold = 40;
58 static const float sizingFullPageAreaRatioThreshold = 0.96;
59 static const Seconds autostartSoonAfterUserGestureThreshold = 5_s;
60
61 // This delay should not exceed the snapshot delay in PluginView.cpp
62 static const Seconds simulatedMouseClickTimerDelay { 750_ms };
63
64 #if PLATFORM(COCOA)
65 static const Seconds removeSnapshotTimerDelay { 1500_ms };
66 #endif
67
68 static const String titleText(Page& page, const String& mimeType)
69 {
70     // FIXME: It's not consistent to get a string from the page's chrome client, but then cache it globally.
71     // If it's global, it should come from elsewhere. If it's per-page then it should be cached per page.
72     static NeverDestroyed<HashMap<String, String>> mimeTypeToLabelTitleMap;
73     return mimeTypeToLabelTitleMap.get().ensure(mimeType, [&] {
74         auto title = page.chrome().client().plugInStartLabelTitle(mimeType);
75         if (!title.isEmpty())
76             return title;
77         return snapshottedPlugInLabelTitle();
78     }).iterator->value;
79 };
80
81 static const String subtitleText(Page& page, const String& mimeType)
82 {
83     // FIXME: It's not consistent to get a string from the page's chrome client, but then cache it globally.
84     // If it's global, it should come from elsewhere. If it's per-page then it should be cached per page.
85     static NeverDestroyed<HashMap<String, String>> mimeTypeToLabelSubtitleMap;
86     return mimeTypeToLabelSubtitleMap.get().ensure(mimeType, [&] {
87         auto subtitle = page.chrome().client().plugInStartLabelSubtitle(mimeType);
88         if (!subtitle.isEmpty())
89             return subtitle;
90         return snapshottedPlugInLabelSubtitle();
91     }).iterator->value;
92 };
93
94 HTMLPlugInImageElement::HTMLPlugInImageElement(const QualifiedName& tagName, Document& document, bool createdByParser)
95     : HTMLPlugInElement(tagName, document)
96     , m_needsWidgetUpdate(!createdByParser) // Set true in finishParsingChildren.
97     , m_simulatedMouseClickTimer(*this, &HTMLPlugInImageElement::simulatedMouseClickTimerFired, simulatedMouseClickTimerDelay)
98     , m_removeSnapshotTimer(*this, &HTMLPlugInImageElement::removeSnapshotTimerFired)
99     , m_createdDuringUserGesture(ScriptController::processingUserGesture())
100 {
101     setHasCustomStyleResolveCallbacks();
102 }
103
104 HTMLPlugInImageElement::~HTMLPlugInImageElement()
105 {
106     if (m_needsDocumentActivationCallbacks)
107         document().unregisterForDocumentSuspensionCallbacks(this);
108 }
109
110 void HTMLPlugInImageElement::setDisplayState(DisplayState state)
111 {
112 #if PLATFORM(COCOA)
113     if (state == RestartingWithPendingMouseClick || state == Restarting) {
114         m_isRestartedPlugin = true;
115         m_snapshotDecision = NeverSnapshot;
116         invalidateStyleAndLayerComposition();
117         if (displayState() == DisplayingSnapshot)
118             m_removeSnapshotTimer.startOneShot(removeSnapshotTimerDelay);
119     }
120 #endif
121
122     HTMLPlugInElement::setDisplayState(state);
123 }
124
125 RenderEmbeddedObject* HTMLPlugInImageElement::renderEmbeddedObject() const
126 {
127     // HTMLObjectElement and HTMLEmbedElement may return arbitrary renderers when using fallback content.
128     return is<RenderEmbeddedObject>(renderer()) ? downcast<RenderEmbeddedObject>(renderer()) : nullptr;
129 }
130
131 bool HTMLPlugInImageElement::isImageType()
132 {
133     if (m_serviceType.isEmpty() && protocolIs(m_url, "data"))
134         m_serviceType = mimeTypeFromDataURL(m_url);
135
136     if (auto* frame = document().frame())
137         return frame->loader().client().objectContentType(document().completeURL(m_url), m_serviceType) == ObjectContentType::Image;
138
139     return Image::supportsType(m_serviceType);
140 }
141
142 // We don't use m_url, as it may not be the final URL that the object loads, depending on <param> values.
143 bool HTMLPlugInImageElement::allowedToLoadFrameURL(const String& url)
144 {
145     URL completeURL = document().completeURL(url);
146     if (contentFrame() && protocolIsJavaScript(completeURL) && !document().securityOrigin().canAccess(contentDocument()->securityOrigin()))
147         return false;
148     return document().frame()->isURLAllowed(completeURL);
149 }
150
151 // We don't use m_url, or m_serviceType as they may not be the final values
152 // that <object> uses depending on <param> values.
153 bool HTMLPlugInImageElement::wouldLoadAsPlugIn(const String& url, const String& serviceType)
154 {
155     ASSERT(document().frame());
156     URL completedURL;
157     if (!url.isEmpty())
158         completedURL = document().completeURL(url);
159     return document().frame()->loader().client().objectContentType(completedURL, serviceType) == ObjectContentType::PlugIn;
160 }
161
162 RenderPtr<RenderElement> HTMLPlugInImageElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition& insertionPosition)
163 {
164     ASSERT(document().pageCacheState() == Document::NotInPageCache);
165
166     if (displayState() >= PreparingPluginReplacement)
167         return HTMLPlugInElement::createElementRenderer(WTFMove(style), insertionPosition);
168
169     // Once a plug-in element creates its renderer, it needs to be told when the document goes
170     // inactive or reactivates so it can clear the renderer before going into the page cache.
171     if (!m_needsDocumentActivationCallbacks) {
172         m_needsDocumentActivationCallbacks = true;
173         document().registerForDocumentSuspensionCallbacks(this);
174     }
175
176     if (displayState() == DisplayingSnapshot) {
177         auto renderSnapshottedPlugIn = createRenderer<RenderSnapshottedPlugIn>(*this, WTFMove(style));
178         renderSnapshottedPlugIn->updateSnapshot(m_snapshotImage.get());
179         return WTFMove(renderSnapshottedPlugIn);
180     }
181
182     if (useFallbackContent())
183         return RenderElement::createFor(*this, WTFMove(style));
184
185     if (isImageType())
186         return createRenderer<RenderImage>(*this, WTFMove(style));
187
188     return HTMLPlugInElement::createElementRenderer(WTFMove(style), insertionPosition);
189 }
190
191 bool HTMLPlugInImageElement::childShouldCreateRenderer(const Node& child) const
192 {
193     if (is<RenderSnapshottedPlugIn>(renderer()) && !hasShadowRootParent(child))
194         return false;
195
196     return HTMLPlugInElement::childShouldCreateRenderer(child);
197 }
198
199 void HTMLPlugInImageElement::willRecalcStyle(Style::Change change)
200 {
201     // Make sure style recalcs scheduled by a child shadow tree don't trigger reconstruction and cause flicker.
202     if (change == Style::NoChange && styleValidity() == Style::Validity::Valid)
203         return;
204
205     // FIXME: There shoudn't be need to force render tree reconstruction here.
206     // It is only done because loading and load event dispatching is tied to render tree construction.
207     if (!useFallbackContent() && needsWidgetUpdate() && renderer() && !isImageType() && (displayState() != DisplayingSnapshot))
208         invalidateStyleAndRenderersForSubtree();
209 }
210
211 void HTMLPlugInImageElement::didAttachRenderers()
212 {
213     if (!isImageType()) {
214         RefPtr<HTMLPlugInImageElement> element = this;
215         Style::queuePostResolutionCallback([element]{
216             element->updateWidgetIfNecessary();
217         });
218         return;
219     }
220     if (!renderer() || useFallbackContent())
221         return;
222
223     // Image load might complete synchronously and cause us to re-enter.
224     RefPtr<HTMLPlugInImageElement> element = this;
225     Style::queuePostResolutionCallback([element]{
226         element->startLoadingImage();
227     });
228 }
229
230 void HTMLPlugInImageElement::willDetachRenderers()
231 {
232     // FIXME: Because of the insanity that is HTMLPlugInImageElement::willRecalcStyle,
233     // we can end up detaching during an attach() call, before we even have a
234     // renderer. In that case, don't mark the widget for update.
235     if (renderer() && !useFallbackContent()) {
236         // Update the widget the next time we attach (detaching destroys the plugin).
237         setNeedsWidgetUpdate(true);
238     }
239
240     auto* widget = pluginWidget(PluginLoadingPolicy::DoNotLoad);
241     if (is<PluginViewBase>(widget))
242         downcast<PluginViewBase>(*widget).willDetatchRenderer();
243
244     HTMLPlugInElement::willDetachRenderers();
245 }
246
247 void HTMLPlugInImageElement::updateWidgetIfNecessary()
248 {
249     document().updateStyleIfNeeded();
250
251     if (!needsWidgetUpdate() || useFallbackContent() || isImageType())
252         return;
253
254     if (!renderEmbeddedObject() || renderEmbeddedObject()->isPluginUnavailable())
255         return;
256
257     updateWidget(CreatePlugins::No);
258 }
259
260 void HTMLPlugInImageElement::finishParsingChildren()
261 {
262     HTMLPlugInElement::finishParsingChildren();
263     if (useFallbackContent())
264         return;
265
266     // HTMLObjectElement needs to delay widget updates until after all children are parsed,
267     // For HTMLEmbedElement this delay is unnecessary, but there is no harm in doing the same.
268     setNeedsWidgetUpdate(true);
269     if (isConnected())
270         invalidateStyleForSubtree();
271 }
272
273 void HTMLPlugInImageElement::didMoveToNewDocument(Document& oldDocument, Document& newDocument)
274 {
275     ASSERT_WITH_SECURITY_IMPLICATION(&document() == &newDocument);
276     if (m_needsDocumentActivationCallbacks) {
277         oldDocument.unregisterForDocumentSuspensionCallbacks(this);
278         newDocument.registerForDocumentSuspensionCallbacks(this);
279     }
280
281     if (m_imageLoader)
282         m_imageLoader->elementDidMoveToNewDocument();
283
284     HTMLPlugInElement::didMoveToNewDocument(oldDocument, newDocument);
285 }
286
287 void HTMLPlugInImageElement::prepareForDocumentSuspension()
288 {
289     if (renderer())
290         RenderTreeUpdater::tearDownRenderers(*this);
291
292     HTMLPlugInElement::prepareForDocumentSuspension();
293 }
294
295 void HTMLPlugInImageElement::resumeFromDocumentSuspension()
296 {
297     invalidateStyleAndRenderersForSubtree();
298
299     HTMLPlugInElement::resumeFromDocumentSuspension();
300 }
301
302 void HTMLPlugInImageElement::startLoadingImage()
303 {
304     if (!m_imageLoader)
305         m_imageLoader = std::make_unique<HTMLImageLoader>(*this);
306     m_imageLoader->updateFromElement();
307 }
308
309 void HTMLPlugInImageElement::updateSnapshot(Image* image)
310 {
311     if (displayState() > DisplayingSnapshot)
312         return;
313
314     m_snapshotImage = image;
315
316     auto* renderer = this->renderer();
317     if (!renderer)
318         return;
319
320     if (is<RenderSnapshottedPlugIn>(*renderer)) {
321         downcast<RenderSnapshottedPlugIn>(*renderer).updateSnapshot(image);
322         return;
323     }
324
325     if (is<RenderEmbeddedObject>(*renderer))
326         renderer->repaint();
327 }
328
329 static DOMWrapperWorld& plugInImageElementIsolatedWorld()
330 {
331     static auto& isolatedWorld = DOMWrapperWorld::create(commonVM()).leakRef();
332     return isolatedWorld;
333 }
334
335 void HTMLPlugInImageElement::didAddUserAgentShadowRoot(ShadowRoot* root)
336 {
337     HTMLPlugInElement::didAddUserAgentShadowRoot(root);
338     if (displayState() >= PreparingPluginReplacement)
339         return;
340
341     auto* page = document().page();
342     if (!page)
343         return;
344
345     // Reset any author styles that may apply as we only want explicit
346     // styles defined in the injected user agents stylesheets to specify
347     // the look-and-feel of the snapshotted plug-in overlay. 
348     root->setResetStyleInheritance(true);
349     
350     String mimeType = loadedMimeType();
351
352     auto& isolatedWorld = plugInImageElementIsolatedWorld();
353     document().ensurePlugInsInjectedScript(isolatedWorld);
354
355     auto& scriptController = document().frame()->script();
356     auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(scriptController.globalObject(isolatedWorld));
357
358     auto& vm = globalObject.vm();
359     JSC::JSLockHolder lock(vm);
360     auto scope = DECLARE_CATCH_SCOPE(vm);
361     auto& state = *globalObject.globalExec();
362
363     JSC::MarkedArgumentBuffer argList;
364     argList.append(toJS<IDLInterface<ShadowRoot>>(state, globalObject, root));
365     argList.append(toJS<IDLDOMString>(state, titleText(*page, mimeType)));
366     argList.append(toJS<IDLDOMString>(state, subtitleText(*page, mimeType)));
367     
368     // This parameter determines whether or not the snapshot overlay should always be visible over the plugin snapshot.
369     // If no snapshot was found then we want the overlay to be visible.
370     argList.append(toJS<IDLBoolean>(!m_snapshotImage));
371
372     // It is expected the JS file provides a createOverlay(shadowRoot, title, subtitle) function.
373     auto* overlay = globalObject.get(&state, JSC::Identifier::fromString(&state, "createOverlay")).toObject(&state);
374     ASSERT(!overlay == !!scope.exception());
375     if (!overlay) {
376         scope.clearException();
377         return;
378     }
379     JSC::CallData callData;
380     auto callType = overlay->methodTable()->getCallData(overlay, callData);
381     if (callType == JSC::CallType::None)
382         return;
383
384     call(&state, overlay, callType, callData, &globalObject, argList);
385     scope.clearException();
386 }
387
388 bool HTMLPlugInImageElement::partOfSnapshotOverlay(const Node* node) const
389 {
390     static NeverDestroyed<AtomicString> selector(".snapshot-overlay", AtomicString::ConstructFromLiteral);
391     auto* shadow = userAgentShadowRoot();
392     if (!shadow)
393         return false;
394     if (!node)
395         return false;
396     auto queryResult = shadow->querySelector(selector.get());
397     if (queryResult.hasException())
398         return false;
399     auto* snapshotLabel = queryResult.releaseReturnValue();
400     return snapshotLabel && snapshotLabel->contains(node);
401 }
402
403 void HTMLPlugInImageElement::removeSnapshotTimerFired()
404 {
405     m_snapshotImage = nullptr;
406     m_isRestartedPlugin = false;
407     invalidateStyleAndLayerComposition();
408     if (renderer())
409         renderer()->repaint();
410 }
411
412 void HTMLPlugInImageElement::restartSimilarPlugIns()
413 {
414     // Restart any other snapshotted plugins in the page with the same origin. Note that they
415     // may be in different frames, so traverse from the top of the document.
416
417     String plugInOrigin = m_loadedUrl.host();
418     String mimeType = loadedMimeType();
419     Vector<Ref<HTMLPlugInImageElement>> similarPlugins;
420
421     if (!document().page())
422         return;
423
424     for (Frame* frame = &document().page()->mainFrame(); frame; frame = frame->tree().traverseNext()) {
425         if (!frame->loader().subframeLoader().containsPlugins())
426             continue;
427         
428         if (!frame->document())
429             continue;
430
431         for (auto& element : descendantsOfType<HTMLPlugInImageElement>(*frame->document())) {
432             if (plugInOrigin == element.loadedUrl().host() && mimeType == element.loadedMimeType())
433                 similarPlugins.append(element);
434         }
435     }
436
437     for (auto& plugInToRestart : similarPlugins) {
438         if (plugInToRestart->displayState() <= HTMLPlugInElement::DisplayingSnapshot) {
439             LOG(Plugins, "%p Plug-in looks similar to a restarted plug-in. Restart.", plugInToRestart.ptr());
440             plugInToRestart->restartSnapshottedPlugIn();
441         }
442         plugInToRestart->m_snapshotDecision = NeverSnapshot;
443     }
444 }
445
446 void HTMLPlugInImageElement::userDidClickSnapshot(MouseEvent& event, bool forwardEvent)
447 {
448     if (forwardEvent)
449         m_pendingClickEventFromSnapshot = &event;
450
451     String plugInOrigin = m_loadedUrl.host();
452     if (document().page() && !SchemeRegistry::shouldTreatURLSchemeAsLocal(document().page()->mainFrame().document()->baseURL().protocol().toStringWithoutCopying()) && document().page()->settings().autostartOriginPlugInSnapshottingEnabled())
453         document().page()->plugInClient()->didStartFromOrigin(document().page()->mainFrame().document()->baseURL().host(), plugInOrigin, loadedMimeType(), document().page()->sessionID());
454
455     LOG(Plugins, "%p User clicked on snapshotted plug-in. Restart.", this);
456     restartSnapshottedPlugIn();
457     if (forwardEvent)
458         setDisplayState(RestartingWithPendingMouseClick);
459     restartSimilarPlugIns();
460 }
461
462 void HTMLPlugInImageElement::setIsPrimarySnapshottedPlugIn(bool isPrimarySnapshottedPlugIn)
463 {
464     if (!document().page() || !document().page()->settings().primaryPlugInSnapshotDetectionEnabled() || document().page()->settings().snapshotAllPlugIns())
465         return;
466
467     if (isPrimarySnapshottedPlugIn) {
468         if (m_plugInWasCreated) {
469             LOG(Plugins, "%p Plug-in was detected as the primary element in the page. Restart.", this);
470             restartSnapshottedPlugIn();
471             restartSimilarPlugIns();
472         } else {
473             LOG(Plugins, "%p Plug-in was detected as the primary element in the page, but is not yet created. Will restart later.", this);
474             m_deferredPromotionToPrimaryPlugIn = true;
475         }
476     }
477 }
478
479 void HTMLPlugInImageElement::restartSnapshottedPlugIn()
480 {
481     if (displayState() >= RestartingWithPendingMouseClick)
482         return;
483
484     setDisplayState(Restarting);
485     invalidateStyleAndRenderersForSubtree();
486 }
487
488 void HTMLPlugInImageElement::dispatchPendingMouseClick()
489 {
490     ASSERT(!m_simulatedMouseClickTimer.isActive());
491     m_simulatedMouseClickTimer.restart();
492 }
493
494 void HTMLPlugInImageElement::simulatedMouseClickTimerFired()
495 {
496     ASSERT(displayState() == RestartingWithPendingMouseClick);
497     ASSERT(m_pendingClickEventFromSnapshot);
498
499     setDisplayState(Playing);
500     dispatchSimulatedClick(m_pendingClickEventFromSnapshot.get(), SendMouseOverUpDownEvents, DoNotShowPressedLook);
501
502     m_pendingClickEventFromSnapshot = nullptr;
503 }
504
505 static bool documentHadRecentUserGesture(Document& document)
506 {
507     MonotonicTime lastKnownUserGestureTimestamp = document.lastHandledUserGestureTimestamp();
508     if (document.frame() != &document.page()->mainFrame() && document.page()->mainFrame().document())
509         lastKnownUserGestureTimestamp = std::max(lastKnownUserGestureTimestamp, document.page()->mainFrame().document()->lastHandledUserGestureTimestamp());
510
511     return MonotonicTime::now() - lastKnownUserGestureTimestamp < autostartSoonAfterUserGestureThreshold;
512 }
513
514 void HTMLPlugInImageElement::checkSizeChangeForSnapshotting()
515 {
516     if (!m_needsCheckForSizeChange || m_snapshotDecision != MaySnapshotWhenResized || documentHadRecentUserGesture(document()))
517         return;
518
519     m_needsCheckForSizeChange = false;
520
521     auto contentBoxRect = downcast<RenderBox>(*renderer()).contentBoxRect();
522     int contentWidth = contentBoxRect.width();
523     int contentHeight = contentBoxRect.height();
524
525     if (contentWidth <= sizingTinyDimensionThreshold || contentHeight <= sizingTinyDimensionThreshold)
526         return;
527
528     LOG(Plugins, "%p Plug-in originally avoided snapshotting because it was sized %dx%d. Now it is %dx%d. Tell it to snapshot.\n", this, m_sizeWhenSnapshotted.width(), m_sizeWhenSnapshotted.height(), contentWidth, contentHeight);
529     setDisplayState(WaitingForSnapshot);
530     m_snapshotDecision = Snapshotted;
531     auto* widget = pluginWidget();
532     if (is<PluginViewBase>(widget))
533         downcast<PluginViewBase>(*widget).beginSnapshottingRunningPlugin();
534 }
535
536 static inline bool is100Percent(Length length)
537 {
538     return length.isPercent() && length.percent() == 100;
539 }
540     
541 static inline bool isSmallerThanTinySizingThreshold(const RenderEmbeddedObject& renderer)
542 {
543     auto contentRect = renderer.contentBoxRect();
544     return contentRect.width() <= sizingTinyDimensionThreshold || contentRect.height() <= sizingTinyDimensionThreshold;
545 }
546     
547 bool HTMLPlugInImageElement::isTopLevelFullPagePlugin(const RenderEmbeddedObject& renderer) const
548 {
549     ASSERT(document().frame());
550     auto& frame = *document().frame();
551     if (!frame.isMainFrame())
552         return false;
553     
554     auto& style = renderer.style();
555     auto visibleSize = frame.view()->visibleSize();
556     auto contentRect = renderer.contentBoxRect();
557     float contentWidth = contentRect.width();
558     float contentHeight = contentRect.height();
559     return is100Percent(style.width()) && is100Percent(style.height()) && contentWidth * contentHeight > visibleSize.area().unsafeGet() * sizingFullPageAreaRatioThreshold;
560 }
561
562 void HTMLPlugInImageElement::checkSnapshotStatus()
563 {
564     if (!is<RenderSnapshottedPlugIn>(*renderer())) {
565         if (displayState() == Playing)
566             checkSizeChangeForSnapshotting();
567         return;
568     }
569     
570     // If width and height styles were previously not set and we've snapshotted the plugin we may need to restart the plugin so that its state can be updated appropriately.
571     if (!document().page()->settings().snapshotAllPlugIns() && displayState() <= DisplayingSnapshot && !m_plugInDimensionsSpecified) {
572         auto& renderer = downcast<RenderSnapshottedPlugIn>(*this->renderer());
573         if (!renderer.style().logicalWidth().isSpecified() && !renderer.style().logicalHeight().isSpecified())
574             return;
575         
576         m_plugInDimensionsSpecified = true;
577         if (isTopLevelFullPagePlugin(renderer)) {
578             m_snapshotDecision = NeverSnapshot;
579             restartSnapshottedPlugIn();
580         } else if (isSmallerThanTinySizingThreshold(renderer)) {
581             m_snapshotDecision = MaySnapshotWhenResized;
582             restartSnapshottedPlugIn();
583         }
584         return;
585     }
586     
587     // Notify the shadow root that the size changed so that we may update the overlay layout.
588     ensureUserAgentShadowRoot().dispatchEvent(Event::create(eventNames().resizeEvent, true, false));
589 }
590     
591 void HTMLPlugInImageElement::subframeLoaderWillCreatePlugIn(const URL& url)
592 {
593     LOG(Plugins, "%p Plug-in URL: %s", this, m_url.utf8().data());
594     LOG(Plugins, "   Actual URL: %s", url.string().utf8().data());
595     LOG(Plugins, "   MIME type: %s", loadedMimeType().utf8().data());
596
597     m_loadedUrl = url;
598     m_plugInWasCreated = false;
599     m_deferredPromotionToPrimaryPlugIn = false;
600
601     if (!document().page() || !document().page()->settings().plugInSnapshottingEnabled()) {
602         m_snapshotDecision = NeverSnapshot;
603         return;
604     }
605
606     if (displayState() == Restarting) {
607         LOG(Plugins, "%p Plug-in is explicitly restarting", this);
608         m_snapshotDecision = NeverSnapshot;
609         setDisplayState(Playing);
610         return;
611     }
612
613     if (displayState() == RestartingWithPendingMouseClick) {
614         LOG(Plugins, "%p Plug-in is explicitly restarting but also waiting for a click", this);
615         m_snapshotDecision = NeverSnapshot;
616         return;
617     }
618
619     if (m_snapshotDecision == NeverSnapshot) {
620         LOG(Plugins, "%p Plug-in is blessed, allow it to start", this);
621         return;
622     }
623
624     bool inMainFrame = document().frame()->isMainFrame();
625
626     if (document().isPluginDocument() && inMainFrame) {
627         LOG(Plugins, "%p Plug-in document in main frame", this);
628         m_snapshotDecision = NeverSnapshot;
629         return;
630     }
631
632     if (ScriptController::processingUserGesture()) {
633         LOG(Plugins, "%p Script is currently processing user gesture, set to play", this);
634         m_snapshotDecision = NeverSnapshot;
635         return;
636     }
637
638     if (m_createdDuringUserGesture) {
639         LOG(Plugins, "%p Plug-in was created when processing user gesture, set to play", this);
640         m_snapshotDecision = NeverSnapshot;
641         return;
642     }
643
644     if (documentHadRecentUserGesture(document())) {
645         LOG(Plugins, "%p Plug-in was created shortly after a user gesture, set to play", this);
646         m_snapshotDecision = NeverSnapshot;
647         return;
648     }
649
650     if (document().page()->settings().snapshotAllPlugIns()) {
651         LOG(Plugins, "%p Plug-in forced to snapshot by user preference", this);
652         m_snapshotDecision = Snapshotted;
653         setDisplayState(WaitingForSnapshot);
654         return;
655     }
656
657     if (document().page()->settings().autostartOriginPlugInSnapshottingEnabled() && document().page()->plugInClient() && document().page()->plugInClient()->shouldAutoStartFromOrigin(document().page()->mainFrame().document()->baseURL().host(), url.host(), loadedMimeType())) {
658         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());
659         m_snapshotDecision = NeverSnapshot;
660         return;
661     }
662
663     if (m_loadedUrl.isEmpty() && !loadedMimeType().isEmpty()) {
664         LOG(Plugins, "%p Plug-in has no src URL but does have a valid mime type %s, set to play", this, loadedMimeType().utf8().data());
665         m_snapshotDecision = MaySnapshotWhenContentIsSet;
666         return;
667     }
668
669     if (!SchemeRegistry::shouldTreatURLSchemeAsLocal(m_loadedUrl.protocol().toStringWithoutCopying()) && !m_loadedUrl.host().isEmpty() && m_loadedUrl.host() == document().page()->mainFrame().document()->baseURL().host()) {
670         LOG(Plugins, "%p Plug-in is served from page's domain, set to play", this);
671         m_snapshotDecision = NeverSnapshot;
672         return;
673     }
674     
675     auto& renderer = downcast<RenderEmbeddedObject>(*this->renderer());
676     auto contentRect = renderer.contentBoxRect();
677     int contentWidth = contentRect.width();
678     int contentHeight = contentRect.height();
679     
680     m_plugInDimensionsSpecified = renderer.style().logicalWidth().isSpecified() || renderer.style().logicalHeight().isSpecified();
681     
682     if (isTopLevelFullPagePlugin(renderer)) {
683         LOG(Plugins, "%p Plug-in is top level full page, set to play", this);
684         m_snapshotDecision = NeverSnapshot;
685         return;
686     }
687
688     if (isSmallerThanTinySizingThreshold(renderer)) {
689         LOG(Plugins, "%p Plug-in is very small %dx%d, set to play", this, contentWidth, contentHeight);
690         m_sizeWhenSnapshotted = IntSize(contentWidth, contentHeight);
691         m_snapshotDecision = MaySnapshotWhenResized;
692         return;
693     }
694
695     if (!document().page()->plugInClient()) {
696         LOG(Plugins, "%p There is no plug-in client. Set to wait for snapshot", this);
697         m_snapshotDecision = NeverSnapshot;
698         setDisplayState(WaitingForSnapshot);
699         return;
700     }
701
702     LOG(Plugins, "%p Plug-in from (%s, %s) is not auto-start, sized at %dx%d, set to wait for snapshot", this, document().topDocument().baseURL().host().utf8().data(), url.host().utf8().data(), contentWidth, contentHeight);
703     m_snapshotDecision = Snapshotted;
704     setDisplayState(WaitingForSnapshot);
705 }
706
707 void HTMLPlugInImageElement::subframeLoaderDidCreatePlugIn(const Widget& widget)
708 {
709     m_plugInWasCreated = true;
710
711     if (is<PluginViewBase>(widget) && downcast<PluginViewBase>(widget).shouldAlwaysAutoStart()) {
712         LOG(Plugins, "%p Plug-in should auto-start, set to play", this);
713         m_snapshotDecision = NeverSnapshot;
714         setDisplayState(Playing);
715         return;
716     }
717
718     if (m_deferredPromotionToPrimaryPlugIn) {
719         LOG(Plugins, "%p Plug-in was created, previously deferred promotion to primary. Will promote", this);
720         setIsPrimarySnapshottedPlugIn(true);
721         m_deferredPromotionToPrimaryPlugIn = false;
722     }
723 }
724
725 void HTMLPlugInImageElement::defaultEventHandler(Event& event)
726 {
727     if (is<RenderEmbeddedObject>(renderer()) && displayState() == WaitingForSnapshot && is<MouseEvent>(event) && event.type() == eventNames().clickEvent) {
728         auto& mouseEvent = downcast<MouseEvent>(event);
729         if (mouseEvent.button() == LeftButton) {
730             userDidClickSnapshot(mouseEvent, true);
731             mouseEvent.setDefaultHandled();
732             return;
733         }
734     }
735     HTMLPlugInElement::defaultEventHandler(event);
736 }
737
738 bool HTMLPlugInImageElement::allowedToLoadPluginContent(const String& url, const String& mimeType) const
739 {
740     // Elements in user agent show tree should load whatever the embedding document policy is.
741     if (isInUserAgentShadowTree())
742         return true;
743
744     URL completedURL;
745     if (!url.isEmpty())
746         completedURL = document().completeURL(url);
747
748     ASSERT(document().contentSecurityPolicy());
749     const ContentSecurityPolicy& contentSecurityPolicy = *document().contentSecurityPolicy();
750
751     contentSecurityPolicy.upgradeInsecureRequestIfNeeded(completedURL, ContentSecurityPolicy::InsecureRequestType::Load);
752
753     if (!contentSecurityPolicy.allowObjectFromSource(completedURL))
754         return false;
755
756     auto& declaredMimeType = document().isPluginDocument() && document().ownerElement() ?
757         document().ownerElement()->attributeWithoutSynchronization(HTMLNames::typeAttr) : attributeWithoutSynchronization(HTMLNames::typeAttr);
758     return contentSecurityPolicy.allowPluginType(mimeType, declaredMimeType, completedURL);
759 }
760
761 bool HTMLPlugInImageElement::requestObject(const String& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
762 {
763     ASSERT(document().frame());
764
765     if (url.isEmpty() && mimeType.isEmpty())
766         return false;
767
768     if (!allowedToLoadPluginContent(url, mimeType)) {
769         renderEmbeddedObject()->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy);
770         return false;
771     }
772
773     if (HTMLPlugInElement::requestObject(url, mimeType, paramNames, paramValues))
774         return true;
775     
776     return document().frame()->loader().subframeLoader().requestObject(*this, url, getNameAttribute(), mimeType, paramNames, paramValues);
777 }
778
779 } // namespace WebCore