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

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

Powered by Google App Engine
This is Rietveld 408576698