Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(381)

Side by Side Diff: Source/core/html/HTMLMetaElement-in.cpp

Issue 19555002: Translate viewport related meta tags into @viewport descriptors as suggested by the CSS Device Adap… (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2003, 2010 Apple Inc. All rights reserved.
6 * Copyright (C) 2013 Intel Corporation. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "config.h"
25 #include "core/html/HTMLMetaElement.h"
26
27 #include "HTMLNames.h"
28 #include "RuntimeEnabledFeatures.h"
29 #include "core/css/CSSStyleSheet.h"
30 #include "core/css/CSSValuePool.h"
31 #include "core/css/MediaList.h"
32 #include "core/css/StylePropertySet.h"
33 #include "core/css/StyleSheetContents.h"
34 #include "core/dom/Document.h"
35 #include "core/dom/DocumentStyleSheetCollection.h"
36 #include "wtf/text/StringBuilder.h"
37 #include "wtf/text/TextPosition.h"
38
39 namespace WebCore {
40
41 using namespace HTMLNames;
42 using namespace std;
43
44 inline HTMLMetaElement::HTMLMetaElement(const QualifiedName& tagName, Document* document)
45 : HTMLElement(tagName, document)
46 {
47 ASSERT(hasTagName(metaTag));
48 ScriptWrappable::init(this);
49 }
50
51 HTMLMetaElement::~HTMLMetaElement()
52 {
53 removeStyleSheet();
54 }
55
56 PassRefPtr<HTMLMetaElement> HTMLMetaElement::create(Document* document)
57 {
58 return adoptRef(new HTMLMetaElement(metaTag, document));
59 }
60
61 PassRefPtr<HTMLMetaElement> HTMLMetaElement::create(const QualifiedName& tagName , Document* document)
62 {
63 return adoptRef(new HTMLMetaElement(tagName, document));
64 }
65
66 void HTMLMetaElement::removedFrom(ContainerNode* insertionPoint)
67 {
68 HTMLElement::removedFrom(insertionPoint);
69 removeStyleSheet();
70 }
71
72 Node::InsertionNotificationRequest HTMLMetaElement::insertedInto(ContainerNode* insertionPoint)
73 {
74 HTMLElement::insertedInto(insertionPoint);
75 if (insertionPoint->inDocument())
76 process();
77 return InsertionDone;
78 }
79
80
81 static inline bool isValueSeparator(UChar c, bool* ok)
82 {
83 if (ok && c == ';')
84 *ok = false;
85 // Though isspace() considers \t and \v to be whitespace, Win IE doesn't.
86 return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ' ,' || c == '\0';
87 }
88
89 static inline bool isPropertySeparator(UChar c, bool* ok)
90 {
91 if (ok && c == ';')
92 *ok = false;
93 return c == ',';
94 }
95
96 float HTMLMetaElement::parsePositiveNumber(const String& property, const String& value, float minValue, float maxValue)
97 {
98 size_t parsedLength;
99 float rawValue;
100 float fValue;
101 if (value.is8Bit())
102 rawValue = charactersToFloat(value.characters8(), value.length(), parsed Length);
103 else
104 rawValue = charactersToFloat(value.characters16(), value.length(), parse dLength);
105
106 if (!parsedLength || rawValue < 0) {
107 reportError(InvalidValueError, property, value);
108 return -1;
109 }
110
111 if (parsedLength < value.length())
112 reportError(TruncatedValueError, property, value);
113
114 fValue = min(maxValue, max(rawValue, minValue));
115 if (fValue != rawValue)
116 reportError(OutOfBoundsValueError, property, value);
117
118 return fValue;
119 }
120
121 void HTMLMetaElement::reportError(ErrorType error, const String& property, const String& value)
122 {
123 StringBuilder builder;
124
125 switch (error) {
126 case NoError:
127 return;
128
129 case InvalidPropertySeparatorError:
130 builder.appendLiteral("Note that ';' is not a property separator. The li st should be comma-separated.");
131 break;
132
133 case InvalidPropertyError:
134 builder.appendLiteral("Property \"");
135 builder.append(property);
136 builder.appendLiteral("\" not recognized and ignored.");
137 break;
138
139 case InvalidValueError:
140 case OutOfBoundsValueError:
141 case TruncatedValueError:
142 if (!value.isEmpty()) {
143 builder.appendLiteral("The value \"");
144 builder.append(value);
145 builder.append('\"');
146 } else {
147 builder.appendLiteral("The empty value");
148 }
149
150 if (!property.isNull()) {
151 builder.appendLiteral(" for property \"");
152 builder.append(property);
153 builder.append('\"');
154 }
155
156 if (error == InvalidValueError)
157 builder.appendLiteral(" is invalid and the property has been ignored .");
158 else if (error == OutOfBoundsValueError)
159 builder.appendLiteral(" is out of bounds and the value has been clam ped.");
160 else
161 builder.appendLiteral(" was truncated to its numeric prefix.");
162 break;
163
164 default:
165 ASSERT_NOT_REACHED();
166 }
167
168 document()->addConsoleMessage(MetaContentMessageSource, WarningMessageLevel, builder.toString());
169 }
170
171 void HTMLMetaElement::parseContentAttribute(MutableStylePropertySet* properties, const String& content, PropertyCollector callback)
172 {
173 bool ok = true;
174
175 // Tread lightly in this code -- it was specifically designed to mimic Win I E's parsing behavior.
176 int keyBegin, keyEnd;
177 int valueBegin, valueEnd;
178
179 int i = 0;
180 int length = content.length();
181 String buffer = content.lower();
182 while (i < length) {
183 // Skip to first non-separator, but don't skip past the end of the strin g.
184 while (isValueSeparator(buffer[i], &ok)) {
185 if (i >= length)
186 break;
187 i++;
188 }
189 keyBegin = i;
190
191 // skip to first separator
192 while (!isValueSeparator(buffer[i], &ok))
193 i++;
194 keyEnd = i;
195
196 // Skip to first '=', but don't skip past a ',' or the end of the string .
197 while (buffer[i] != '=') {
198 if (isPropertySeparator(buffer[i], &ok) || i >= length)
199 break;
200 i++;
201 }
202
203 // Skip to first non-separator, but don't skip past a ',' or the end of the string.
204 while (isValueSeparator(buffer[i], &ok)) {
205 if (isPropertySeparator(buffer[i], &ok) || i >= length)
206 break;
207 i++;
208 }
209 valueBegin = i;
210
211 // Skip to first separator.
212 while (!isValueSeparator(buffer[i], &ok))
213 i++;
214 valueEnd = i;
215
216 ASSERT_WITH_SECURITY_IMPLICATION(i <= length);
217
218 String propertyString = buffer.substring(keyBegin, keyEnd - keyBegin);
219 String valueString = buffer.substring(valueBegin, valueEnd - valueBegin) ;
220
221 callback(this, properties, propertyString, valueString);
222 }
223 if (!ok)
224 reportError(InvalidPropertySeparatorError, String(), String());
225 }
226
227 PassRefPtr<CSSValue> HTMLMetaElement::parseViewportValueAsLength(const String& p ropertyString, const String& valueString)
228 {
229 const UChar* characters;
230 unsigned valueLength = valueString.length();
231
232 const unsigned longestValueLength = 13;
233 UChar characterBuffer[longestValueLength];
234 if (valueString.is8Bit()) {
235 unsigned length = std::min(longestValueLength, valueLength);
236 const LChar* characters8 = valueString.characters8();
237 for (unsigned i = 0; i < length; ++i)
238 characterBuffer[i] = characters8[i];
239 characters = characterBuffer;
240 } else {
241 characters = valueString.characters16();
242 }
243
244 SWITCH(characters, valueLength) {
245 CASE("device-width") {
246 return cssValuePool().createValue(100, CSSPrimitiveValue::CSS_PERCEN TAGE);
247 }
248 CASE("device-height") {
249 return cssValuePool().createValue(100, CSSPrimitiveValue::CSS_PERCEN TAGE);
250 }
251 }
252
253 // Other keywords and unknown values translate to 1px.
254 float value = parsePositiveNumber(propertyString, valueString, float(1), flo at(10000));
255 if (value >= 0)
256 return cssValuePool().createValue(value, CSSPrimitiveValue::CSS_PX);
257
258 return 0;
259 }
260
261 PassRefPtr<CSSValue> HTMLMetaElement::parseViewportValueAsZoom(const String& pro pertyString, const String& valueString)
262 {
263 const UChar* characters;
264 unsigned valueLength = valueString.length();
265
266 const unsigned longestValueLength = 13;
267 UChar characterBuffer[longestValueLength];
268 if (valueString.is8Bit()) {
269 unsigned length = std::min(longestValueLength, valueLength);
270 const LChar* characters8 = valueString.characters8();
271 for (unsigned i = 0; i < length; ++i)
272 characterBuffer[i] = characters8[i];
273 characters = characterBuffer;
274 } else {
275 characters = valueString.characters16();
276 }
277
278 float value = -1;
279 SWITCH(characters, valueLength) {
280 CASE("yes") {
281 value = 1;
282 }
283 CASE("no") {
284 value = 0.1;
285 }
286 CASE("device-width") {
287 value = 10;
288 }
289 CASE("device-height") {
290 value = 10;
291 }
292 }
293
294 if (value < 0)
295 value = parsePositiveNumber(propertyString, valueString, float(0.1), flo at(10));
296 if (value >= 0)
297 return cssValuePool().createValue(value, CSSPrimitiveValue::CSS_NUMBER);
298
299 return 0;
300 }
301
302 CSSValueID HTMLMetaElement::parseViewportValueAsUserZoom(const String& propertyS tring, const String& valueString)
303 {
304 const UChar* characters;
305 unsigned valueLength = valueString.length();
306
307 const unsigned longestValueLength = 13;
308 UChar characterBuffer[longestValueLength];
309 if (valueString.is8Bit()) {
310 unsigned length = std::min(longestValueLength, valueLength);
311 const LChar* characters8 = valueString.characters8();
312 for (unsigned i = 0; i < length; ++i)
313 characterBuffer[i] = characters8[i];
314 characters = characterBuffer;
315 } else {
316 characters = valueString.characters16();
317 }
318
319 SWITCH(characters, valueLength) {
320 CASE("yes") {
321 return CSSValueZoom;
322 }
323 CASE("no") {
324 return CSSValueFixed;
325 }
326 CASE("device-width") {
327 return CSSValueZoom;
328 }
329 CASE("device-height") {
330 return CSSValueZoom;
331 }
332 }
333
334 float value = parsePositiveNumber(propertyString, valueString, float(0), flo at(1));
335 // Numbers in the range <-1, 1>, and unknown values (represented as 0), are mapped to "fixed".
336 // Numbers >= 1, numbers <= -1 are mapped to "zoom"
337 return (value > -1 && value < 1) ? CSSValueFixed : CSSValueZoom;
338 }
339
340 PassRefPtr<CSSValue> HTMLMetaElement::parseViewportValueAsDPI(const String& prop ertyString, const String& valueString)
341 {
342 const UChar* characters;
343 unsigned valueLength = valueString.length();
344
345 const unsigned longestValueLength = 10;
346 UChar characterBuffer[longestValueLength];
347 if (valueString.is8Bit()) {
348 unsigned length = std::min(longestValueLength, valueLength);
349 const LChar* characters8 = valueString.characters8();
350 for (unsigned i = 0; i < length; ++i)
351 characterBuffer[i] = characters8[i];
352 characters = characterBuffer;
353 } else {
354 characters = valueString.characters16();
355 }
356
357 CSSValueID id = CSSValueAuto; // Fallback for invalid.
358 SWITCH(characters, valueLength) {
359 CASE("device-dpi") {
360 id = CSSValueDevice;
361 }
362 CASE("low-dpi") {
363 id = CSSValueSmall;
364 }
365 CASE("medium-dpi") {
366 id = CSSValueMedium;
367 }
368 CASE("high-dpi") {
369 id = CSSValueLarge;
370 }
371 }
372
373 if (id == CSSValueAuto) {
374 float value = parsePositiveNumber(propertyString, valueString, float(70) , float(400));
375
376 if (value >= 70 && value <= 400)
377 return cssValuePool().createValue(value, CSSPrimitiveValue::CSS_NUMB ER);
378 }
379
380 return cssValuePool().createIdentifierValue(id);
381 }
382
383 void HTMLMetaElement::viewportPropertyCollector(HTMLMetaElement* self, MutableSt ylePropertySet* properties, const String& propertyString, const String& valueStr ing)
384 {
385 const UChar* characters;
386 unsigned propertyLength = propertyString.length();
387
388 const unsigned longestPropertyLength = 17;
389 UChar characterBuffer[longestPropertyLength];
390 if (propertyString.is8Bit()) {
391 unsigned length = std::min(longestPropertyLength, propertyLength);
392 const LChar* characters8 = propertyString.characters8();
393 for (unsigned i = 0; i < length; ++i)
394 characterBuffer[i] = characters8[i];
395 characters = characterBuffer;
396 } else {
397 characters = propertyString.characters16();
398 }
399
400 SWITCH(characters, propertyLength) {
401 CASE("width") {
402 RefPtr<CSSValue> value = self->parseViewportValueAsLength(propertySt ring, valueString);
403 if (!value)
404 return;
405 properties->setProperty(CSSPropertyMinWidth, CSSValueInternalExtendT oZoom);
406 properties->setProperty(CSSPropertyMaxWidth, value);
407 return;
408 }
409 CASE("height") {
410 RefPtr<CSSValue> value = self->parseViewportValueAsLength(propertySt ring, valueString);
411 if (!value)
412 return;
413 properties->setProperty(CSSPropertyMinHeight, CSSValueInternalExtend ToZoom);
414 properties->setProperty(CSSPropertyMaxHeight, value);
415 return;
416 }
417 CASE("initial-scale") {
418 if (RefPtr<CSSValue> value = self->parseViewportValueAsZoom(property String, valueString))
419 properties->setProperty(CSSPropertyZoom, value);
420 return;
421 }
422 CASE("minimum-scale") {
423 if (RefPtr<CSSValue> value = self->parseViewportValueAsZoom(property String, valueString))
424 properties->setProperty(CSSPropertyMinZoom, value);
425 return;
426 }
427 CASE("maximum-scale") {
428 if (RefPtr<CSSValue> value = self->parseViewportValueAsZoom(property String, valueString))
429 properties->setProperty(CSSPropertyMaxZoom, value);
430 return;
431 }
432 CASE("user-scalable") {
433 CSSValueID value = self->parseViewportValueAsUserZoom(propertyString , valueString);
434 properties->setProperty(CSSPropertyUserZoom, value);
435 return;
436 }
437 CASE("target-densitydpi") {
438 RefPtr<CSSValue> value = self->parseViewportValueAsDPI(propertyStrin g, valueString);
439 properties->setProperty(CSSPropertyInternalTargetDensity, value);
440 return;
441 }
442 }
443 self->reportError(InvalidPropertyError, propertyString, String());
444 }
445
446 PassRefPtr<StyleRuleBase> HTMLMetaElement::parseViewportContent(const String& co ntentValue)
447 {
448 RefPtr<StyleRuleViewport> rule = StyleRuleViewport::create();
449 RefPtr<MutableStylePropertySet> properties = MutableStylePropertySet::create (CSSStrictMode);
450 parseContentAttribute(properties.get(), contentValue, &HTMLMetaElement::view portPropertyCollector);
451
452 // For a viewport META element that translates into an @viewport rule with a
453 // non-"auto" "zoom" declaration and no "width" declaration:
454 // If it adds a "height" descriptor, add: width: auto; Otherwise, add: width : extend-to-zoom;
455 RefPtr<CSSValue> widthValue = properties->getPropertyCSSValue(CSSPropertyMin Width);
456 RefPtr<CSSValue> zoomValue = properties->getPropertyCSSValue(CSSPropertyZoom );
457
458 if (!widthValue && zoomValue) {
459 RefPtr<CSSValue> heightValue = properties->getPropertyCSSValue(CSSProper tyMinHeight);
460 CSSValueID commonHeightValue = heightValue ? CSSValueAuto : CSSValueInte rnalExtendToZoom;
461 properties->setProperty(CSSPropertyMinWidth, commonHeightValue);
462 properties->setProperty(CSSPropertyMaxWidth, commonHeightValue);
463 }
464
465 rule->setProperties(properties);
466
467 return rule.release();
468 }
469
470 PassRefPtr<StyleRuleBase> HTMLMetaElement::parseHandheldFriendlyContent(const St ring& contentValue)
471 {
472 RefPtr<StyleRuleViewport> rule = StyleRuleViewport::create();
473 RefPtr<MutableStylePropertySet> properties = MutableStylePropertySet::create (CSSStrictMode);
474 properties->setProperty(CSSPropertyInternalPriority, cssValuePool().createVa lue(2, CSSPrimitiveValue::CSS_NUMBER));
475
476 if (equalIgnoringCase(contentValue, "true")) {
477 properties->setProperty(CSSPropertyMinWidth, cssValuePool().createValue( 100, CSSPrimitiveValue::CSS_PERCENTAGE));
478 properties->setProperty(CSSPropertyMaxWidth, cssValuePool().createValue( 100, CSSPrimitiveValue::CSS_PERCENTAGE));
479 }
480
481 rule->setProperties(properties);
482 return rule.release();
483 }
484
485 PassRefPtr<StyleRuleBase> HTMLMetaElement::parseMobileOptimizedContent(const Str ing& contentValue)
486 {
487 RefPtr<StyleRuleViewport> rule = StyleRuleViewport::create();
488 RefPtr<MutableStylePropertySet> properties = MutableStylePropertySet::create (CSSStrictMode);
489 properties->setProperty(CSSPropertyInternalPriority, cssValuePool().createVa lue(3, CSSPrimitiveValue::CSS_NUMBER));
490
491 float value = parsePositiveNumber(String(), contentValue, float(0), float(10 000));
492 RefPtr<CSSValue> widthValue;
493 if (value > 0)
494 widthValue = cssValuePool().createValue(value, CSSPrimitiveValue::CSS_PX );
495 else
496 widthValue = cssValuePool().createValue(100, CSSPrimitiveValue::CSS_PERC ENTAGE);
497
498 properties->setProperty(CSSPropertyMinWidth, CSSValueAuto);
499 properties->setProperty(CSSPropertyMaxWidth, widthValue);
500
501 rule->setProperties(properties);
502 return rule.release();
503 }
504
505 void HTMLMetaElement::parseAttribute(const QualifiedName& name, const AtomicStri ng& value)
506 {
507 if (name == http_equivAttr || name == contentAttr) {
508 process();
509 } else if (name == nameAttr) {
510 // Do nothing.
511 } else {
512 HTMLElement::parseAttribute(name, value);
513 }
514 }
515
516 void HTMLMetaElement::process()
517 {
518 // We always need to clear rules as the meta tag could have been modified.
519 removeStyleSheet();
520
521 if (!inDocument())
522 return;
523
524 // All below situations requires a content attribute (which can be the empty string).
525 const AtomicString& contentValue = fastGetAttribute(contentAttr);
526 if (contentValue.isNull())
527 return;
528
529 const AtomicString& nameValue = fastGetAttribute(nameAttr);
530
531 if (nameValue.isNull()) {
532 // Get the document to process the tag, but only if we're actually part of DOM tree (changing a meta tag while
533 // it's not in the tree shouldn't have any effect on the document)
534 const AtomicString& httpEquivValue = fastGetAttribute(http_equivAttr);
535 if (!httpEquivValue.isNull())
536 document()->processHttpEquiv(httpEquivValue, contentValue);
537 return;
538 }
539
540 if (equalIgnoringCase(nameValue, "viewport")) {
541 addStyleSheetForRule(parseViewportContent(contentValue));
542 } else if (equalIgnoringCase(name(), "handheldfriendly")) {
543 addStyleSheetForRule(parseHandheldFriendlyContent(contentValue));
544 } else if (equalIgnoringCase(name(), "mobileoptimized")) {
545 addStyleSheetForRule(parseMobileOptimizedContent(contentValue));
546 } else if (equalIgnoringCase(name(), "referrer")) {
547 document()->processReferrerPolicy(contentValue);
548 }
549 }
550
551 void HTMLMetaElement::addStyleSheetForRule(PassRefPtr<StyleRuleBase> rule)
552 {
553 ASSERT(inDocument());
554 ASSERT(!m_sheet);
555
556 document()->styleSheetCollection()->addStyleSheetCandidateNode(this, false);
557
558 document()->styleSheetCollection()->addPendingSheet();
559
560 // As the stylesheet has no source URL, the position makes no difference.
561 TextPosition startPosition = TextPosition::minimumPosition();
562
563 m_sheet = CSSStyleSheet::createInline(this, KURL(), startPosition, document( )->inputEncoding());
564
565 m_sheet->setMediaQueries(MediaQuerySet::create("screen"));
566 m_sheet->setTitle(title());
567
568 m_sheet->contents()->parserAppendRule(rule);
569
570 document()->styleSheetCollection()->removePendingSheet(m_sheet->ownerNode()) ;
571 }
572
573 void HTMLMetaElement::removeStyleSheet()
574 {
575 if (m_sheet) {
576 document()->styleSheetCollection()->removeStyleSheetCandidateNode(this);
577 m_sheet.release()->clearOwnerNode();
578 }
579
580 // No need to resolve style during teardown.
581 if (document()->renderer())
582 document()->styleResolverChanged(DeferRecalcStyle);
583 }
584
585 String HTMLMetaElement::content() const
586 {
587 return getAttribute(contentAttr);
588 }
589
590 String HTMLMetaElement::httpEquiv() const
591 {
592 return getAttribute(http_equivAttr);
593 }
594
595 String HTMLMetaElement::name() const
596 {
597 return getNameAttribute();
598 }
599
600 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698