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