+wxFSFile *wxHtmlParser::OpenURL(wxHtmlURLType WXUNUSED(type), 
+                                const wxString& url) const
+{
+    return GetFS()->OpenFile(url);
+}
+
+
+//-----------------------------------------------------------------------------
+// wxHtmlParser::ExtractCharsetInformation
+//-----------------------------------------------------------------------------
+
+class wxMetaTagParser : public wxHtmlParser
+{
+public:
+    wxMetaTagParser() { }
+
+    wxObject* GetProduct() { return NULL; }
+
+protected:
+    virtual void AddText(const wxChar* WXUNUSED(txt)) {}
+
+    DECLARE_NO_COPY_CLASS(wxMetaTagParser)
+};
+
+class wxMetaTagHandler : public wxHtmlTagHandler
+{
+public:
+    wxMetaTagHandler(wxString *retval) : wxHtmlTagHandler(), m_retval(retval) {}
+    wxString GetSupportedTags() { return wxT("META,BODY"); }
+    bool HandleTag(const wxHtmlTag& tag);
+
+private:
+    wxString *m_retval;
+
+    DECLARE_NO_COPY_CLASS(wxMetaTagHandler)
+};
+
+bool wxMetaTagHandler::HandleTag(const wxHtmlTag& tag)
+{
+    if (tag.GetName() == _T("BODY"))
+    {
+        m_Parser->StopParsing();
+        return FALSE;
+    }
+
+    if (tag.HasParam(_T("HTTP-EQUIV")) &&
+        tag.GetParam(_T("HTTP-EQUIV")).IsSameAs(_T("Content-Type"), false) &&
+        tag.HasParam(_T("CONTENT")))
+    {
+        wxString content = tag.GetParam(_T("CONTENT")).Lower();
+        if (content.Left(19) == _T("text/html; charset="))
+        {
+            *m_retval = content.Mid(19);
+            m_Parser->StopParsing();
+        }
+    }
+    return FALSE;
+}
+
+
+/*static*/
+wxString wxHtmlParser::ExtractCharsetInformation(const wxString& markup)
+{
+    wxString charset;
+    wxMetaTagParser parser;
+    parser.AddTagHandler(new wxMetaTagHandler(&charset));
+    parser.Parse(markup);
+    return charset;
+}
+