[BlackBerry] Add RSS content handling support
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Aug 2012 00:14:41 +0000 (00:14 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Aug 2012 00:14:41 +0000 (00:14 +0000)
https://bugs.webkit.org/show_bug.cgi?id=93496

Patch by Lianghui Chen <liachen@rim.com> on 2012-08-21
Reviewed by Rob Buis.

Add code to filter RSS content, and properly convert them to HTML
content so they can display nicely, instead of as plain text.

Following modules are included:

RSSFilterStream: the code for detecting RSS content, and controlling
the handling of these content.

RSSParserBase: the base class for the following 3 RSS parser.
RSS10Parser: the code for decoding RSS 1.0 content.
RSS20Parser: the code for decoding RSS 2.0 content.
RSSAtomParser: the code for decoding Atom format RSS content.
RSSGenerator: the code for generating HTML content based on RSS feed.

No new tests as it's not changing how HTML content is handled.

* PlatformBlackBerry.cmake:
* platform/network/blackberry/NetworkJob.cpp:
(WebCore::NetworkJob::initialize):
* platform/network/blackberry/rss/RSS10Parser.cpp: Added.
(WebCore):
(WebCore::RSS10Parser::RSS10Parser):
(WebCore::RSS10Parser::parseBuffer):
(WebCore::RSS10Parser::parseXmlDoc):
(WebCore::RSS10Parser::parseItemBaseAttribute):
(WebCore::RSS10Parser::parseItem):
(WebCore::RSS10Parser::parseFeed):
* platform/network/blackberry/rss/RSS10Parser.h: Added.
(WebCore):
(RSS10Parser):
* platform/network/blackberry/rss/RSS20Parser.cpp: Added.
(WebCore):
(WebCore::RSS20Parser::RSS20Parser):
(WebCore::RSS20Parser::parseBuffer):
(WebCore::RSS20Parser::parseXmlDoc):
(WebCore::RSS20Parser::parseItemBaseAttribute):
(WebCore::RSS20Parser::parseItem):
(WebCore::RSS20Parser::parseFeed):
(WebCore::RSS20Parser::parseEnclosure):
* platform/network/blackberry/rss/RSS20Parser.h: Added.
(WebCore):
(RSS20Parser):
* platform/network/blackberry/rss/RSSAtomParser.cpp: Added.
(WebCore):
(WebCore::isRelativePath):
(WebCore::RSSAtomLink::relType):
(WebCore::RSSAtomParser::RSSAtomParser):
(WebCore::RSSAtomParser::parseBuffer):
(WebCore::RSSAtomParser::parseXmlDoc):
(WebCore::RSSAtomParser::parseItemBaseAttribute):
(WebCore::RSSAtomParser::parseItem):
(WebCore::RSSAtomParser::parseFeed):
(WebCore::RSSAtomParser::parseLink):
(WebCore::RSSAtomParser::enclosureFromLink):
(WebCore::RSSAtomParser::parseContent):
(WebCore::RSSAtomParser::parseAuthor):
(WebCore::RSSAtomParser::parseCategory):
* platform/network/blackberry/rss/RSSAtomParser.h: Added.
(WebCore):
(RSSAtomLink):
(WebCore::RSSAtomLink::RSSAtomLink):
(RSSAtomParser):
* platform/network/blackberry/rss/RSSFilterStream.cpp: Added.
(WebCore):
(WebCore::isASCIISpaceLowerByte):
(WebCore::stripWhiteSpace):
(WebCore::equalIgnoringCase):
(WebCore::isAtomMIMEType):
(WebCore::isRSSMIMEType):
(WebCore::isPotentialRSSMIMEType):
(WebCore::isRSSContent):
(WebCore::RSSTypeFromContentType):
(WebCore::RSSTypeFromContent):
(WebCore::createParser):
(WebCore::findXMLEncodingPosition):
(WebCore::findXMLLanguagePosition):
(WebCore::defaultEncodingForLanguage):
(WebCore::isTranscodingNeeded):
(WebCore::transcode):
(WebCore::transcodeContent):
(WebCore::RSSFilterStream::RSSFilterStream):
(WebCore::RSSFilterStream::notifyStatusReceived):
(WebCore::RSSFilterStream::notifyHeadersReceived):
(WebCore::RSSFilterStream::notifyDataReceived):
(WebCore::RSSFilterStream::notifyClose):
(WebCore::RSSFilterStream::convertContentToHtml):
(WebCore::RSSFilterStream::handleRSSContent):
(WebCore::RSSFilterStream::charset):
(WebCore::RSSFilterStream::encoding):
(WebCore::RSSFilterStream::saveHeaders):
(WebCore::RSSFilterStream::removeHeader):
(WebCore::RSSFilterStream::updateHeader):
(WebCore::RSSFilterStream::updateRSSHeaders):
(WebCore::RSSFilterStream::sendSavedHeaders):
(WebCore::RSSFilterStream::appendData):
* platform/network/blackberry/rss/RSSFilterStream.h: Added.
(WebCore):
(RSSFilterStream):
* platform/network/blackberry/rss/RSSGenerator.cpp: Added.
(WebCore):
(WebCore::RSSGenerator::RSSGenerator):
(WebCore::RSSGenerator::~RSSGenerator):
(WebCore::RSSGenerator::generateHtml):
* platform/network/blackberry/rss/RSSGenerator.h: Added.
(WebCore):
(RSSGenerator):
* platform/network/blackberry/rss/RSSParserBase.cpp: Added.
(WebCore):
(WebCore::RSSEnclosure::RSSEnclosure):
(WebCore::RSSEnclosure::typeInEnum):
(WebCore::RSSEnclosure::suggestedName):
(WebCore::RSSFeed::RSSFeed):
(WebCore::RSSFeed::~RSSFeed):
(WebCore::RSSFeed::clear):
(WebCore::RSSItem::RSSItem):
(WebCore::RSSItem::~RSSItem):
(WebCore::RSSItem::clear):
(WebCore::RSSParserBase::RSSParserBase):
(WebCore::RSSParserBase::~RSSParserBase):
(WebCore::textFromXMLAttr):
(WebCore::textFromXMLNode):
* platform/network/blackberry/rss/RSSParserBase.h: Added.
(WebCore):
(RSSEnclosure):
(RSSItemBase):
(RSSParserBase):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@126230 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
Source/WebCore/ChangeLog
Source/WebCore/PlatformBlackBerry.cmake
Source/WebCore/platform/network/blackberry/NetworkJob.cpp
Source/WebCore/platform/network/blackberry/rss/RSS10Parser.cpp [new file with mode: 0644]
Source/WebCore/platform/network/blackberry/rss/RSS10Parser.h [new file with mode: 0644]
Source/WebCore/platform/network/blackberry/rss/RSS20Parser.cpp [new file with mode: 0644]
Source/WebCore/platform/network/blackberry/rss/RSS20Parser.h [new file with mode: 0644]
Source/WebCore/platform/network/blackberry/rss/RSSAtomParser.cpp [new file with mode: 0644]
Source/WebCore/platform/network/blackberry/rss/RSSAtomParser.h [new file with mode: 0644]
Source/WebCore/platform/network/blackberry/rss/RSSFilterStream.cpp [new file with mode: 0644]
Source/WebCore/platform/network/blackberry/rss/RSSFilterStream.h [new file with mode: 0644]
Source/WebCore/platform/network/blackberry/rss/RSSGenerator.cpp [new file with mode: 0644]
Source/WebCore/platform/network/blackberry/rss/RSSGenerator.h [new file with mode: 0644]
Source/WebCore/platform/network/blackberry/rss/RSSParserBase.cpp [new file with mode: 0644]
Source/WebCore/platform/network/blackberry/rss/RSSParserBase.h [new file with mode: 0644]

index 123f324..e12dd5e 100644 (file)
@@ -1,3 +1,137 @@
+2012-08-21  Lianghui Chen  <liachen@rim.com>
+
+        [BlackBerry] Add RSS content handling support
+        https://bugs.webkit.org/show_bug.cgi?id=93496
+
+        Reviewed by Rob Buis.
+
+        Add code to filter RSS content, and properly convert them to HTML
+        content so they can display nicely, instead of as plain text.
+
+        Following modules are included:
+
+        RSSFilterStream: the code for detecting RSS content, and controlling
+        the handling of these content.
+
+        RSSParserBase: the base class for the following 3 RSS parser.
+        RSS10Parser: the code for decoding RSS 1.0 content.
+        RSS20Parser: the code for decoding RSS 2.0 content.
+        RSSAtomParser: the code for decoding Atom format RSS content.
+        RSSGenerator: the code for generating HTML content based on RSS feed.
+
+        No new tests as it's not changing how HTML content is handled.
+
+        * PlatformBlackBerry.cmake:
+        * platform/network/blackberry/NetworkJob.cpp:
+        (WebCore::NetworkJob::initialize):
+        * platform/network/blackberry/rss/RSS10Parser.cpp: Added.
+        (WebCore):
+        (WebCore::RSS10Parser::RSS10Parser):
+        (WebCore::RSS10Parser::parseBuffer):
+        (WebCore::RSS10Parser::parseXmlDoc):
+        (WebCore::RSS10Parser::parseItemBaseAttribute):
+        (WebCore::RSS10Parser::parseItem):
+        (WebCore::RSS10Parser::parseFeed):
+        * platform/network/blackberry/rss/RSS10Parser.h: Added.
+        (WebCore):
+        (RSS10Parser):
+        * platform/network/blackberry/rss/RSS20Parser.cpp: Added.
+        (WebCore):
+        (WebCore::RSS20Parser::RSS20Parser):
+        (WebCore::RSS20Parser::parseBuffer):
+        (WebCore::RSS20Parser::parseXmlDoc):
+        (WebCore::RSS20Parser::parseItemBaseAttribute):
+        (WebCore::RSS20Parser::parseItem):
+        (WebCore::RSS20Parser::parseFeed):
+        (WebCore::RSS20Parser::parseEnclosure):
+        * platform/network/blackberry/rss/RSS20Parser.h: Added.
+        (WebCore):
+        (RSS20Parser):
+        * platform/network/blackberry/rss/RSSAtomParser.cpp: Added.
+        (WebCore):
+        (WebCore::isRelativePath):
+        (WebCore::RSSAtomLink::relType):
+        (WebCore::RSSAtomParser::RSSAtomParser):
+        (WebCore::RSSAtomParser::parseBuffer):
+        (WebCore::RSSAtomParser::parseXmlDoc):
+        (WebCore::RSSAtomParser::parseItemBaseAttribute):
+        (WebCore::RSSAtomParser::parseItem):
+        (WebCore::RSSAtomParser::parseFeed):
+        (WebCore::RSSAtomParser::parseLink):
+        (WebCore::RSSAtomParser::enclosureFromLink):
+        (WebCore::RSSAtomParser::parseContent):
+        (WebCore::RSSAtomParser::parseAuthor):
+        (WebCore::RSSAtomParser::parseCategory):
+        * platform/network/blackberry/rss/RSSAtomParser.h: Added.
+        (WebCore):
+        (RSSAtomLink):
+        (WebCore::RSSAtomLink::RSSAtomLink):
+        (RSSAtomParser):
+        * platform/network/blackberry/rss/RSSFilterStream.cpp: Added.
+        (WebCore):
+        (WebCore::isASCIISpaceLowerByte):
+        (WebCore::stripWhiteSpace):
+        (WebCore::equalIgnoringCase):
+        (WebCore::isAtomMIMEType):
+        (WebCore::isRSSMIMEType):
+        (WebCore::isPotentialRSSMIMEType):
+        (WebCore::isRSSContent):
+        (WebCore::RSSTypeFromContentType):
+        (WebCore::RSSTypeFromContent):
+        (WebCore::createParser):
+        (WebCore::findXMLEncodingPosition):
+        (WebCore::findXMLLanguagePosition):
+        (WebCore::defaultEncodingForLanguage):
+        (WebCore::isTranscodingNeeded):
+        (WebCore::transcode):
+        (WebCore::transcodeContent):
+        (WebCore::RSSFilterStream::RSSFilterStream):
+        (WebCore::RSSFilterStream::notifyStatusReceived):
+        (WebCore::RSSFilterStream::notifyHeadersReceived):
+        (WebCore::RSSFilterStream::notifyDataReceived):
+        (WebCore::RSSFilterStream::notifyClose):
+        (WebCore::RSSFilterStream::convertContentToHtml):
+        (WebCore::RSSFilterStream::handleRSSContent):
+        (WebCore::RSSFilterStream::charset):
+        (WebCore::RSSFilterStream::encoding):
+        (WebCore::RSSFilterStream::saveHeaders):
+        (WebCore::RSSFilterStream::removeHeader):
+        (WebCore::RSSFilterStream::updateHeader):
+        (WebCore::RSSFilterStream::updateRSSHeaders):
+        (WebCore::RSSFilterStream::sendSavedHeaders):
+        (WebCore::RSSFilterStream::appendData):
+        * platform/network/blackberry/rss/RSSFilterStream.h: Added.
+        (WebCore):
+        (RSSFilterStream):
+        * platform/network/blackberry/rss/RSSGenerator.cpp: Added.
+        (WebCore):
+        (WebCore::RSSGenerator::RSSGenerator):
+        (WebCore::RSSGenerator::~RSSGenerator):
+        (WebCore::RSSGenerator::generateHtml):
+        * platform/network/blackberry/rss/RSSGenerator.h: Added.
+        (WebCore):
+        (RSSGenerator):
+        * platform/network/blackberry/rss/RSSParserBase.cpp: Added.
+        (WebCore):
+        (WebCore::RSSEnclosure::RSSEnclosure):
+        (WebCore::RSSEnclosure::typeInEnum):
+        (WebCore::RSSEnclosure::suggestedName):
+        (WebCore::RSSFeed::RSSFeed):
+        (WebCore::RSSFeed::~RSSFeed):
+        (WebCore::RSSFeed::clear):
+        (WebCore::RSSItem::RSSItem):
+        (WebCore::RSSItem::~RSSItem):
+        (WebCore::RSSItem::clear):
+        (WebCore::RSSParserBase::RSSParserBase):
+        (WebCore::RSSParserBase::~RSSParserBase):
+        (WebCore::textFromXMLAttr):
+        (WebCore::textFromXMLNode):
+        * platform/network/blackberry/rss/RSSParserBase.h: Added.
+        (WebCore):
+        (RSSEnclosure):
+        (RSSItemBase):
+        (RSSParserBase):
+
 2012-08-21  Andrew Lo  <anlo@rim.com>
 
         [BlackBerry] requestAnimationFrame: Unscheduled display link frames need to be sent to main thread
index f45e501..e4c97f8 100644 (file)
@@ -6,13 +6,14 @@ LIST(INSERT WebCore_INCLUDE_DIRECTORIES 0
 LIST(APPEND WebCore_INCLUDE_DIRECTORIES
     "${WEBCORE_DIR}/bindings/cpp"
     "${WEBCORE_DIR}/platform/blackberry/CookieDatabaseBackingStore"
-    "${WEBCORE_DIR}/platform/network/blackberry"
     "${WEBCORE_DIR}/platform/graphics/blackberry/skia"
     "${WEBCORE_DIR}/platform/graphics/harfbuzz"
     "${WEBCORE_DIR}/platform/graphics/opentype/"
     "${WEBCORE_DIR}/platform/graphics/skia"
     "${WEBCORE_DIR}/platform/image-decoders/skia"
     "${WEBCORE_DIR}/platform/image-encoders/skia"
+    "${WEBCORE_DIR}/platform/network/blackberry"
+    "${WEBCORE_DIR}/platform/network/blackberry/rss"
 )
 
 # Skia sources
@@ -102,6 +103,12 @@ LIST(APPEND WebCore_SOURCES
     platform/network/blackberry/ResourceRequestBlackBerry.cpp
     platform/network/blackberry/ResourceResponseBlackBerry.cpp
     platform/network/blackberry/SocketStreamHandleBlackBerry.cpp
+    platform/network/blackberry/rss/RSSAtomParser.cpp
+    platform/network/blackberry/rss/RSS10Parser.cpp
+    platform/network/blackberry/rss/RSS20Parser.cpp
+    platform/network/blackberry/rss/RSSFilterStream.cpp
+    platform/network/blackberry/rss/RSSGenerator.cpp
+    platform/network/blackberry/rss/RSSParserBase.cpp
 )
 
 LIST(APPEND WebCore_USER_AGENT_STYLE_SHEETS
index 3d803a2..45ae8af 100644 (file)
@@ -31,6 +31,7 @@
 #include "MIMETypeRegistry.h"
 #include "NetworkManager.h"
 #include "Page.h"
+#include "RSSFilterStream.h"
 #include "ResourceHandleClient.h"
 #include "ResourceHandleInternal.h"
 #include "ResourceRequest.h"
@@ -133,6 +134,16 @@ bool NetworkJob::initialize(int playerId,
     BlackBerry::Platform::FilterStream* wrappedStream = m_streamFactory->createNetworkStream(request, m_playerId);
     if (!wrappedStream)
         return false;
+
+    BlackBerry::Platform::NetworkRequest::TargetType targetType = request.getTargetType();
+    if ((targetType == BlackBerry::Platform::NetworkRequest::TargetIsMainFrame
+         || targetType == BlackBerry::Platform::NetworkRequest::TargetIsSubframe)
+            && !m_isOverrideContentType) {
+        RSSFilterStream* filter = new RSSFilterStream();
+        filter->setWrappedStream(wrappedStream);
+        wrappedStream = filter;
+    }
+
     setWrappedStream(wrappedStream);
 
     return true;
diff --git a/Source/WebCore/platform/network/blackberry/rss/RSS10Parser.cpp b/Source/WebCore/platform/network/blackberry/rss/RSS10Parser.cpp
new file mode 100644 (file)
index 0000000..5392330
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+#include "RSS10Parser.h"
+
+#include "BlackBerryPlatformAssert.h"
+#include "libxml/parser.h"
+#include "libxml/xmlwriter.h"
+
+namespace WebCore {
+
+RSS10Parser::RSS10Parser()
+{
+}
+
+bool RSS10Parser::parseBuffer(const char* buffer, int length, const char* url, const char* encoding)
+{
+    return parseXmlDoc(xmlReadMemory(buffer, length, url, encoding, XML_PARSE_NOBLANKS | XML_PARSE_NONET));
+}
+
+bool RSS10Parser::parseXmlDoc(xmlDocPtr doc)
+{
+    if (!doc)
+        return false;
+
+    xmlNode* node = xmlDocGetRootElement(doc);
+    if (!node) {
+        xmlFreeDoc(doc);
+        return false;
+    }
+
+    for (; node; node = node->next) {
+        String name(reinterpret_cast<const char*>(node->name));
+
+        if (name == "RDF") {
+            xmlNode* childnode = node->children;
+            for (; childnode; childnode = childnode->next) {
+                if (childnode->type == XML_ELEMENT_NODE) {
+                    name = String(reinterpret_cast<const char*>(childnode->name));
+                    name.makeLower();
+                    if (name == "channel") {
+                        BLACKBERRY_ASSERT(!m_root);
+                        if (!m_root)
+                            m_root = parseFeed(childnode->children);
+                    } else if (name == "item") {
+                        BLACKBERRY_ASSERT(m_root);
+                        if (m_root) {
+                            RSSItem* item = parseItem(childnode->children);
+                            if (item)
+                                m_root->m_items.append(item);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    xmlFreeDoc(doc);
+
+    return m_root;
+}
+
+bool RSS10Parser::parseItemBaseAttribute(RSSItemBase* item, const String& name, xmlNode* node)
+{
+    if (name == "link")
+        item->m_link = textFromXMLNode(node);
+    else if (name == "title")
+        item->m_title = textFromXMLNode(node);
+    else if (name == "description")
+        item->m_description = textFromXMLNode(node);
+    else
+        return false;
+
+    return true;
+}
+
+RSSItem* RSS10Parser::parseItem(xmlNode* node)
+{
+    BLACKBERRY_ASSERT(node);
+
+    RSSItem* item = new RSSItem();
+
+    for (; node; node = node->next) {
+        String name(reinterpret_cast<const char*>(node->name));
+        name.makeLower();
+
+        parseItemBaseAttribute(item, name, node);
+    }
+
+    return item;
+}
+
+RSSFeed* RSS10Parser::parseFeed(xmlNode* node)
+{
+    BLACKBERRY_ASSERT(node);
+
+    RSSFeed* feed = new RSSFeed();
+
+    for (; node; node = node->next) {
+        String name(reinterpret_cast<const char*>(node->name));
+        name.makeLower();
+
+        parseItemBaseAttribute(feed, name, node);
+    }
+
+    return feed;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/network/blackberry/rss/RSS10Parser.h b/Source/WebCore/platform/network/blackberry/rss/RSS10Parser.h
new file mode 100644 (file)
index 0000000..396641d
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef RSS10Parser_h
+#define RSS10Parser_h
+
+#include "RSSParserBase.h"
+
+namespace WebCore {
+
+class RSS10Parser : public RSSParserBase {
+public:
+    RSS10Parser();
+
+    bool parseBuffer(const char* buffer, int length, const char* url, const char* encoding);
+
+private:
+    bool parseXmlDoc(xmlDocPtr);
+    bool parseItemBaseAttribute(RSSItemBase*, const String& name, xmlNode*);
+    RSSItem* parseItem(xmlNode*);
+    RSSFeed* parseFeed(xmlNode*);
+};
+
+} // namespace WebCore
+
+#endif // RSS10Parser_h
diff --git a/Source/WebCore/platform/network/blackberry/rss/RSS20Parser.cpp b/Source/WebCore/platform/network/blackberry/rss/RSS20Parser.cpp
new file mode 100644 (file)
index 0000000..778e4dc
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+#include "RSS20Parser.h"
+
+#include "BlackBerryPlatformAssert.h"
+#include "libxml/parser.h"
+#include "libxml/xmlwriter.h"
+
+namespace WebCore {
+
+RSS20Parser::RSS20Parser()
+{
+}
+
+bool RSS20Parser::parseBuffer(const char* buffer, int length, const char* url, const char* encoding)
+{
+    return parseXmlDoc(xmlReadMemory(buffer, length, url, encoding, XML_PARSE_NOBLANKS | XML_PARSE_NONET));
+}
+
+bool RSS20Parser::parseXmlDoc(xmlDocPtr doc)
+{
+    if (!doc)
+        return false;
+
+    xmlNode* node = xmlDocGetRootElement(doc);
+    if (!node) {
+        xmlFreeDoc(doc);
+        return false;
+    }
+
+    for (; node; node = node->next) {
+        String name(reinterpret_cast<const char*>(node->name));
+        name.makeLower();
+
+        if (name == "rss") {
+            xmlNode* channel = node->children;
+            if (channel->type == XML_ELEMENT_NODE) {
+                name = reinterpret_cast<const char*>(channel->name);
+                name.makeLower();
+                if (name == "channel")
+                    m_root = parseFeed(channel->children);
+            }
+            break;
+        }
+    }
+
+    xmlFreeDoc(doc);
+
+    return m_root;
+}
+
+bool RSS20Parser::parseItemBaseAttribute(RSSItemBase* item, const String& name, xmlNode* node)
+{
+    if (name == "link")
+        item->m_link = textFromXMLNode(node);
+    else if (name == "title")
+        item->m_title = textFromXMLNode(node);
+    else if (name == "description")
+        item->m_description = textFromXMLNode(node);
+    else if (name == "pubdate")
+        item->m_pubDate = textFromXMLNode(node);
+    else
+        return false;
+
+    return true;
+}
+
+RSSItem* RSS20Parser::parseItem(xmlNode* node)
+{
+    BLACKBERRY_ASSERT(node);
+
+    RSSItem* item = new RSSItem();
+
+    for (; node; node = node->next) {
+        String name(reinterpret_cast<const char*>(node->name));
+        name.makeLower();
+
+        if (parseItemBaseAttribute(item, name, node))
+            continue;
+
+        if (name == "author")
+            item->m_author = textFromXMLNode(node);
+        else if (name == "creator") {
+            if (item->m_author.isEmpty())
+                item->m_author = textFromXMLNode(node);
+        } else if (name == "category")
+            item->m_categories.append(textFromXMLNode(node));
+        else if (name == "comments")
+            item->m_comments = textFromXMLNode(node);
+        else if (name == "enclosure") {
+            // Right now we assume there is only one enclosure per item, and we handle only
+            // the first enclosure if there are multiple ones.
+            // Reference: http://www.rssboard.org/rss-profile#element-channel-item-enclosure
+            if (!item->m_enclosure)
+                item->m_enclosure = parseEnclosure(node);
+        }
+    }
+
+    return item;
+}
+
+RSSFeed* RSS20Parser::parseFeed(xmlNode* node)
+{
+    BLACKBERRY_ASSERT(node);
+
+    RSSFeed* feed = new RSSFeed();
+
+    for (; node; node = node->next) {
+        String name(reinterpret_cast<const char*>(node->name));
+        name.makeLower();
+
+        if (parseItemBaseAttribute(feed, name, node))
+            continue;
+
+        if (name == "item")
+            feed->m_items.append(parseItem(node->children));
+        else if (name == "language")
+            feed->m_language = textFromXMLNode(node);
+        else if (name == "ttl")
+            feed->m_ttl = textFromXMLNode(node);
+    }
+
+    return feed;
+}
+
+RSSEnclosure* RSS20Parser::parseEnclosure(xmlNode* node)
+{
+    BLACKBERRY_ASSERT(node);
+
+    RSSEnclosure* enclosure = new RSSEnclosure();
+
+    for (xmlAttr* attr = node->properties; attr; attr = attr->next) {
+        String name(reinterpret_cast<const char*>(attr->name));
+        name.makeLower();
+
+        if (name == "url")
+            enclosure->m_url = textFromXMLAttr(attr);
+        else if (name == "type")
+            enclosure->m_type = textFromXMLAttr(attr);
+        else if (name == "length")
+            enclosure->m_length = textFromXMLAttr(attr);
+    }
+
+    return enclosure;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/network/blackberry/rss/RSS20Parser.h b/Source/WebCore/platform/network/blackberry/rss/RSS20Parser.h
new file mode 100644 (file)
index 0000000..6adad18
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef RSS20Parser_h
+#define RSS20Parser_h
+
+#include "RSSParserBase.h"
+
+namespace WebCore {
+
+class RSS20Parser : public RSSParserBase {
+public:
+    RSS20Parser();
+
+    bool parseBuffer(const char* buffer, int length, const char* url, const char* encoding);
+
+private:
+    bool parseXmlDoc(xmlDocPtr);
+    bool parseItemBaseAttribute(RSSItemBase*, const String& name, xmlNode*);
+    RSSItem* parseItem(xmlNode*);
+    RSSFeed* parseFeed(xmlNode*);
+    RSSEnclosure* parseEnclosure(xmlNode*);
+};
+
+} // namespace WebCore
+
+#endif // RSS20Parser_h
diff --git a/Source/WebCore/platform/network/blackberry/rss/RSSAtomParser.cpp b/Source/WebCore/platform/network/blackberry/rss/RSSAtomParser.cpp
new file mode 100644 (file)
index 0000000..6af6bc4
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+#include "RSSAtomParser.h"
+
+#include "BlackBerryPlatformAssert.h"
+#include "libxml/parser.h"
+#include "libxml/xmlwriter.h"
+
+namespace WebCore {
+
+static inline bool isRelativePath(const String& path)
+{
+    return !(path.startsWith("/") || path.find(":/") != WTF::notFound);
+}
+
+RSSAtomLink::Type RSSAtomLink::relType()
+{
+    if (m_typeInEnum != TypeUnknown)
+        return m_typeInEnum;
+
+    if (m_rel.isEmpty())
+        m_typeInEnum = TypeAlternate;
+    else {
+        String lowrel = m_rel.lower();
+        if (lowrel == "alternate")
+            m_typeInEnum = TypeAlternate;
+        else if (lowrel == "enclosure")
+            m_typeInEnum = TypeEnclosure;
+        else if (lowrel == "related")
+            m_typeInEnum = TypeRelated;
+        else if (lowrel == "self")
+            m_typeInEnum = TypeSelf;
+        else if (lowrel == "via")
+            m_typeInEnum = TypeVia;
+        else
+            m_typeInEnum = TypeUnsupported;
+    }
+
+    return m_typeInEnum;
+}
+
+RSSAtomParser::RSSAtomParser()
+{
+}
+
+bool RSSAtomParser::parseBuffer(const char* buffer, int length, const char* url, const char* encoding)
+{
+    m_url = KURL(blankURL(), url);
+    return parseXmlDoc(xmlReadMemory(buffer, length, url, encoding, XML_PARSE_NOBLANKS | XML_PARSE_NONET));
+}
+
+bool RSSAtomParser::parseXmlDoc(xmlDocPtr doc)
+{
+    if (!doc)
+        return false;
+
+    xmlNode* node = xmlDocGetRootElement(doc);
+    if (!node) {
+        xmlFreeDoc(doc);
+        return false;
+    }
+
+    for (; node; node = node->next) {
+        String name(reinterpret_cast<const char*>(node->name));
+        name.makeLower();
+
+        if (name == "feed") {
+            m_root = parseFeed(node->children);
+            break;
+        }
+    }
+
+    xmlFreeDoc(doc);
+    return m_root;
+}
+
+bool RSSAtomParser::parseItemBaseAttribute(RSSItemBase* item, const String& name, xmlNode* node, const String& base)
+{
+    if (name == "title")
+        item->m_title = textFromXMLNode(node);
+    else if (name == "id")
+        item->m_id = textFromXMLNode(node);
+    else if (name == "author")
+        item->m_author = parseAuthor(node);
+    else if (name == "updated")
+        item->m_updated = textFromXMLNode(node);
+    else if (name == "content")
+        item->m_description = parseContent(base, node);
+    else if (name == "published")
+        item->m_pubDate = textFromXMLNode(node);
+    else
+        return false;
+
+    return true;
+}
+
+RSSItem* RSSAtomParser::parseItem(xmlNode* node)
+{
+    BLACKBERRY_ASSERT(node);
+
+    RSSItem* item = new RSSItem();
+
+    String base;
+    for (xmlAttr* attr = node->properties; attr; attr = attr->next) {
+        String name(reinterpret_cast<const char*>(attr->name));
+        name.makeLower();
+        if (name == "base")
+            base = textFromXMLAttr(attr);
+    }
+
+    node = node->children;
+    for (; node; node = node->next) {
+        String name(reinterpret_cast<const char*>(node->name));
+        name.makeLower();
+
+        if (parseItemBaseAttribute(item, name, node, base))
+            continue;
+
+        if (name == "link") {
+            RSSAtomLink* link = parseLink(node);
+            if (isRelativePath(link->m_href))
+                link->m_href = base + "/" + link->m_href;
+
+            switch (link->relType()) {
+            case RSSAtomLink::TypeAlternate:
+                item->m_link = link->m_href;
+                break;
+            case RSSAtomLink::TypeEnclosure:
+                BLACKBERRY_ASSERT(!item->m_enclosure);
+                if (!item->m_enclosure)
+                    item->m_enclosure = enclosureFromLink(link);
+                break;
+            default:
+                break;
+            }
+            delete link;
+        } else if (name == "category")
+            item->m_categories.append(parseCategory(node));
+    }
+
+    return item;
+}
+
+RSSFeed* RSSAtomParser::parseFeed(xmlNode* node)
+{
+    BLACKBERRY_ASSERT(node);
+
+    RSSFeed* feed = new RSSFeed();
+
+    for (; node; node = node->next) {
+        String name(reinterpret_cast<const char*>(node->name));
+        name.makeLower();
+
+        if (parseItemBaseAttribute(feed, name, node, emptyString()))
+            continue;
+
+        if (name == "entry")
+            feed->m_items.append(parseItem(node));
+        else if (name == "link") {
+            RSSAtomLink* link = parseLink(node);
+            if (link->relType() == RSSAtomLink::TypeAlternate)
+                feed->m_link = link->m_href;
+            delete link;
+        }
+    }
+
+    return feed;
+}
+
+RSSAtomLink* RSSAtomParser::parseLink(xmlNode* node)
+{
+    BLACKBERRY_ASSERT(node);
+
+    RSSAtomLink* link = new RSSAtomLink();
+
+    for (xmlAttr* attr = node->properties; attr; attr = attr->next) {
+        String name(reinterpret_cast<const char*>(attr->name));
+        name.makeLower();
+
+        if (name == "href")
+            link->m_href = textFromXMLAttr(attr);
+        else if (name == "rel")
+            link->m_rel = textFromXMLAttr(attr);
+        else if (name == "type")
+            link->m_type = textFromXMLAttr(attr);
+        else if (name == "hreflang")
+            link->m_hreflang = textFromXMLAttr(attr);
+        else if (name == "title")
+            link->m_title = textFromXMLAttr(attr);
+        else if (name == "length")
+            link->m_length = textFromXMLAttr(attr);
+    }
+
+    return link;
+}
+
+RSSEnclosure* RSSAtomParser::enclosureFromLink(RSSAtomLink* link)
+{
+    BLACKBERRY_ASSERT(link);
+    BLACKBERRY_ASSERT(link->relType() == RSSAtomLink::TypeEnclosure);
+
+    RSSEnclosure* enclosure = new RSSEnclosure();
+
+    enclosure->m_url = link->m_href;
+    enclosure->m_type = link->m_type;
+    enclosure->m_length = link->m_length;
+
+    return enclosure;
+}
+
+String RSSAtomParser::parseContent(const String& base, xmlNode* node)
+{
+    // See: http://tools.ietf.org/html/rfc4287#page-16
+
+    BLACKBERRY_ASSERT(node);
+    String content;
+    String type = "default";
+    String src;
+    for (xmlAttr* attr = node->properties; attr; attr = attr->next) {
+        String name(reinterpret_cast<const char*>(attr->name));
+        name.makeLower();
+
+        if (name == "type")
+            type = textFromXMLAttr(attr);
+        else if (name == "src")
+            src = textFromXMLAttr(attr);
+    }
+
+    if (!src.isEmpty()) {
+        if (isRelativePath(src))
+            src = base + "/" + src;
+        content += "<a href=\"";
+        content += src + "\">" + src + "</a>";
+        return content;
+    }
+
+    if (type == "text" || type.startsWith("text/"))
+        content = textFromXMLNode(node);
+    else if (type == "html")
+        content = textFromXMLNode(node);
+    else if (type == "xhtml") {
+        xmlBufferPtr buffer = xmlBufferCreate();
+        xmlNode * cur = node->children;
+        if (cur && cur->type == XML_ELEMENT_NODE) {
+            // Encoding of buffer is utf-8.
+            xmlNodeDump(buffer, cur->doc, cur, 0, 0);
+            if (!base.isEmpty()) {
+                content += "<base href='";
+                content += m_url.baseAsString();
+                content += "/";
+                content += base;
+                content += "/' />";
+            }
+            content += (const char*)xmlBufferContent(buffer);
+        }
+        xmlBufferFree(buffer);
+    } else if (type.endsWith("+xml") || type.endsWith("/xml"))
+        // FIXME: see atom spec 4.1.3.3.4.
+        content = textFromXMLNode(node);
+    else
+        content = textFromXMLNode(node);
+
+    return content;
+}
+
+String RSSAtomParser::parseAuthor(xmlNode* node)
+{
+    BLACKBERRY_ASSERT(node);
+
+    String username;
+    String email;
+
+    for (node = node->children; node; node = node->next) {
+        String name(reinterpret_cast<const char*>(node->name));
+        name.makeLower();
+
+        if (name == "name")
+            username = textFromXMLNode(node);
+        else if (name == "email")
+            email = textFromXMLNode(node);
+    }
+
+    if (!email.isEmpty()) {
+        username += " (";
+        username += email;
+        username += ")";
+    }
+
+    return username;
+}
+
+String RSSAtomParser::parseCategory(xmlNode* node)
+{
+    BLACKBERRY_ASSERT(node);
+
+    String category;
+
+    for (xmlAttr* attr = node->properties; attr; attr = attr->next) {
+        String name(reinterpret_cast<const char*>(attr->name));
+        name.makeLower();
+
+        // If there's a label, we use it, if not, use term attribute, as label is
+        // optional, but term is mandatory.
+        if (name == "label") {
+            category = textFromXMLAttr(attr);
+            break;
+        }
+
+        if (name == "term")
+            category = textFromXMLAttr(attr);
+    }
+
+    return category;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/network/blackberry/rss/RSSAtomParser.h b/Source/WebCore/platform/network/blackberry/rss/RSSAtomParser.h
new file mode 100644 (file)
index 0000000..865f50f
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef RSSAtomParser_h
+#define RSSAtomParser_h
+
+#include "KURL.h"
+#include "RSSParserBase.h"
+
+namespace WebCore {
+
+class RSSAtomLink {
+public:
+    enum Type {
+        TypeUnknown,
+        TypeAlternate,
+        TypeRelated,
+        TypeSelf,
+        TypeEnclosure,
+        TypeVia,
+        TypeUnsupported
+    };
+
+    RSSAtomLink()
+        : m_typeInEnum(TypeUnknown)
+    { }
+
+    Type relType();
+
+    String m_rel;
+    String m_href;
+    String m_hreflang;
+    String m_type;
+    String m_title;
+    String m_length;
+
+private:
+    Type m_typeInEnum;
+};
+
+class RSSAtomParser : public RSSParserBase {
+public:
+    RSSAtomParser();
+
+    bool parseBuffer(const char* buffer, int length, const char* url, const char* encoding);
+
+private:
+    bool parseXmlDoc(xmlDocPtr);
+    bool parseItemBaseAttribute(RSSItemBase*, const String& name, xmlNode*, const String& base);
+    RSSItem* parseItem(xmlNode*);
+    RSSFeed* parseFeed(xmlNode*);
+    RSSAtomLink* parseLink(xmlNode*);
+    RSSEnclosure* enclosureFromLink(RSSAtomLink*);
+
+    String parseContent(const String& base, xmlNode*);
+    String parseAuthor(xmlNode*);
+    String parseCategory(xmlNode*);
+    KURL m_url;
+};
+
+} // namespace WebCore
+
+#endif // RSSAtomParser_h
diff --git a/Source/WebCore/platform/network/blackberry/rss/RSSFilterStream.cpp b/Source/WebCore/platform/network/blackberry/rss/RSSFilterStream.cpp
new file mode 100644 (file)
index 0000000..58915bd
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+#include "RSSFilterStream.h"
+
+#include "RSS10Parser.h"
+#include "RSS20Parser.h"
+#include "RSSAtomParser.h"
+#include "RSSGenerator.h"
+#include "TextCodecICU.h"
+
+#include <BlackBerryPlatformAssert.h>
+#include <BlackBerryPlatformLog.h>
+#include <ScopePointer.h>
+#include <network/NetworkRequest.h>
+#include <wtf/ASCIICType.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/WTFString.h>
+
+using BlackBerry::Platform::LogLevelCritical;
+using BlackBerry::Platform::LogLevelInfo;
+using BlackBerry::Platform::LogLevelWarn;
+using BlackBerry::Platform::NetworkRequest;
+
+namespace WebCore {
+
+static const char* const s_utf8EncodingName = "UTF-8";
+static const char* const s_latin1EncodingName = "ISO-8859-1";
+static const char* const s_gbkEncodingName = "GBK";
+
+static const char* const s_contentEncodingHeaderKey = "Content-Encoding";
+static const char* const s_contentLengthHeaderKey = "Content-Length";
+static const char* const s_contentTypeHeaderKey = "Content-Type";
+
+static const char* const s_atom10VersionKey = "xmlns";
+static const char* const s_atom10VersionValue = "http://www.w3.org/2005/Atom";
+static const char* const s_rss10VersionKey = "xmlns";
+static const char* const s_rss10VersionValue = "http://purl.org/rss/1.0/";
+static const char* const s_rss20VersionKey = "version";
+static const char* const s_rss20VersionValue = "2.0";
+static const char* const s_rssXmlVersionKey = "version";
+static const char* const s_rssXmlVersionValue = "1.0";
+
+static int isASCIISpaceLowerByte(int ch)
+{
+    return isASCIISpace<int>(ch & 0xff);
+}
+
+static std::string& stripWhiteSpace(std::string& str)
+{
+    str.erase(str.begin(), std::find_if(str.begin(), str.end(), std::not1(std::ptr_fun<int, int>(isASCIISpaceLowerByte))));
+    str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::ptr_fun<int, int>(isASCIISpaceLowerByte))).base(), str.end());
+    return str;
+}
+
+static inline bool equalIgnoringCase(const std::string& first, const char* second)
+{
+    return !strcasecmp(first.c_str(), second);
+}
+
+static inline bool isAtomMIMEType(const std::string& mimeType)
+{
+    return equalIgnoringCase(mimeType, "application/atom+xml");
+}
+
+static inline bool isRSSMIMEType(const std::string& mimeType)
+{
+    return equalIgnoringCase(mimeType, "application/rss+xml");
+}
+
+static inline bool isPotentialRSSMIMEType(const std::string& mimeType)
+{
+    return !strncmp(mimeType.data(), "text/xml", 8) || !strncmp(mimeType.data(), "application/xml", 15);
+}
+
+static inline bool isRSSContent(RSSFilterStream::ResourceType type)
+{
+    return type == RSSFilterStream::TypeRSS10
+            || type == RSSFilterStream::TypeRSS20
+            || type == RSSFilterStream::TypeRSSAtom;
+}
+
+static RSSFilterStream::ResourceType RSSTypeFromContentType(const std::string& contentType)
+{
+    if (contentType.empty())
+        return RSSFilterStream::TypeUnknown;
+
+    std::string mimeType;
+    std::string version;
+    bool isRSS = false;
+
+    // The contentType can contain both a mime type and version attributes
+    // If we already know the type is Atom, we don't need either of these, so skip it
+    for (size_t start = 0, delimiter = 0; delimiter != std::string::npos; start = delimiter + 1) {
+        delimiter = contentType.find(';', start);
+
+        std::string component = contentType.substr(start, delimiter == std::string::npos ? delimiter : delimiter - start);
+
+        if (mimeType.empty()) {
+            if (isAtomMIMEType(component))
+                return RSSFilterStream::TypeRSSAtom;
+
+            if (isRSSMIMEType(component)) {
+                // definitely not a version attribute, skip to next token
+                mimeType = component;
+                isRSS = true;
+                continue;
+            }
+        }
+
+        size_t equalsign = component.find('=');
+        if (equalsign != std::string::npos && equalIgnoringCase(component.substr(0, equalsign), "version"))
+            version = component.substr(equalsign + 1);
+
+        // Check if we now have found both MIME type and version
+        if (!mimeType.empty() && !version.empty())
+            break;
+    }
+
+    if (isRSS) {
+        if (!version.compare("1.0"))
+            return RSSFilterStream::TypeRSS10;
+
+        return RSSFilterStream::TypeRSS20;
+    }
+
+    return RSSFilterStream::TypeUnknown;
+}
+
+static RSSFilterStream::ResourceType RSSTypeFromContent(const char* str, int strLen)
+{
+    // Locate xml signature.
+    const char* pos = str;
+
+    // Locate the RSS tags.
+    while (true) {
+        pos = strchr(pos, '<');
+        if (!pos || (pos - str >= strLen)) {
+            BBLOG(LogLevelInfo, "RSSTypeFromContent(): can not find any start tag");
+            return RSSFilterStream::TypeUnknown;
+        }
+
+        ++pos;
+
+        if (!strncasecmp(pos, "rss", 3)) {
+            if (isSpaceOrNewline(*(pos + 3))) {
+                BBLOG(LogLevelInfo, "RSSTypeFromContent(): found RSS 2.0 tag");
+                return RSSFilterStream::TypeRSS20;
+            }
+
+            BBLOG(LogLevelInfo, "RSSTypeFromContent(): wrong RSS 2.0 tag: %.*s", 32, pos);
+            return RSSFilterStream::TypeUnknown;
+        }
+
+        if (!strncasecmp(pos, "feed", 4)) {
+            if (isSpaceOrNewline(*(pos + 4))) {
+                BBLOG(LogLevelInfo, "RSSTypeFromContent(): found Atom 1.0 tag");
+                return RSSFilterStream::TypeRSSAtom;
+            }
+
+            BBLOG(LogLevelInfo, "RSSTypeFromContent(): wrong Atom 1.0 tag: %.*s", 32, pos);
+            return RSSFilterStream::TypeUnknown;
+        }
+
+        if (!strncasecmp(pos, "rdf:RDF", 7)) {
+            if (isSpaceOrNewline(*(pos + 7))) {
+                BBLOG(LogLevelInfo, "RSSTypeFromContent(): found RSS 1.0 tag");
+                return RSSFilterStream::TypeRSS10;
+            }
+
+            BBLOG(LogLevelInfo, "RSSTypeFromContent(): wrong RSS 1.0 tag: %.*s", 32, pos);
+            return RSSFilterStream::TypeUnknown;
+        }
+
+        if (!strncasecmp(pos, "?xml", 4)) {
+            BBLOG(LogLevelInfo, "RSSTypeFromContent(): found another xml tag");
+            // This is another xml info, skip it.
+            pos += 4;
+            pos = strstr(pos, "?>");
+
+            if (!pos || (pos - str >= strLen)) {
+                BBLOG(LogLevelInfo, "RSSTypeFromContent(): extra xml tag too long");
+                return RSSFilterStream::TypeUnknown;
+            }
+
+            pos += 2;
+            continue;
+        }
+
+        if (!strncasecmp(pos, "!--", 3)) {
+            BBLOG(LogLevelInfo, "RSSTypeFromContent(): found comments");
+            // This is comments, skip it.
+            pos += 3;
+            pos = strstr(pos, "-->");
+
+            if (!pos || (pos - str >= strLen)) {
+                BBLOG(LogLevelInfo, "RSSTypeFromContent(): comments too long");
+                return RSSFilterStream::TypeUnknown;
+            }
+
+            pos += 3;
+            continue;
+        }
+
+        BBLOG(LogLevelInfo, "RSSTypeFromContent(): non rss type (%.*s) found", 32, pos);
+        return RSSFilterStream::TypeUnknown;
+    }
+
+    return RSSFilterStream::TypeUnknown;
+}
+
+static PassOwnPtr<RSSParserBase> createParser(RSSFilterStream::ResourceType type)
+{
+    BLACKBERRY_ASSERT(isRSSContent(type));
+
+    switch (type) {
+    case RSSFilterStream::TypeRSSAtom:
+        return adoptPtr(new RSSAtomParser());
+    case RSSFilterStream::TypeRSS10:
+        return adoptPtr(new RSS10Parser());
+    case RSSFilterStream::TypeRSS20:
+        return adoptPtr(new RSS20Parser());
+    default:
+        // The following code is just for compiler, it should never reach here.
+        return adoptPtr(new RSS20Parser());
+    }
+}
+
+static bool findXMLEncodingPosition(const char* str, const char*& encodingStart, const char*& encodingEnd)
+{
+    encodingStart = strstr(str, "<?xml ");
+    if (!encodingStart)
+        return false;
+
+    encodingStart += 6; // Length of "<?xml ".
+    char* endPos = strstr(const_cast<char*>(encodingStart), "?>");
+    if (!endPos)
+        return false;
+
+    encodingStart = strstr(const_cast<char*>(encodingStart), "encoding");
+
+    if (!encodingStart || encodingStart > endPos)
+        return false;
+
+    encodingStart += 8;
+
+    while (encodingStart < endPos && *encodingStart <= ' ')
+        ++encodingStart;
+
+    if (encodingStart == endPos || *encodingStart != '=')
+        return false;
+
+    ++encodingStart;
+
+    while (encodingStart < endPos && *encodingStart <= ' ')
+        ++encodingStart;
+
+    if (encodingStart == endPos)
+        return false;
+
+    char quote = *encodingStart;
+    if (quote != '\"' && quote != '\'')
+        return false;
+
+    ++encodingStart;
+    encodingEnd = strchr(const_cast<char*>(encodingStart), quote);
+
+    if (!encodingEnd || encodingEnd == encodingStart || encodingEnd > endPos)
+        return false;
+
+    return true;
+}
+
+static bool findXMLLanguagePosition(const char* str, const char*& langStart, const char*& langEnd)
+{
+    langStart = strstr(const_cast<char*>(str), "<language>");
+    if (!langStart)
+        return false;
+
+    langStart += 10; // Length of "<language>".
+    char* endPos = strstr(const_cast<char*>(langStart), "</language>");
+    if (!endPos)
+        return false;
+
+    while (langStart < endPos && *langStart <= ' ')
+        ++langStart;
+
+    if (langStart == endPos)
+        return false;
+
+    langEnd = endPos - 1;
+
+    while (langEnd > langStart && *langEnd <= ' ')
+        --langEnd;
+
+    ++langEnd;
+    return true;
+}
+
+static const char* defaultEncodingForLanguage(const char* language)
+{
+    if (!strcasecmp(language, "en")
+        || !strcasecmp(language, "en-US"))
+        return s_latin1EncodingName;
+    if (!strcasecmp(language, "zh-cn"))
+        return s_gbkEncodingName;
+
+    return 0;
+}
+
+static bool isTranscodingNeeded(const std::string& encoding)
+{
+    // When there's no encoding information, or the encoding can not be found in all encodings
+    // supported in our phone, we will try to transcode the content anyway, supposed to ASCII.
+
+    if (encoding.empty())
+        return true;
+
+    return TextEncoding(s_utf8EncodingName) != TextEncoding(encoding.c_str());
+}
+
+enum TranscodeResult {
+    Success,
+    SourceEncodingUnsupported,
+    SourceBroken,
+    TargetEncodingUnsupported,
+    TargetBufferInsufficient
+};
+
+static TranscodeResult transcode(const char* sourceEncoding,
+                                 const char* targetEncoding,
+                                 const char*& sourceStart,
+                                 int sourceLength,
+                                 char*& targetStart,
+                                 unsigned int targetLength)
+{
+    TextEncoding textEncodingSource(sourceEncoding);
+    if (!textEncodingSource.isValid())
+        return SourceEncodingUnsupported;
+
+    TextEncoding textEncodingTarget(targetEncoding);
+    if (!textEncodingTarget.isValid())
+        return TargetEncodingUnsupported;
+
+    bool sawError = false;
+    String ucs2 = TextCodecICU(textEncodingSource).decode(sourceStart, sourceLength, true, true, sawError);
+
+    if (sawError)
+        return SourceBroken;
+
+    CString encoded = TextCodecICU(textEncodingTarget).encode(ucs2.characters(), ucs2.length(), WebCore::EntitiesForUnencodables);
+    if (encoded.length() > targetLength)
+        return TargetBufferInsufficient;
+
+    strncpy(targetStart, encoded.data(), encoded.length());
+    targetStart += encoded.length();
+
+    return Success;
+}
+
+static bool transcodeContent(const std::string& content, const std::string& encoding, std::string& result)
+{
+    unsigned utf8length = content.size() * 3 / 2; // Use maximum number utf-8 length.
+    ScopeArray<char> buffer(new char[utf8length + 1]);
+
+    std::string xmlHeaderString;
+
+    const char* start = content.c_str();
+    const char* xmlStart = strstr(start, "<?xml ");
+    const char* xmlEnd;
+    if (xmlStart) {
+        xmlEnd = strstr(xmlStart, "?>");
+        if (!xmlEnd)
+            return false;
+        xmlEnd += 2; // Length of "?>".
+
+        const char* encodingStart;
+        const char* encodingEnd;
+        if (findXMLEncodingPosition(start, encodingStart, encodingEnd)) {
+            xmlHeaderString.assign(start, encodingStart - start);
+            xmlHeaderString.append(s_utf8EncodingName);
+            xmlHeaderString.append(encodingEnd, xmlEnd - encodingEnd);
+        } else {
+            xmlHeaderString.assign(start, (xmlEnd - 2) - start);
+            xmlHeaderString.append(" encoding=\"UTF-8\"?>");
+        }
+    } else {
+        xmlEnd = start;
+        xmlHeaderString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+    }
+
+    const char* contentStart = xmlEnd;
+    char* bufferStart = &buffer[0];
+
+    TranscodeResult transcodeResult = transcode(encoding.c_str(), s_utf8EncodingName, contentStart, content.length() - (xmlEnd - xmlStart), bufferStart, utf8length);
+
+    // If the encoding is not supported, or if we failed to transcode the content, we will just
+    // encode the content to ASCII content, and replacing any non-ASCII character with a question
+    // mark. This will be better than displaying the raw text, as the raw text can not be displayed
+    // correctly anyway, as their encoding is not supported.
+
+    if (Success != transcodeResult) {
+        int i = 0;
+        for (; contentStart < start + content.size() ; ++contentStart, ++i) {
+            if (isASCIIPrintable(*contentStart))
+                buffer[i] = *contentStart;
+            else
+                buffer[i] = '?';
+        }
+
+        buffer[i] = 0;
+    } else
+        *bufferStart = 0;
+
+    result.assign(xmlHeaderString);
+    result.append(&buffer[0]);
+
+    return true;
+}
+
+RSSFilterStream::RSSFilterStream()
+    : FilterStream()
+    , m_resourceType(TypeUnknown)
+    , m_contentTypeIndex(-1)
+    , m_charsetChecked(false)
+    , m_encodingChecked(false)
+{
+}
+
+void RSSFilterStream::notifyStatusReceived(int status, const char* message)
+{
+    // Non-HTTP errors have no data to display, and redirects will never be displayed,
+    // so no need to check if they have RSS data
+    // HTTP errors may have data, and in theory it could be RSS data (although it's highly
+    // unlikely) so better to keep checking them.
+    if (status < 0 || (300 <= status && status < 400 && status != 304)) {
+        m_resourceType = TypeNotRSS;
+        // It is possible we will get 2 status code.
+        sendSavedHeaders();
+    }
+
+    FilterStream::notifyStatusReceived(status, message);
+}
+
+void RSSFilterStream::notifyHeadersReceived(NetworkRequest::HeaderList& headers)
+{
+    if (!isRSSContent(m_resourceType)) {
+        NetworkRequest::HeaderList::const_iterator end = headers.end();
+        NetworkRequest::HeaderList::const_iterator iter = headers.begin();
+        for (; iter != end; ++iter) {
+            if (equalIgnoringCase(iter->first, s_contentTypeHeaderKey)) {
+                if (iter->second.find("xml") != std::string::npos) {
+                    m_contentTypeIndex = std::distance(NetworkRequest::HeaderList::const_iterator(headers.begin()), iter);
+                    ResourceType type = RSSTypeFromContentType(iter->second);
+                    if (isRSSContent(type))
+                        m_resourceType = type;
+                    else if (!isPotentialRSSMIMEType(iter->second))
+                        m_resourceType = TypeNotRSS;
+                }
+                break;
+            }
+        }
+    }
+
+    if (m_contentTypeIndex < 0)
+        m_resourceType = TypeNotRSS;
+
+    if (m_resourceType == TypeNotRSS) {
+        sendSavedHeaders();
+        FilterStream::notifyHeadersReceived(headers);
+    } else
+        saveHeaders(headers);
+}
+
+void RSSFilterStream::notifyDataReceived(BlackBerry::Platform::NetworkBuffer* buffer)
+{
+    // Here we assume the first packet of data is enough for us to sniff the content type.
+    // If someday some decent real life web site void this assumption, we should re-implement
+    // this code. But for now it should be just fine.
+    if (m_resourceType == TypeUnknown)
+        m_resourceType = RSSTypeFromContent(BlackBerry::Platform::networkBufferData(buffer), BlackBerry::Platform::networkBufferDataLength(buffer));
+
+    // If we don't know the resource type at this stage, then it is not a RSS content.
+    if (m_resourceType == TypeUnknown)
+        m_resourceType = TypeNotRSS;
+
+    if (isRSSContent(m_resourceType))
+        appendData(buffer);
+    else {
+        sendSavedHeaders();
+        FilterStream::notifyDataReceived(buffer);
+    }
+}
+
+void RSSFilterStream::notifyClose(int status)
+{
+    if (isRSSContent(m_resourceType))
+        handleRSSContent();
+    else
+        FilterStream::notifyClose(status);
+}
+
+bool RSSFilterStream::convertContentToHtml(std::string& result)
+{
+    bool success = false;
+    const std::string& base = url();
+    const std::string& enc = encoding();
+
+    OwnPtr<RSSParserBase> parser = createParser(m_resourceType);
+
+    if (isTranscodingNeeded(enc)) {
+        std::string transcodedContent;
+        if (transcodeContent(m_content, enc, transcodedContent))
+            success = parser->parseBuffer(transcodedContent.data(), transcodedContent.length(), base.c_str(), s_utf8EncodingName);
+    } else
+        success = parser->parseBuffer(m_content.data(), m_content.length(), base.c_str(), s_utf8EncodingName);
+
+    if (!success)
+        return false;
+
+    OwnPtr<RSSGenerator> generator = adoptPtr(new RSSGenerator());
+    String html = generator->generateHtml(parser->m_root);
+    result = html.utf8(true).data();
+
+    return true;
+}
+
+void RSSFilterStream::handleRSSContent()
+{
+    BLACKBERRY_ASSERT(isRSSContent(m_resourceType));
+
+    BlackBerry::Platform::NetworkBuffer* buffer;
+    std::string html;
+    if (!convertContentToHtml(html))
+        buffer = BlackBerry::Platform::createNetworkBufferWithData(m_content.c_str(), m_content.size());
+    else {
+        updateRSSHeaders(html.size());
+        buffer = BlackBerry::Platform::createNetworkBufferWithData(html.c_str(), html.size());
+    }
+
+    sendSavedHeaders();
+    FilterStream::notifyDataReceived(buffer);
+    BlackBerry::Platform::releaseNetworkBuffer(buffer);
+    FilterStream::notifyClose(FilterStream::StatusSuccess);
+}
+
+const std::string& RSSFilterStream::charset()
+{
+    BLACKBERRY_ASSERT(m_contentTypeIndex >= 0);
+
+    if (m_charsetChecked)
+        return m_charset;
+
+    m_charsetChecked = true;
+
+    static const char* charsetPrefix = "charset=";
+    static const int charsetPrefixLen = strlen(charsetPrefix);
+    size_t pos = (m_headers.at(m_contentTypeIndex).second).find(charsetPrefix);
+    if (pos != std::string::npos) {
+        m_charset = m_headers.at(m_contentTypeIndex).second.substr(pos + charsetPrefixLen);
+        stripWhiteSpace(m_charset);
+    }
+
+    return m_charset;
+}
+
+const std::string& RSSFilterStream::encoding()
+{
+    if (m_encodingChecked)
+        return m_encoding;
+
+    m_encodingChecked = true;
+    const std::string& str = charset();
+
+    if (!str.empty()) {
+        m_encoding = str;
+        return m_encoding;
+    }
+
+    const char* start = m_content.c_str();
+    const char* encodingStart = 0;
+    const char* encodingEnd = 0;
+    if (findXMLEncodingPosition(start, encodingStart, encodingEnd)) {
+        m_encoding = std::string(encodingStart, encodingEnd - encodingStart);
+        return m_encoding;
+    }
+
+    if (findXMLLanguagePosition(start, encodingStart, encodingEnd)) {
+        std::string languageString(encodingStart, encodingEnd - encodingStart);
+        const char* defaultEncoding = defaultEncodingForLanguage(languageString.c_str());
+        if (defaultEncoding) {
+            m_encoding = defaultEncoding;
+            return m_encoding;
+        }
+    }
+
+    return m_encoding;
+}
+
+void RSSFilterStream::saveHeaders(const BlackBerry::Platform::NetworkRequest::HeaderList& headers)
+{
+    m_headers = headers;
+}
+
+void RSSFilterStream::removeHeader(const std::string& key)
+{
+    NetworkRequest::HeaderList::iterator end = m_headers.end();
+    for (NetworkRequest::HeaderList::iterator iter = m_headers.begin(); iter != end; ++iter) {
+        if (!strcasecmp(iter->first.c_str(), key.c_str())) {
+            m_headers.erase(iter);
+            return;
+        }
+    }
+}
+
+void RSSFilterStream::updateHeader(const std::string& key, const std::string& value)
+{
+    NetworkRequest::HeaderList::iterator iter = m_headers.begin();
+    NetworkRequest::HeaderList::iterator end = m_headers.end();
+    for (; iter != end; ++iter) {
+        if (!strcasecmp(iter->first.c_str(), key.c_str())) {
+            iter->second = value;
+            return;
+        }
+    }
+
+    m_headers.insert(iter, std::make_pair(key, value));
+}
+
+void RSSFilterStream::updateRSSHeaders(size_t size)
+{
+    removeHeader(s_contentEncodingHeaderKey);
+    updateHeader(s_contentTypeHeaderKey, "text/html; charset=utf-8");
+
+    char buffer[16];
+    snprintf(buffer, 16, "%d", size);
+    updateHeader(s_contentLengthHeaderKey, std::string(buffer, 16));
+}
+
+void RSSFilterStream::sendSavedHeaders()
+{
+    if (!m_headers.empty()) {
+        FilterStream::notifyHeadersReceived(m_headers);
+        m_contentTypeIndex = -1;
+        m_headers.clear();
+    }
+}
+
+void RSSFilterStream::appendData(BlackBerry::Platform::NetworkBuffer* buffer)
+{
+    m_content.append(networkBufferData(buffer), networkBufferDataLength(buffer));
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/network/blackberry/rss/RSSFilterStream.h b/Source/WebCore/platform/network/blackberry/rss/RSSFilterStream.h
new file mode 100644 (file)
index 0000000..6c3e848
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef RSSFilterStream_h
+#define RSSFilterStream_h
+
+#include <network/FilterStream.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+
+namespace WebCore {
+
+class RSSFilterStream : public BlackBerry::Platform::FilterStream {
+public:
+    enum ResourceType {
+        TypeUnknown,
+        TypeNotRSS,
+        TypeRSS10,
+        TypeRSS20,
+        TypeRSSAtom
+    };
+
+    RSSFilterStream();
+
+    virtual void notifyStatusReceived(int status, const char* message);
+    virtual void notifyHeadersReceived(BlackBerry::Platform::NetworkRequest::HeaderList&);
+    virtual void notifyDataReceived(BlackBerry::Platform::NetworkBuffer*);
+    virtual void notifyClose(int status);
+
+private:
+    bool convertContentToHtml(std::string& result);
+    void handleRSSContent();
+
+    const std::string& charset();
+    const std::string& encoding();
+
+    void saveHeaders(const BlackBerry::Platform::NetworkRequest::HeaderList&);
+    void removeHeader(const std::string& key);
+    void updateHeader(const std::string& key, const std::string& value);
+    void updateRSSHeaders(size_t);
+    void sendSavedHeaders();
+
+    void appendData(BlackBerry::Platform::NetworkBuffer*);
+
+    BlackBerry::Platform::NetworkRequest::HeaderList m_headers;
+    std::string m_content;
+    std::string m_charset;
+    std::string m_encoding;
+
+    ResourceType m_resourceType;
+    int m_contentTypeIndex;
+
+    bool m_charsetChecked;
+    bool m_encodingChecked;
+};
+
+} // namespace WebCore
+
+#endif // RSSFilterStream_h
diff --git a/Source/WebCore/platform/network/blackberry/rss/RSSGenerator.cpp b/Source/WebCore/platform/network/blackberry/rss/RSSGenerator.cpp
new file mode 100644 (file)
index 0000000..6f1f842
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+#include "RSSGenerator.h"
+
+#include "LocalizeResource.h"
+#include "RSSParserBase.h"
+
+#include <wtf/text/CString.h>
+
+namespace WebCore {
+
+static const char* s_defaultFeedTitle = i18n("Untitled Feed");
+static const char* s_defaultEntryTitle = i18n("Untitled Entry");
+
+RSSGenerator::RSSGenerator()
+{
+}
+
+RSSGenerator::~RSSGenerator()
+{
+}
+
+String RSSGenerator::generateHtml(RSSFeed* feed)
+{
+    String rc;
+    rc += "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\" />";
+    rc += "<meta name=\"viewport\" content=\"initial-scale=1.0, user-scalable= no\">";
+    rc += "<title>";
+
+    if (!feed->m_title.isEmpty())
+        rc += feed->m_title;
+    else if (!feed->m_link.isEmpty())
+        rc += feed->m_link;
+    else
+        rc += s_defaultFeedTitle;
+
+    rc += "</title></head><body>";
+
+    rc += "<h2>";
+    if (!feed->m_link.isEmpty()) {
+        rc += "<a href=\"";
+        rc += feed->m_link;
+        rc += "\">";
+    }
+    if (!feed->m_title.isEmpty())
+        rc += feed->m_title;
+    else
+        rc += s_defaultFeedTitle;
+
+    if (!feed->m_link.isEmpty())
+        rc += "</a>";
+
+    rc += "</h2>";
+
+    if (!feed->m_description.isEmpty()) {
+        rc += "<p>";
+        rc += feed->m_description;
+        rc += "</p>";
+    }
+
+    for (unsigned i = 0; i < feed->m_items.size(); ++i) {
+        RSSItem* item = feed->m_items[i];
+        String articleName;
+        rc += "<div id=\"";
+        rc += articleName;
+        rc += "\" class=\"article\">\n<a href=\"";
+        if (!item->m_link.isEmpty())
+            rc +=  item->m_link.utf8(true).data();
+        rc += "\"><b>";
+        if (!item->m_title.isEmpty())
+            rc +=  item->m_title.utf8(true).data();
+        else
+            rc += s_defaultEntryTitle;
+        rc += "</b></a>\n<br />";
+
+        if (!item->m_author.isEmpty()) {
+            rc += i18n("By");
+            rc += " <b>";
+            rc += item->m_author.utf8(true).data();
+            rc += "</b> ";
+        } else {
+            if (!feed->m_author.isEmpty()) {
+                rc += i18n("By");
+                rc += " <b>";
+                rc += feed->m_author.utf8(true).data();
+                rc += "</b> ";
+            }
+        }
+
+        if (!item->m_categories.isEmpty()) {
+            if (!item->m_author.isEmpty())
+                rc += i18n("under ");
+            else
+                rc += i18n("Under ");
+
+            for (unsigned i = 0; i < item->m_categories.size() ; ++i) {
+                rc += "<b>";
+                rc += item->m_categories[i].utf8(true).data();
+                rc += "</b>";
+
+                if (i < item->m_categories.size() - 1)
+                    rc += ", ";
+            }
+        }
+
+        rc += "<br />";
+        if (!item->m_pubDate.isEmpty())
+            rc += item->m_pubDate.utf8(true).data();
+
+        rc += "<br />";
+        if (!item->m_description.isEmpty())
+            rc += item->m_description.utf8(true).data();
+        rc += "<br />";
+
+        if (item->m_enclosure) {
+            rc += "<br />";
+            rc += "<a href=\"";
+            rc += item->m_enclosure->m_url;
+            rc += "\">";
+            rc += i18n("Embedded ");
+
+            RSSEnclosure::Type type = item->m_enclosure->typeInEnum();
+            switch (type) {
+            case RSSEnclosure::TypeImage:
+                rc += i18n("Image");
+                break;
+            case RSSEnclosure::TypeAudio:
+                rc += i18n("Audio");
+                break;
+            case RSSEnclosure::TypeVideo:
+                rc += i18n("Video");
+                break;
+            case RSSEnclosure::TypeApplication:
+            default:
+                rc += i18n("Unknown Content");
+                break;
+            }
+
+            rc += i18n(" Source: ");
+            rc += item->m_enclosure->suggestedName();
+            rc += "</a><br /><br />";
+        }
+
+        rc += "<br /></div>\n";
+    }
+
+    rc += "</body></html>\n";
+
+    return rc;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/network/blackberry/rss/RSSGenerator.h b/Source/WebCore/platform/network/blackberry/rss/RSSGenerator.h
new file mode 100644 (file)
index 0000000..d78625b
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef RSSGenerator_h
+#define RSSGenerator_h
+
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class RSSFeed;
+
+class RSSGenerator {
+public:
+    RSSGenerator();
+    ~RSSGenerator();
+
+    String generateHtml(RSSFeed*);
+};
+
+} // namespace WebCore
+
+#endif // RSSGenerator_h
diff --git a/Source/WebCore/platform/network/blackberry/rss/RSSParserBase.cpp b/Source/WebCore/platform/network/blackberry/rss/RSSParserBase.cpp
new file mode 100644 (file)
index 0000000..e0a9d1a
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+#include "RSSParserBase.h"
+
+#include "libxml/parser.h"
+#include "libxml/xmlwriter.h"
+
+namespace WebCore {
+
+RSSEnclosure::RSSEnclosure()
+    : m_typeInEnum(TypeUnknown)
+{ }
+
+RSSEnclosure::Type RSSEnclosure::typeInEnum()
+{
+    if (TypeUnknown != m_typeInEnum)
+        return m_typeInEnum;
+
+    size_t div = m_type.find('/');
+    if (WTF::notFound == div || !div) {
+        m_typeInEnum = TypeUnsupported;
+        return m_typeInEnum;
+    }
+
+    String base = m_type.substring(0, div).lower();
+    if (base == "image")
+        m_typeInEnum = TypeImage;
+    else if (base == "audio")
+        m_typeInEnum = TypeAudio;
+    else if (base == "video")
+        m_typeInEnum = TypeVideo;
+    else if (base == "application")
+        m_typeInEnum = TypeApplication;
+    else
+        m_typeInEnum = TypeUnsupported;
+
+    return m_typeInEnum;
+}
+
+String RSSEnclosure::suggestedName() const
+{
+    size_t start = m_url.reverseFind('/');
+    if (WTF::notFound == start)
+        start = 0;
+    else
+        ++start;
+
+    return m_url.substring(start);
+}
+
+RSSFeed::RSSFeed()
+{
+}
+
+RSSFeed::~RSSFeed()
+{
+    clear();
+}
+
+void RSSFeed::clear()
+{
+    deleteAllValues(m_items);
+    m_items.clear();
+}
+
+RSSItem::RSSItem()
+    : m_enclosure(0)
+{
+}
+
+RSSItem::~RSSItem()
+{
+    clear();
+}
+
+void RSSItem::clear()
+{
+    delete m_enclosure;
+}
+
+RSSParserBase::RSSParserBase()
+    : m_root(0)
+{
+}
+
+RSSParserBase::~RSSParserBase()
+{
+    delete m_root;
+}
+
+String textFromXMLAttr(xmlAttr* attr)
+{
+    if (!attr)
+        return emptyString();
+
+    String text;
+
+    for (xmlNode* node = attr->children; node; node = node->next) {
+        if (node->type == XML_TEXT_NODE)
+            text += reinterpret_cast<const char*>(node->content);
+    }
+
+    return text.stripWhiteSpace();
+}
+
+String textFromXMLNode(xmlNode* node)
+{
+    if (!node)
+        return emptyString();
+
+    String text;
+
+    for (node = node->children; node; node = node->next) {
+        if ((node->type == XML_TEXT_NODE) || (node->type == XML_CDATA_SECTION_NODE))
+            text += reinterpret_cast<const char*>(node->content);
+    }
+
+    return text.stripWhiteSpace();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/network/blackberry/rss/RSSParserBase.h b/Source/WebCore/platform/network/blackberry/rss/RSSParserBase.h
new file mode 100644 (file)
index 0000000..fcb5adc
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#ifndef RSSParserBase_h
+#define RSSParserBase_h
+
+#include <libxml/tree.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class RSSItem;
+
+class RSSEnclosure {
+public:
+    enum Type {
+        TypeUnknown,
+        TypeImage,
+        TypeAudio,
+        TypeVideo,
+        TypeApplication,
+        TypeUnsupported
+    };
+
+    RSSEnclosure();
+
+    Type typeInEnum();
+    String suggestedName() const;
+
+    String m_url;
+    String m_type;
+    String m_length;
+
+private:
+    Type m_typeInEnum;
+};
+
+class RSSItemBase {
+public:
+    String m_title;
+    String m_link;
+    String m_description;
+    String m_updated;
+    String m_author;
+    String m_pubDate;
+    String m_id;
+};
+
+class RSSFeed: public RSSItemBase {
+public:
+    RSSFeed();
+    ~RSSFeed();
+
+    void clear();
+
+    String m_language;
+    String m_ttl;
+
+    Vector<RSSItem*> m_items;
+};
+
+class RSSItem: public RSSItemBase {
+public:
+    RSSItem();
+    ~RSSItem();
+
+    void clear();
+
+    String m_comments;
+    Vector<String> m_categories;
+    RSSEnclosure* m_enclosure;
+};
+
+class RSSParserBase {
+public:
+    RSSParserBase();
+    virtual ~RSSParserBase();
+
+    virtual bool parseBuffer(const char* buffer, int length, const char* url, const char* encoding) = 0;
+
+    RSSFeed* m_root;
+};
+
+String textFromXMLAttr(xmlAttr*);
+String textFromXMLNode(xmlNode*);
+
+} // namespace WebCore
+
+#endif // RSSParserBase_h