1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2000 Stefan Schimanski (1Stein@gmx.de)
5  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2011 Apple Inc. All rights
6  * reserved.
7  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24 
25 #include "third_party/blink/renderer/core/html/html_object_element.h"
26 
27 #include "third_party/blink/renderer/bindings/core/v8/script_event_listener.h"
28 #include "third_party/blink/renderer/core/css/style_change_reason.h"
29 #include "third_party/blink/renderer/core/dom/attribute.h"
30 #include "third_party/blink/renderer/core/dom/document.h"
31 #include "third_party/blink/renderer/core/dom/element_traversal.h"
32 #include "third_party/blink/renderer/core/dom/shadow_root.h"
33 #include "third_party/blink/renderer/core/dom/tag_collection.h"
34 #include "third_party/blink/renderer/core/dom/text.h"
35 #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
36 #include "third_party/blink/renderer/core/frame/local_frame.h"
37 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
38 #include "third_party/blink/renderer/core/frame/settings.h"
39 #include "third_party/blink/renderer/core/html/html_image_loader.h"
40 #include "third_party/blink/renderer/core/html/html_meta_element.h"
41 #include "third_party/blink/renderer/core/html/html_param_element.h"
42 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
43 #include "third_party/blink/renderer/core/html_names.h"
44 #include "third_party/blink/renderer/core/layout/layout_embedded_object.h"
45 #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
46 
47 namespace blink {
48 
HTMLObjectElement(Document & document,const CreateElementFlags flags)49 HTMLObjectElement::HTMLObjectElement(Document& document,
50                                      const CreateElementFlags flags)
51     : HTMLPlugInElement(html_names::kObjectTag,
52                         document,
53                         flags,
54                         kShouldNotPreferPlugInsForImages),
55       use_fallback_content_(false) {
56   EnsureUserAgentShadowRoot();
57 }
58 
59 inline HTMLObjectElement::~HTMLObjectElement() = default;
60 
Trace(Visitor * visitor)61 void HTMLObjectElement::Trace(Visitor* visitor) {
62   ListedElement::Trace(visitor);
63   HTMLPlugInElement::Trace(visitor);
64 }
65 
GetCheckedAttributeTypes() const66 const AttrNameToTrustedType& HTMLObjectElement::GetCheckedAttributeTypes()
67     const {
68   DEFINE_STATIC_LOCAL(AttrNameToTrustedType, attribute_map,
69                       ({{"data", SpecificTrustedType::kScriptURL},
70                         {"codebase", SpecificTrustedType::kScriptURL}}));
71   return attribute_map;
72 }
73 
ExistingLayoutEmbeddedContent() const74 LayoutEmbeddedContent* HTMLObjectElement::ExistingLayoutEmbeddedContent()
75     const {
76   // This will return 0 if the layoutObject is not a LayoutEmbeddedContent.
77   return GetLayoutEmbeddedContent();
78 }
79 
IsPresentationAttribute(const QualifiedName & name) const80 bool HTMLObjectElement::IsPresentationAttribute(
81     const QualifiedName& name) const {
82   if (name == html_names::kBorderAttr)
83     return true;
84   return HTMLPlugInElement::IsPresentationAttribute(name);
85 }
86 
CollectStyleForPresentationAttribute(const QualifiedName & name,const AtomicString & value,MutableCSSPropertyValueSet * style)87 void HTMLObjectElement::CollectStyleForPresentationAttribute(
88     const QualifiedName& name,
89     const AtomicString& value,
90     MutableCSSPropertyValueSet* style) {
91   if (name == html_names::kBorderAttr)
92     ApplyBorderAttributeToStyle(value, style);
93   else
94     HTMLPlugInElement::CollectStyleForPresentationAttribute(name, value, style);
95 }
96 
ParseAttribute(const AttributeModificationParams & params)97 void HTMLObjectElement::ParseAttribute(
98     const AttributeModificationParams& params) {
99   const QualifiedName& name = params.name;
100   if (name == html_names::kFormAttr) {
101     FormAttributeChanged();
102   } else if (name == html_names::kTypeAttr) {
103     SetServiceType(params.new_value.LowerASCII());
104     wtf_size_t pos = service_type_.Find(";");
105     if (pos != kNotFound)
106       SetServiceType(service_type_.Left(pos));
107     // TODO(schenney): crbug.com/572908 What is the right thing to do here?
108     // Should we suppress the reload stuff when a persistable widget-type is
109     // specified?
110     ReloadPluginOnAttributeChange(name);
111   } else if (name == html_names::kDataAttr) {
112     SetUrl(StripLeadingAndTrailingHTMLSpaces(params.new_value));
113     if (GetLayoutObject() && IsImageType()) {
114       SetNeedsPluginUpdate(true);
115       if (!image_loader_)
116         image_loader_ = MakeGarbageCollected<HTMLImageLoader>(this);
117       image_loader_->UpdateFromElement(ImageLoader::kUpdateIgnorePreviousError);
118     } else {
119       ReloadPluginOnAttributeChange(name);
120     }
121   } else if (name == html_names::kClassidAttr) {
122     class_id_ = params.new_value;
123     ReloadPluginOnAttributeChange(name);
124   } else {
125     HTMLPlugInElement::ParseAttribute(params);
126   }
127 }
128 
129 // TODO(schenney): crbug.com/572908 This function should not deal with url or
130 // serviceType!
ParametersForPlugin(PluginParameters & plugin_params)131 void HTMLObjectElement::ParametersForPlugin(PluginParameters& plugin_params) {
132   HashSet<StringImpl*, CaseFoldingHash> unique_param_names;
133 
134   // Scan the PARAM children and store their name/value pairs.
135   // Get the URL and type from the params if we don't already have them.
136   for (HTMLParamElement* p = Traversal<HTMLParamElement>::FirstChild(*this); p;
137        p = Traversal<HTMLParamElement>::NextSibling(*p)) {
138     String name = p->GetName();
139     if (name.IsEmpty())
140       continue;
141 
142     unique_param_names.insert(name.Impl());
143     plugin_params.AppendNameWithValue(p->GetName(), p->Value());
144 
145     // TODO(schenney): crbug.com/572908 url adjustment does not belong in this
146     // function.
147     // HTML5 says that an object resource's URL is specified by the object's
148     // data attribute, not by a param element with a name of "data". However,
149     // for compatibility, allow the resource's URL to be given by a param
150     // element with one of the common names if we know that resource points
151     // to a plugin.
152     if (url_.IsEmpty() && !EqualIgnoringASCIICase(name, "data") &&
153         HTMLParamElement::IsURLParameter(name)) {
154       SetUrl(StripLeadingAndTrailingHTMLSpaces(p->Value()));
155     }
156     // TODO(schenney): crbug.com/572908 serviceType calculation does not belong
157     // in this function.
158     if (service_type_.IsEmpty() && EqualIgnoringASCIICase(name, "type")) {
159       wtf_size_t pos = p->Value().Find(";");
160       if (pos != kNotFound)
161         SetServiceType(p->Value().GetString().Left(pos));
162     }
163   }
164 
165   // Turn the attributes of the <object> element into arrays, but don't override
166   // <param> values.
167   AttributeCollection attributes = Attributes();
168   for (const Attribute& attribute : attributes) {
169     const AtomicString& name = attribute.GetName().LocalName();
170     if (!unique_param_names.Contains(name.Impl()))
171       plugin_params.AppendAttribute(attribute);
172   }
173 
174   // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e.
175   // Real and WMP require "src" attribute).
176   plugin_params.MapDataParamToSrc();
177 }
178 
HasFallbackContent() const179 bool HTMLObjectElement::HasFallbackContent() const {
180   for (Node* child = firstChild(); child; child = child->nextSibling()) {
181     // Ignore whitespace-only text, and <param> tags, any other content is
182     // fallback content.
183     auto* child_text_node = DynamicTo<Text>(child);
184     if (child_text_node) {
185       if (!child_text_node->ContainsOnlyWhitespaceOrEmpty())
186         return true;
187     } else if (!IsA<HTMLParamElement>(*child)) {
188       return true;
189     }
190   }
191   return false;
192 }
193 
HasValidClassId() const194 bool HTMLObjectElement::HasValidClassId() const {
195   if (MIMETypeRegistry::IsJavaAppletMIMEType(service_type_) &&
196       ClassId().StartsWithIgnoringASCIICase("java:"))
197     return true;
198 
199   // HTML5 says that fallback content should be rendered if a non-empty
200   // classid is specified for which the UA can't find a suitable plugin.
201   return ClassId().IsEmpty();
202 }
203 
ReloadPluginOnAttributeChange(const QualifiedName & name)204 void HTMLObjectElement::ReloadPluginOnAttributeChange(
205     const QualifiedName& name) {
206   // Following,
207   //   http://www.whatwg.org/specs/web-apps/current-work/#the-object-element
208   //   (Enumerated list below "Whenever one of the following conditions occur:")
209   //
210   // the updating of certain attributes should bring about "redetermination"
211   // of what the element contains.
212   bool needs_invalidation;
213   if (name == html_names::kTypeAttr) {
214     needs_invalidation = !FastHasAttribute(html_names::kClassidAttr) &&
215                          !FastHasAttribute(html_names::kDataAttr);
216   } else if (name == html_names::kDataAttr) {
217     needs_invalidation = !FastHasAttribute(html_names::kClassidAttr);
218   } else if (name == html_names::kClassidAttr) {
219     needs_invalidation = true;
220   } else {
221     NOTREACHED();
222     needs_invalidation = false;
223   }
224   SetNeedsPluginUpdate(true);
225   if (needs_invalidation)
226     ReattachOnPluginChangeIfNeeded();
227 }
228 
229 // TODO(schenney): crbug.com/572908 This should be unified with
230 // HTMLEmbedElement::UpdatePlugin and moved down into html_plugin_element.cc
UpdatePluginInternal()231 void HTMLObjectElement::UpdatePluginInternal() {
232   DCHECK(!GetLayoutEmbeddedObject()->ShowsUnavailablePluginIndicator());
233   DCHECK(NeedsPluginUpdate());
234   SetNeedsPluginUpdate(false);
235   // TODO(schenney): crbug.com/572908 This should ASSERT
236   // isFinishedParsingChildren() instead.
237   if (!IsFinishedParsingChildren()) {
238     DispatchErrorEvent();
239     return;
240   }
241 
242   // TODO(schenney): crbug.com/572908 I'm not sure it's ever possible to get
243   // into updateWidget during a removal, but just in case we should avoid
244   // loading the frame to prevent security bugs.
245   if (!SubframeLoadingDisabler::CanLoadFrame(*this)) {
246     DispatchErrorEvent();
247     return;
248   }
249 
250   PluginParameters plugin_params;
251   ParametersForPlugin(plugin_params);
252 
253   // Note: url is modified above by parametersForPlugin.
254   if (!AllowedToLoadFrameURL(url_)) {
255     DispatchErrorEvent();
256     return;
257   }
258 
259   // TODO(schenney): crbug.com/572908 Is it possible to get here without a
260   // layoutObject now that we don't have beforeload events?
261   if (!GetLayoutObject())
262     return;
263 
264   // Overwrites the URL and MIME type of a Flash embed to use an HTML5 embed.
265   KURL overriden_url =
266       GetDocument().GetFrame()->Client()->OverrideFlashEmbedWithHTML(
267           GetDocument().CompleteURL(url_));
268   if (!overriden_url.IsEmpty()) {
269     url_ = overriden_url.GetString();
270     SetServiceType("text/html");
271   }
272 
273   if (!HasValidClassId() || !RequestObject(plugin_params)) {
274     if (!url_.IsEmpty())
275       DispatchErrorEvent();
276     if (HasFallbackContent())
277       RenderFallbackContent(ContentFrame());
278   } else {
279     if (IsErrorplaceholder())
280       DispatchErrorEvent();
281   }
282 }
283 
InsertedInto(ContainerNode & insertion_point)284 Node::InsertionNotificationRequest HTMLObjectElement::InsertedInto(
285     ContainerNode& insertion_point) {
286   HTMLPlugInElement::InsertedInto(insertion_point);
287   ListedElement::InsertedInto(insertion_point);
288   return kInsertionDone;
289 }
290 
RemovedFrom(ContainerNode & insertion_point)291 void HTMLObjectElement::RemovedFrom(ContainerNode& insertion_point) {
292   HTMLPlugInElement::RemovedFrom(insertion_point);
293   ListedElement::RemovedFrom(insertion_point);
294 }
295 
ChildrenChanged(const ChildrenChange & change)296 void HTMLObjectElement::ChildrenChanged(const ChildrenChange& change) {
297   if (isConnected() && !UseFallbackContent()) {
298     SetNeedsPluginUpdate(true);
299     ReattachOnPluginChangeIfNeeded();
300   }
301   HTMLPlugInElement::ChildrenChanged(change);
302 }
303 
IsURLAttribute(const Attribute & attribute) const304 bool HTMLObjectElement::IsURLAttribute(const Attribute& attribute) const {
305   return attribute.GetName() == html_names::kCodebaseAttr ||
306          attribute.GetName() == html_names::kDataAttr ||
307          (attribute.GetName() == html_names::kUsemapAttr &&
308           attribute.Value()[0] != '#') ||
309          HTMLPlugInElement::IsURLAttribute(attribute);
310 }
311 
HasLegalLinkAttribute(const QualifiedName & name) const312 bool HTMLObjectElement::HasLegalLinkAttribute(const QualifiedName& name) const {
313   return name == html_names::kClassidAttr || name == html_names::kDataAttr ||
314          name == html_names::kCodebaseAttr ||
315          HTMLPlugInElement::HasLegalLinkAttribute(name);
316 }
317 
SubResourceAttributeName() const318 const QualifiedName& HTMLObjectElement::SubResourceAttributeName() const {
319   return html_names::kDataAttr;
320 }
321 
ImageSourceURL() const322 const AtomicString HTMLObjectElement::ImageSourceURL() const {
323   return FastGetAttribute(html_names::kDataAttr);
324 }
325 
ReattachFallbackContent()326 void HTMLObjectElement::ReattachFallbackContent() {
327   if (!GetDocument().InStyleRecalc()) {
328     // TODO(futhark): Currently needs kSubtreeStyleChange because a style recalc
329     // for the object element does not detect the changed need for descendant
330     // style when we have a change in HTMLObjectElement::ChildrenCanHaveStyle().
331     SetNeedsStyleRecalc(
332         kSubtreeStyleChange,
333         StyleChangeReasonForTracing::Create(style_change_reason::kUseFallback));
334     SetForceReattachLayoutTree();
335   }
336 }
337 
RenderFallbackContent(Frame * frame)338 void HTMLObjectElement::RenderFallbackContent(Frame* frame) {
339   DCHECK(!frame || frame == ContentFrame());
340   if (UseFallbackContent())
341     return;
342 
343   if (!isConnected())
344     return;
345 
346   // Before we give up and use fallback content, check to see if this is a MIME
347   // type issue.
348   if (image_loader_ && image_loader_->GetContent() &&
349       image_loader_->GetContent()->GetContentStatus() !=
350           ResourceStatus::kLoadError) {
351     SetServiceType(image_loader_->GetContent()->GetResponse().MimeType());
352     if (!IsImageType()) {
353       // If we don't think we have an image type anymore, then clear the image
354       // from the loader.
355       image_loader_->ClearImage();
356       ReattachFallbackContent();
357       return;
358     }
359   }
360 
361   use_fallback_content_ = true;
362   ReattachFallbackContent();
363 }
364 
IsExposed() const365 bool HTMLObjectElement::IsExposed() const {
366   // http://www.whatwg.org/specs/web-apps/current-work/#exposed
367   for (HTMLObjectElement* ancestor =
368            Traversal<HTMLObjectElement>::FirstAncestor(*this);
369        ancestor;
370        ancestor = Traversal<HTMLObjectElement>::FirstAncestor(*ancestor)) {
371     if (ancestor->IsExposed())
372       return false;
373   }
374   for (HTMLElement& element : Traversal<HTMLElement>::DescendantsOf(*this)) {
375     if (IsA<HTMLObjectElement>(element) || IsA<HTMLEmbedElement>(element))
376       return false;
377   }
378   return true;
379 }
380 
ContainsJavaApplet() const381 bool HTMLObjectElement::ContainsJavaApplet() const {
382   if (MIMETypeRegistry::IsJavaAppletMIMEType(
383           FastGetAttribute(html_names::kTypeAttr)))
384     return true;
385 
386   for (HTMLElement& child : Traversal<HTMLElement>::ChildrenOf(*this)) {
387     if (IsA<HTMLParamElement>(child) &&
388         EqualIgnoringASCIICase(child.GetNameAttribute(), "type") &&
389         MIMETypeRegistry::IsJavaAppletMIMEType(
390             child.FastGetAttribute(html_names::kValueAttr).GetString()))
391       return true;
392 
393     auto* html_image_element = DynamicTo<HTMLObjectElement>(child);
394     if (html_image_element && html_image_element->ContainsJavaApplet())
395       return true;
396   }
397 
398   return false;
399 }
400 
DidMoveToNewDocument(Document & old_document)401 void HTMLObjectElement::DidMoveToNewDocument(Document& old_document) {
402   ListedElement::DidMoveToNewDocument(old_document);
403   HTMLPlugInElement::DidMoveToNewDocument(old_document);
404 }
405 
formOwner() const406 HTMLFormElement* HTMLObjectElement::formOwner() const {
407   return ListedElement::Form();
408 }
409 
IsInteractiveContent() const410 bool HTMLObjectElement::IsInteractiveContent() const {
411   return FastHasAttribute(html_names::kUsemapAttr);
412 }
413 
UseFallbackContent() const414 bool HTMLObjectElement::UseFallbackContent() const {
415   return HTMLPlugInElement::UseFallbackContent() || use_fallback_content_;
416 }
417 
WillUseFallbackContentAtLayout() const418 bool HTMLObjectElement::WillUseFallbackContentAtLayout() const {
419   return !HasValidClassId() && HasFallbackContent();
420 }
421 
AssociateWith(HTMLFormElement * form)422 void HTMLObjectElement::AssociateWith(HTMLFormElement* form) {
423   AssociateByParser(form);
424 }
425 
DidFinishLoading() const426 bool HTMLObjectElement::DidFinishLoading() const {
427   if (!isConnected())
428     return false;
429   if (OwnedPlugin())
430     return true;
431   if (auto* frame = ContentFrame()) {
432     if (!frame->IsLoading())
433       return true;
434   }
435   if (ImageLoader() && !HasPendingActivity() && IsImageType())
436     return true;
437 
438   return UseFallbackContent();
439 }
440 
DefaultTabIndex() const441 int HTMLObjectElement::DefaultTabIndex() const {
442   return 0;
443 }
444 
ToHTMLObjectElementFromListedElement(const ListedElement * element)445 const HTMLObjectElement* ToHTMLObjectElementFromListedElement(
446     const ListedElement* element) {
447   SECURITY_DCHECK(!element || !element->IsFormControlElement());
448   const HTMLObjectElement* object_element =
449       static_cast<const HTMLObjectElement*>(element);
450   // We need to assert after the cast because ListedElement doesn't
451   // have hasTagName.
452   SECURITY_DCHECK(!object_element ||
453                   object_element->HasTagName(html_names::kObjectTag));
454   return object_element;
455 }
456 
ToHTMLObjectElementFromListedElement(const ListedElement & element)457 const HTMLObjectElement& ToHTMLObjectElementFromListedElement(
458     const ListedElement& element) {
459   return *ToHTMLObjectElementFromListedElement(&element);
460 }
461 
462 }  // namespace blink
463