Viewport meta tag - content - bug 436083 r=jst,sr=sicking
authorBobby Holley <bholley@mozilla.com>
Wed, 16 Jul 2008 20:16:23 -0700
changeset 15988 ad66fd14f4852e369a0d97ecd094ace8e92fb25c
parent 15987 cd504b4a2257524f68f1b96c1034735d44785916
child 15989 ec061b745244bcb8c6bc47de241c053fd2030bf3
push idunknown
push userunknown
push dateunknown
reviewersjst, sicking
bugs436083
milestone1.9.1a1pre
Viewport meta tag - content - bug 436083 r=jst,sr=sicking
content/base/public/nsContentUtils.h
content/base/src/nsContentSink.h
content/base/src/nsContentUtils.cpp
content/base/src/nsGkAtomList.h
content/html/document/src/nsHTMLContentSink.cpp
content/xml/document/src/nsXMLContentSink.cpp
content/xml/document/src/nsXMLContentSink.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1301,16 +1301,25 @@ public:
     --sRemovableScriptBlockerCount;
     RemoveScriptBlocker();
   }
   static PRUint32 GetRemovableScriptBlockerLevel()
   {
     return sRemovableScriptBlockerCount;
   }
 
+  /* Process viewport META data. This gives us information for the scale
+   * and zoom of a page on mobile devices. We stick the information in
+   * the document header and use it later on after rendering.
+   *
+   * See Bug #436083
+   */
+  static nsresult ProcessViewportInfo(nsIDocument *aDocument,
+                                      const nsAString &viewportInfo);
+
 private:
 
   static PRBool InitializeEventTable();
 
   static nsresult doReparentContentWrapper(nsIContent *aChild,
                                            JSContext *cx,
                                            JSObject *aOldGlobal,
                                            JSObject *aNewGlobal,
--- a/content/base/src/nsContentSink.h
+++ b/content/base/src/nsContentSink.h
@@ -122,17 +122,17 @@ class nsContentSink : public nsICSSLoade
 
     // nsITimerCallback
   NS_DECL_NSITIMERCALLBACK
 
   // nsICSSLoaderObserver
   NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aWasAlternate,
                               nsresult aStatus);
 
-  nsresult ProcessMETATag(nsIContent* aContent);
+  virtual nsresult ProcessMETATag(nsIContent* aContent);
 
   // nsIContentSink implementation helpers
   NS_HIDDEN_(nsresult) WillInterruptImpl(void);
   NS_HIDDEN_(nsresult) WillResumeImpl(void);
   NS_HIDDEN_(nsresult) DidProcessATokenImpl(void);
   NS_HIDDEN_(void) WillBuildModelImpl(void);
   NS_HIDDEN_(void) DidBuildModelImpl(void);
   NS_HIDDEN_(void) DropParserAndPerfHint(void);
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -4215,16 +4215,104 @@ nsContentUtils::AddScriptRunner(nsIRunna
   }
   
   nsCOMPtr<nsIRunnable> run = aRunnable;
   run->Run();
 
   return PR_TRUE;
 }
 
+/* 
+ * Helper function for nsContentUtils::ProcessViewportInfo.
+ *
+ * Handles a single key=value pair. If it corresponds to a valid viewport
+ * attribute, add it to the document header data. No validation is done on the
+ * value itself (this is done at display time).
+ */
+static void ProcessViewportToken(nsIDocument *aDocument, 
+                                 const nsAString &token) {
+
+  /* Iterators. */
+  nsAString::const_iterator tip, tail, end;
+  token.BeginReading(tip);
+  tail = tip;
+  token.EndReading(end);
+
+  /* Move tip to the '='. */
+  while ((tip != end) && (*tip != '='))
+    ++tip;
+
+  /* If we didn't find an '=', punt. */
+  if (tip == end)
+    return;
+
+  /* Extract the key and value. */
+  const nsAString &key = Substring(tail, tip);
+  const nsAString &value = Substring(++tip, end);
+
+  /* Check for known keys. If we find a match, insert the appropriate
+   * information into the document header. */
+  nsCOMPtr<nsIAtom> key_atom = do_GetAtom(key);
+  if (key_atom == nsGkAtoms::height)
+    aDocument->SetHeaderData(nsGkAtoms::viewport_height, value);
+  else if (key_atom == nsGkAtoms::width)
+    aDocument->SetHeaderData(nsGkAtoms::viewport_width, value);
+  else if (key_atom == nsGkAtoms::initial_scale)
+    aDocument->SetHeaderData(nsGkAtoms::viewport_initial_scale, value);
+  else if (key_atom == nsGkAtoms::minimum_scale)
+    aDocument->SetHeaderData(nsGkAtoms::viewport_minimum_scale, value);
+  else if (key_atom == nsGkAtoms::maximum_scale)
+    aDocument->SetHeaderData(nsGkAtoms::viewport_maximum_scale, value);
+  else if (key_atom == nsGkAtoms::user_scalable)
+    aDocument->SetHeaderData(nsGkAtoms::viewport_user_scalable, value);
+}
+
+#define IS_SEPARATOR(c) ((c == ' ') || (c == ',') || (c == ';'))
+/* static */
+nsresult
+nsContentUtils::ProcessViewportInfo(nsIDocument *aDocument,
+                                    const nsAString &viewportInfo) {
+
+  /* We never fail. */
+  nsresult rv = NS_OK;
+
+  /* Iterators. */
+  nsAString::const_iterator tip, tail, end;
+  viewportInfo.BeginReading(tip);
+  tail = tip;
+  viewportInfo.EndReading(end);
+
+  /* Read the tip to the first non-separator character. */
+  while ((tip != end) && IS_SEPARATOR(*tip))
+    ++tip;
+
+  /* Read through and find tokens seperated by separators. */
+  while (tip != end) {
+    
+    /* Synchronize tip and tail. */
+    tail = tip;
+
+    /* Advance tip past non-separator characters. */
+    while ((tip != end) && !IS_SEPARATOR(*tip))
+      ++tip;
+
+    /* Our token consists of the characters between tail and tip. */
+    ProcessViewportToken(aDocument, Substring(tail, tip));
+
+    /* Skip separators. */
+    while ((tip != end) && IS_SEPARATOR(*tip))
+      ++tip;
+  }
+
+  return rv;
+
+}
+
+#undef IS_SEPARATOR
+
 /* static */
 void
 nsContentUtils::HidePopupsInDocument(nsIDocument* aDocument)
 {
   NS_PRECONDITION(aDocument, "Null document");
 
 #ifdef MOZ_XUL
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -424,16 +424,17 @@ GK_ATOM(includes, "includes")
 GK_ATOM(increment, "increment")
 GK_ATOM(indent, "indent")
 GK_ATOM(index, "index")
 GK_ATOM(infer, "infer")
 GK_ATOM(infinity, "infinity")
 GK_ATOM(inherit, "inherit")
 GK_ATOM(inherits, "inherits")
 GK_ATOM(inheritstyle, "inheritstyle")
+GK_ATOM(initial_scale, "initial-scale")
 GK_ATOM(input, "input")
 GK_ATOM(ins, "ins")
 GK_ATOM(insertafter, "insertafter")
 GK_ATOM(insertbefore, "insertbefore")
 GK_ATOM(instanceOf, "instanceOf")
 GK_ATOM(int32, "int32")
 GK_ATOM(int64, "int64")
 GK_ATOM(integer, "integer")
@@ -509,16 +510,17 @@ GK_ATOM(menuitem, "menuitem")
 GK_ATOM(menulist, "menulist")
 GK_ATOM(menupopup, "menupopup")
 GK_ATOM(message, "message")
 GK_ATOM(meta, "meta")
 GK_ATOM(method, "method")
 GK_ATOM(middle, "middle")
 GK_ATOM(min, "min")
 GK_ATOM(minheight, "minheight")
+GK_ATOM(minimum_scale, "minimum-scale")
 GK_ATOM(minpos, "minpos")
 GK_ATOM(minusSign, "minus-sign")
 GK_ATOM(minwidth, "minwidth")
 GK_ATOM(mod, "mod")
 GK_ATOM(mode, "mode")
 GK_ATOM(modifiers, "modifiers")
 GK_ATOM(mousedown, "mousedown")
 GK_ATOM(mousemove, "mousemove")
@@ -908,16 +910,17 @@ GK_ATOM(underflow, "underflow")
 GK_ATOM(undetermined, "undetermined")
 GK_ATOM(unload, "unload")
 GK_ATOM(unparsedEntityUri, "unparsed-entity-uri")
 GK_ATOM(upperFirst, "upper-first")
 GK_ATOM(uri, "uri")
 GK_ATOM(use, "use")
 GK_ATOM(useAttributeSets, "use-attribute-sets")
 GK_ATOM(usemap, "usemap")
+GK_ATOM(user_scalable, "user-scalable")
 GK_ATOM(userInput, "userInput")
 GK_ATOM(validate, "validate")
 GK_ATOM(valign, "valign")
 GK_ATOM(value, "value")
 GK_ATOM(values, "values")
 GK_ATOM(valueOf, "value-of")
 GK_ATOM(valuetype, "valuetype")
 GK_ATOM(var, "var")
@@ -929,16 +932,23 @@ GK_ATOM(vendorUrl, "vendor-url")
 GK_ATOM(version, "version")
 GK_ATOM(vert, "vert")
 GK_ATOM(vertical, "vertical")
 #ifdef MOZ_MEDIA
 GK_ATOM(audio, "audio")
 GK_ATOM(video, "video")
 GK_ATOM(videocontrols, "videocontrols")
 #endif
+GK_ATOM(viewport, "viewport")
+GK_ATOM(viewport_height, "viewport-height")
+GK_ATOM(viewport_initial_scale, "viewport-initial-scale")
+GK_ATOM(viewport_maximum_scale, "viewport-maximum-scale")
+GK_ATOM(viewport_minimum_scale, "viewport-minimum-scale")
+GK_ATOM(viewport_user_scalable, "viewport-user-scalable")
+GK_ATOM(viewport_width, "viewport-width")
 GK_ATOM(visibility, "visibility")
 GK_ATOM(vlink, "vlink")
 GK_ATOM(vspace, "vspace")
 GK_ATOM(wbr, "wbr")
 GK_ATOM(when, "when")
 GK_ATOM(where, "where")
 GK_ATOM(widget, "widget")
 GK_ATOM(width, "width")
@@ -1300,16 +1310,17 @@ GK_ATOM(lt_, "lt")
 GK_ATOM(maction_, "maction")
 GK_ATOM(maligngroup_, "maligngroup")
 GK_ATOM(malignmark_, "malignmark")
 GK_ATOM(mathbackground_, "mathbackground")
 GK_ATOM(mathcolor_, "mathcolor")
 GK_ATOM(mathsize_, "mathsize")
 GK_ATOM(mathvariant_, "mathvariant")
 GK_ATOM(matrixrow_, "matrixrow")
+GK_ATOM(maximum_scale, "maximum-scale")
 GK_ATOM(maxsize_, "maxsize")
 GK_ATOM(mean_, "mean")
 GK_ATOM(median_, "median")
 GK_ATOM(mediummathspace_, "mediummathspace")
 GK_ATOM(menclose_, "menclose")
 GK_ATOM(merror_, "merror")
 GK_ATOM(mfenced_, "mfenced")
 GK_ATOM(mfrac_, "mfrac")
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -205,16 +205,18 @@ public:
   NS_IMETHOD DidProcessAToken(void);
   NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode);
   NS_IMETHOD BeginContext(PRInt32 aID);
   NS_IMETHOD EndContext(PRInt32 aID);
   NS_IMETHOD OpenHead();
   NS_IMETHOD IsEnabled(PRInt32 aTag, PRBool* aReturn);
   NS_IMETHOD_(PRBool) IsFormOnStack();
 
+  virtual nsresult ProcessMETATag(nsIContent* aContent);
+
 #ifdef DEBUG
   // nsIDebugDumpContent
   NS_IMETHOD DumpContentModel();
 #endif
 
 protected:
   nsresult UpdateDocumentTitle();
   // If aCheckIfPresent is true, will only set an attribute in cases
@@ -2994,16 +2996,43 @@ HTMLContentSink::ProcessLINKTag(const ns
         }
       }
     }
   }
 
   return result;
 }
 
+/* 
+ * Extends nsContentSink::ProcessMETATag to grab the 'viewport' meta tag. This
+ * information is ignored by the generic content sink because it only stores
+ * http-equiv meta tags.
+ *
+ * Initially implemented for bug #436083
+ */
+nsresult
+HTMLContentSink::ProcessMETATag(nsIContent *aContent) {
+
+  /* Call the superclass method. */
+  nsContentSink::ProcessMETATag(aContent);
+
+  nsresult rv = NS_OK;
+
+  /* Look for the viewport meta tag. If we find it, process it and put the
+   * data into the document header. */
+  if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
+                            nsGkAtoms::viewport, eIgnoreCase)) {
+    nsAutoString value;
+    aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::content, value);
+    rv = nsContentUtils::ProcessViewportInfo(mDocument, value);
+  }
+
+  return rv;
+}
+
 #ifdef DEBUG
 void
 HTMLContentSink::ForceReflow()
 {
   mCurrentContext->FlushTags();
 }
 #endif
 
--- a/content/xml/document/src/nsXMLContentSink.cpp
+++ b/content/xml/document/src/nsXMLContentSink.cpp
@@ -1390,16 +1390,43 @@ nsXMLContentSink::ParsePIData(const nsSt
   nsParserUtils::GetQuotedAttributeValue(aData, nsGkAtoms::media, aMedia);
 
   nsAutoString alternate;
   nsParserUtils::GetQuotedAttributeValue(aData, nsGkAtoms::alternate, alternate);
 
   aIsAlternate = alternate.EqualsLiteral("yes");
 }
 
+/*
+ * Extends nsContentSink::ProcessMETATag to grab the 'viewport' meta tag. This
+ * information is ignored by the generic content sink because it only stores
+ * http-equiv meta tags. We need it in the XMLContentSink for XHTML documents.
+ *
+ * Initially implemented for bug #436083
+ */
+nsresult
+nsXMLContentSink::ProcessMETATag(nsIContent *aContent) {
+
+  /* Call the superclass method. */
+  nsContentSink::ProcessMETATag(aContent);
+
+  nsresult rv = NS_OK;
+
+  /* Look for the viewport meta tag. If we find it, process it and put the
+   * data into the document header. */
+  if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
+                            nsGkAtoms::viewport, eIgnoreCase)) {
+    nsAutoString value;
+    aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::content, value);
+    rv = nsContentUtils::ProcessViewportInfo(mDocument, value);
+  }
+
+  return rv;
+}
+
 NS_IMETHODIMP
 nsXMLContentSink::HandleXMLDeclaration(const PRUnichar *aVersion,
                                        const PRUnichar *aEncoding,
                                        PRInt32 aStandalone)
 {
   mDocument->SetXMLDeclaration(aVersion, aEncoding, aStandalone);
 
   return DidProcessATokenImpl();
--- a/content/xml/document/src/nsXMLContentSink.h
+++ b/content/xml/document/src/nsXMLContentSink.h
@@ -106,16 +106,18 @@ public:
 
   // nsICSSLoaderObserver
   NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aWasAlternate,
                               nsresult aStatus);
   static void ParsePIData(const nsString &aData, nsString &aHref,
                           nsString &aTitle, nsString &aMedia,
                           PRBool &aIsAlternate);
 
+  virtual nsresult ProcessMETATag(nsIContent* aContent);
+
 protected:
   // Start layout.  If aIgnorePendingSheets is true, this will happen even if
   // we still have stylesheet loads pending.  Otherwise, we'll wait until the
   // stylesheets are all done loading.
   virtual void MaybeStartLayout(PRBool aIgnorePendingSheets);
 
   virtual nsresult AddAttributes(const PRUnichar** aNode, nsIContent* aContent);
   nsresult AddText(const PRUnichar* aString, PRInt32 aLength);