X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/96d665d24907c1cb4b0c8907541932647243b423..a5bb451448bc5abdadd4ded3f3bc18dbbf07fedd:/src/html/htmlpars.cpp diff --git a/src/html/htmlpars.cpp b/src/html/htmlpars.cpp index 2d3b86f1ee..2076129925 100644 --- a/src/html/htmlpars.cpp +++ b/src/html/htmlpars.cpp @@ -29,7 +29,7 @@ #include "wx/fontmap.h" #include "wx/html/htmldefs.h" #include "wx/html/htmlpars.h" -#include "wx/arrimpl.cpp" +#include "wx/vector.h" #ifdef __WXWINCE__ #include "wx/msw/wince/missing.h" // for bsearch() @@ -38,7 +38,7 @@ // DLL options compatibility check: WX_CHECK_BUILD_OPTIONS("wxHTML") -const wxChar *wxTRACE_HTML_DEBUG = _T("htmldebug"); +const wxChar *wxTRACE_HTML_DEBUG = wxT("htmldebug"); //----------------------------------------------------------------------------- // wxHtmlParser helpers @@ -47,12 +47,17 @@ const wxChar *wxTRACE_HTML_DEBUG = _T("htmldebug"); class wxHtmlTextPiece { public: - wxHtmlTextPiece(int pos, int lng) : m_pos(pos), m_lng(lng) {} - int m_pos, m_lng; + wxHtmlTextPiece() {} + wxHtmlTextPiece(const wxString::const_iterator& start, + const wxString::const_iterator& end) + : m_start(start), m_end(end) {} + wxString::const_iterator m_start, m_end; }; -WX_DECLARE_OBJARRAY(wxHtmlTextPiece, wxHtmlTextPieces); -WX_DEFINE_OBJARRAY(wxHtmlTextPieces) +// NB: this is an empty class and not typedef because of forward declaration +class wxHtmlTextPieces : public wxVector +{ +}; class wxHtmlParserState { @@ -61,7 +66,7 @@ public: wxHtmlTag *m_tags; wxHtmlTextPieces *m_textPieces; int m_curTextPiece; - wxString m_source; + const wxString *m_source; wxHtmlParserState *m_nextState; }; @@ -72,9 +77,10 @@ public: IMPLEMENT_ABSTRACT_CLASS(wxHtmlParser,wxObject) wxHtmlParser::wxHtmlParser() - : wxObject(), m_HandlersHash(wxKEY_STRING), - m_FS(NULL), m_HandlersStack(NULL) + : wxObject(), + m_FS(NULL) { + m_Source = NULL; m_entitiesParser = new wxHtmlEntitiesParser; m_Tags = NULL; m_CurTag = NULL; @@ -88,18 +94,10 @@ wxHtmlParser::~wxHtmlParser() while (RestoreState()) {} DestroyDOMTree(); - if (m_HandlersStack) - { - wxList& tmp = *m_HandlersStack; - wxList::iterator it, en; - for( it = tmp.begin(), en = tmp.end(); it != en; ++it ) - delete (wxHashTable*)*it; - tmp.clear(); - } - delete m_HandlersStack; - m_HandlersHash.Clear(); - WX_CLEAR_LIST(wxList, m_HandlersList); + WX_CLEAR_ARRAY(m_HandlersStack); + WX_CLEAR_HASH_SET(wxHtmlTagHandlersSet, m_HandlersSet); delete m_entitiesParser; + delete m_Source; } wxObject* wxHtmlParser::Parse(const wxString& source) @@ -125,7 +123,15 @@ void wxHtmlParser::DoneParser() void wxHtmlParser::SetSource(const wxString& src) { DestroyDOMTree(); - m_Source = src; + // NB: This is allocated on heap because wxHtmlTag uses iterators and + // making a copy of m_Source string in SetSourceAndSaveState() and + // RestoreState() would invalidate them (because wxString::m_impl's + // memory would change completely twice and iterators use pointers + // into it). So instead, we keep the string object intact and only + // store/restore pointer to it, for which we need it to be allocated + // on the heap. + delete m_Source; + m_Source = new wxString(src); CreateDOMTree(); m_CurTag = NULL; m_CurTextPiece = 0; @@ -133,54 +139,53 @@ void wxHtmlParser::SetSource(const wxString& src) void wxHtmlParser::CreateDOMTree() { - wxHtmlTagsCache cache(m_Source); + wxHtmlTagsCache cache(*m_Source); m_TextPieces = new wxHtmlTextPieces; - CreateDOMSubTree(NULL, 0, m_Source.length(), &cache); + CreateDOMSubTree(NULL, m_Source->begin(), m_Source->end(), &cache); m_CurTextPiece = 0; } -extern bool wxIsCDATAElement(const wxChar *tag); +extern bool wxIsCDATAElement(const wxString& tag); void wxHtmlParser::CreateDOMSubTree(wxHtmlTag *cur, - int begin_pos, int end_pos, + const wxString::const_iterator& begin_pos, + const wxString::const_iterator& end_pos, wxHtmlTagsCache *cache) { - if (end_pos <= begin_pos) return; + if (end_pos <= begin_pos) + return; wxChar c; - int i = begin_pos; - int textBeginning = begin_pos; + wxString::const_iterator i = begin_pos; + wxString::const_iterator textBeginning = begin_pos; // If the tag contains CDATA text, we include the text between beginning // and ending tag verbosely. Setting i=end_pos will skip to the very // end of this function where text piece is added, bypassing any child // tags parsing (CDATA element can't have child elements by definition): - if (cur != NULL && wxIsCDATAElement(cur->GetName().c_str())) + if (cur != NULL && wxIsCDATAElement(cur->GetName())) { i = end_pos; } while (i < end_pos) { - c = m_Source.GetChar(i); + c = *i; if (c == wxT('<')) { // add text to m_TextPieces: - if (i - textBeginning > 0) - m_TextPieces->Add( - wxHtmlTextPiece(textBeginning, i - textBeginning)); + if (i > textBeginning) + m_TextPieces->push_back(wxHtmlTextPiece(textBeginning, i)); // if it is a comment, skip it: - wxString::const_iterator iter = m_Source.begin() + i; - if ( SkipCommentTag(iter, m_Source.end()) ) + if ( SkipCommentTag(i, m_Source->end()) ) { - textBeginning = - i = iter - m_Source.begin() + 1; // skip closing '>' too + textBeginning = i = i + 1; // skip closing '>' too } // add another tag to the tree: - else if (i < end_pos-1 && m_Source.GetChar(i+1) != wxT('/')) + else if (i < end_pos-1 && *(i+1) != wxT('/')) { wxHtmlTag *chd; if (cur) @@ -208,12 +213,12 @@ void wxHtmlParser::CreateDOMSubTree(wxHtmlTag *cur, if (chd->HasEnding()) { CreateDOMSubTree(chd, - chd->GetBeginPos(), chd->GetEndPos1(), + chd->GetBeginIter(), chd->GetEndIter1(), cache); - i = chd->GetEndPos2(); + i = chd->GetEndIter2(); } else - i = chd->GetBeginPos(); + i = chd->GetBeginIter(); textBeginning = i; } @@ -221,17 +226,16 @@ void wxHtmlParser::CreateDOMSubTree(wxHtmlTag *cur, // ... or skip ending tag: else { - while (i < end_pos && m_Source.GetChar(i) != wxT('>')) i++; + while (i < end_pos && *i != wxT('>')) ++i; textBeginning = i+1; } } - else i++; + else ++i; } // add remaining text to m_TextPieces: - if (end_pos - textBeginning > 0) - m_TextPieces->Add( - wxHtmlTextPiece(textBeginning, end_pos - textBeginning)); + if (end_pos > textBeginning) + m_TextPieces->push_back(wxHtmlTextPiece(textBeginning, end_pos)); } void wxHtmlParser::DestroyDOMTree() @@ -246,50 +250,52 @@ void wxHtmlParser::DestroyDOMTree() } m_Tags = m_CurTag = NULL; - delete m_TextPieces; - m_TextPieces = NULL; + wxDELETE(m_TextPieces); } void wxHtmlParser::DoParsing() { m_CurTag = m_Tags; m_CurTextPiece = 0; - DoParsing(0, m_Source.length()); + DoParsing(m_Source->begin(), m_Source->end()); } -void wxHtmlParser::DoParsing(int begin_pos, int end_pos) +void wxHtmlParser::DoParsing(const wxString::const_iterator& begin_pos_, + const wxString::const_iterator& end_pos) { - if (end_pos <= begin_pos) return; + wxString::const_iterator begin_pos(begin_pos_); + + if (end_pos <= begin_pos) + return; wxHtmlTextPieces& pieces = *m_TextPieces; - size_t piecesCnt = pieces.GetCount(); + size_t piecesCnt = pieces.size(); while (begin_pos < end_pos) { - while (m_CurTag && m_CurTag->GetBeginPos() < begin_pos) + while (m_CurTag && m_CurTag->GetBeginIter() < begin_pos) m_CurTag = m_CurTag->GetNextTag(); while (m_CurTextPiece < piecesCnt && - pieces[m_CurTextPiece].m_pos < begin_pos) + pieces[m_CurTextPiece].m_start < begin_pos) m_CurTextPiece++; if (m_CurTextPiece < piecesCnt && (!m_CurTag || - pieces[m_CurTextPiece].m_pos < m_CurTag->GetBeginPos())) + pieces[m_CurTextPiece].m_start < m_CurTag->GetBeginIter())) { // Add text: AddText(GetEntitiesParser()->Parse( - m_Source.Mid(pieces[m_CurTextPiece].m_pos, - pieces[m_CurTextPiece].m_lng))); - begin_pos = pieces[m_CurTextPiece].m_pos + - pieces[m_CurTextPiece].m_lng; + wxString(pieces[m_CurTextPiece].m_start, + pieces[m_CurTextPiece].m_end))); + begin_pos = pieces[m_CurTextPiece].m_end; m_CurTextPiece++; } else if (m_CurTag) { if (m_CurTag->HasEnding()) - begin_pos = m_CurTag->GetEndPos2(); + begin_pos = m_CurTag->GetEndIter2(); else - begin_pos = m_CurTag->GetBeginPos(); + begin_pos = m_CurTag->GetBeginIter(); wxHtmlTag *t = m_CurTag; m_CurTag = m_CurTag->GetNextTag(); AddTag(*t); @@ -302,20 +308,19 @@ void wxHtmlParser::DoParsing(int begin_pos, int end_pos) void wxHtmlParser::AddTag(const wxHtmlTag& tag) { - wxHtmlTagHandler *h; bool inner = false; - h = (wxHtmlTagHandler*) m_HandlersHash.Get(tag.GetName()); - if (h) + wxHtmlTagHandlersHash::const_iterator h = m_HandlersHash.find(tag.GetName()); + if (h != m_HandlersHash.end()) { - inner = h->HandleTag(tag); + inner = h->second->HandleTag(tag); if (m_stopParsing) return; } if (!inner) { if (tag.HasEnding()) - DoParsing(tag.GetBeginPos(), tag.GetEndPos1()); + DoParsing(tag.GetBeginIter(), tag.GetEndIter1()); } } @@ -325,10 +330,9 @@ void wxHtmlParser::AddTagHandler(wxHtmlTagHandler *handler) wxStringTokenizer tokenizer(s, wxT(", ")); while (tokenizer.HasMoreTokens()) - m_HandlersHash.Put(tokenizer.GetNextToken(), handler); + m_HandlersHash[tokenizer.GetNextToken()] = handler; - if (m_HandlersList.IndexOf(handler) == wxNOT_FOUND) - m_HandlersList.Append(handler); + m_HandlersSet.insert(handler); handler->SetParser(this); } @@ -338,39 +342,24 @@ void wxHtmlParser::PushTagHandler(wxHtmlTagHandler *handler, const wxString& tag wxStringTokenizer tokenizer(tags, wxT(", ")); wxString key; - if (m_HandlersStack == NULL) - { - m_HandlersStack = new wxList; - } - - m_HandlersStack->Insert((wxObject*)new wxHashTable(m_HandlersHash)); + m_HandlersStack.push_back(new wxHtmlTagHandlersHash(m_HandlersHash)); while (tokenizer.HasMoreTokens()) { key = tokenizer.GetNextToken(); - m_HandlersHash.Delete(key); - m_HandlersHash.Put(key, handler); + m_HandlersHash[key] = handler; } } void wxHtmlParser::PopTagHandler() { - wxList::compatibility_iterator first; + wxCHECK_RET( !m_HandlersStack.empty(), + "attempt to remove HTML tag handler from empty stack" ); - if ( !m_HandlersStack || -#if wxUSE_STL - !(first = m_HandlersStack->GetFirst()) -#else // !wxUSE_STL - ((first = m_HandlersStack->GetFirst()) == NULL) -#endif // wxUSE_STL/!wxUSE_STL - ) - { - wxLogWarning(_("Warning: attempt to remove HTML tag handler from empty stack.")); - return; - } - m_HandlersHash = *((wxHashTable*) first->GetData()); - delete (wxHashTable*) first->GetData(); - m_HandlersStack->Erase(first); + wxHtmlTagHandlersHash *prev = m_HandlersStack.back(); + m_HandlersStack.pop_back(); + m_HandlersHash = *prev; + delete prev; } void wxHtmlParser::SetSourceAndSaveState(const wxString& src) @@ -390,7 +379,7 @@ void wxHtmlParser::SetSourceAndSaveState(const wxString& src) m_Tags = NULL; m_TextPieces = NULL; m_CurTextPiece = 0; - m_Source = wxEmptyString; + m_Source = NULL; SetSource(src); } @@ -400,6 +389,7 @@ bool wxHtmlParser::RestoreState() if (!m_SavedStates) return false; DestroyDOMTree(); + delete m_Source; wxHtmlParserState *s = m_SavedStates; m_SavedStates = s->m_nextState; @@ -416,8 +406,7 @@ bool wxHtmlParser::RestoreState() wxString wxHtmlParser::GetInnerSource(const wxHtmlTag& tag) { - return GetSource()->Mid(tag.GetBeginPos(), - tag.GetEndPos1() - tag.GetBeginPos()); + return wxString(tag.GetBeginIter(), tag.GetEndIter1()); } //----------------------------------------------------------------------------- @@ -443,7 +432,7 @@ void wxHtmlTagHandler::ParseInnerSource(const wxString& source) IMPLEMENT_DYNAMIC_CLASS(wxHtmlEntitiesParser,wxObject) wxHtmlEntitiesParser::wxHtmlEntitiesParser() -#if wxUSE_WCHAR_T && !wxUSE_UNICODE +#if !wxUSE_UNICODE : m_conv(NULL), m_encoding(wxFONTENCODING_SYSTEM) #endif { @@ -451,14 +440,14 @@ wxHtmlEntitiesParser::wxHtmlEntitiesParser() wxHtmlEntitiesParser::~wxHtmlEntitiesParser() { -#if wxUSE_WCHAR_T && !wxUSE_UNICODE +#if !wxUSE_UNICODE delete m_conv; #endif } +#if !wxUSE_UNICODE void wxHtmlEntitiesParser::SetEncoding(wxFontEncoding encoding) { -#if wxUSE_WCHAR_T && !wxUSE_UNICODE if (encoding == m_encoding) return; @@ -469,60 +458,68 @@ void wxHtmlEntitiesParser::SetEncoding(wxFontEncoding encoding) m_conv = NULL; else m_conv = new wxCSConv(wxFontMapper::GetEncodingName(m_encoding)); -#else - (void) encoding; -#endif } +#endif // !wxUSE_UNICODE wxString wxHtmlEntitiesParser::Parse(const wxString& input) const { - const wxChar *c, *last; - const wxChar *in_str = input.c_str(); wxString output; - output.reserve(input.length()); + const wxString::const_iterator end(input.end()); + wxString::const_iterator c(input.begin()); + wxString::const_iterator last(c); - for (c = in_str, last = in_str; *c != wxT('\0'); c++) + for ( ; c < end; ++c ) { if (*c == wxT('&')) { + if ( output.empty() ) + output.reserve(input.length()); + if (c - last > 0) - output.append(last, c - last); - if ( *++c == wxT('\0') ) + output.append(last, c); + if ( ++c == end ) break; wxString entity; - const wxChar *ent_s = c; + const wxString::const_iterator ent_s = c; wxChar entity_char; - for (; (*c >= wxT('a') && *c <= wxT('z')) || - (*c >= wxT('A') && *c <= wxT('Z')) || - (*c >= wxT('0') && *c <= wxT('9')) || - *c == wxT('_') || *c == wxT('#'); c++) {} - entity.append(ent_s, c - ent_s); - if (*c != wxT(';')) c--; + for ( ; c != end; ++c ) + { + wxChar ch = *c; + if ( !((ch >= wxT('a') && ch <= wxT('z')) || + (ch >= wxT('A') && ch <= wxT('Z')) || + (ch >= wxT('0') && ch <= wxT('9')) || + ch == wxT('_') || ch == wxT('#')) ) + break; + } + + entity.append(ent_s, c); + if (c == end || *c != wxT(';')) --c; last = c+1; entity_char = GetEntityChar(entity); if (entity_char) output << entity_char; else { - output.append(ent_s-1, c-ent_s+2); + output.append(ent_s-1, c+1); wxLogTrace(wxTRACE_HTML_DEBUG, - wxT("Unrecognized HTML entity: '%s'"), - entity.c_str()); + "Unrecognized HTML entity: '%s'", + entity); } } } - if (*last != wxT('\0')) - output.append(last); + if ( last == input.begin() ) // common case: no entity + return input; + if ( last != end ) + output.append(last, end); return output; } #if !wxUSE_UNICODE wxChar wxHtmlEntitiesParser::GetCharForCode(unsigned code) const { -#if wxUSE_WCHAR_T char buf[2]; wchar_t wbuf[2]; wbuf[0] = (wchar_t)code; @@ -531,9 +528,6 @@ wxChar wxHtmlEntitiesParser::GetCharForCode(unsigned code) const if (conv->WC2MB(buf, wbuf, 2) == (size_t)-1) return '?'; return buf[0]; -#else - return (code < 256) ? (wxChar)code : '?'; -#endif } #endif @@ -556,18 +550,23 @@ wxChar wxHtmlEntitiesParser::GetEntityChar(const wxString& entity) const { unsigned code = 0; + if (entity.empty()) + return 0; // invalid entity reference + if (entity[0] == wxT('#')) { - const wxChar *ent_s = entity.c_str(); - const wxChar *format; + // NB: parsed value is a number, so it's OK to use wx_str(), internal + // representation is the same for numbers + const wxStringCharType *ent_s = entity.wx_str(); + const wxStringCharType *format; - if (ent_s[1] == wxT('x') || ent_s[1] == wxT('X')) + if (ent_s[1] == wxS('x') || ent_s[1] == wxS('X')) { - format = wxT("%x"); + format = wxS("%x"); ent_s++; } else - format = wxT("%u"); + format = wxS("%u"); ent_s++; if (wxSscanf(ent_s, format, &code) != 1) @@ -577,7 +576,7 @@ wxChar wxHtmlEntitiesParser::GetEntityChar(const wxString& entity) const { // store the literals in wx's internal representation (either char* // in UTF-8 or wchar_t*) for best performance: - #define ENTITY(name, code) { wxSTRING_TEXT(name), code } + #define ENTITY(name, code) { wxS(name), code } static wxHtmlEntityInfo substitutions[] = { ENTITY("AElig", 198), @@ -649,6 +648,7 @@ wxChar wxHtmlEntitiesParser::GetEntityChar(const wxString& entity) const ENTITY("amp", 38), ENTITY("and", 8743), ENTITY("ang", 8736), + ENTITY("apos", 39), ENTITY("aring", 229), ENTITY("asymp", 8776), ENTITY("atilde", 227), @@ -840,9 +840,10 @@ wxChar wxHtmlEntitiesParser::GetEntityChar(const wxString& entity) const while (substitutions[substitutions_cnt].code != 0) substitutions_cnt++; - wxHtmlEntityInfo *info = NULL; + wxHtmlEntityInfo *info; #ifdef __WXWINCE__ // bsearch crashes under WinCE for some reason + info = NULL; size_t i; for (i = 0; i < substitutions_cnt; i++) { @@ -890,7 +891,7 @@ public: protected: virtual void AddText(const wxString& WXUNUSED(txt)) {} - DECLARE_NO_COPY_CLASS(wxMetaTagParser) + wxDECLARE_NO_COPY_CLASS(wxMetaTagParser); }; class wxMetaTagHandler : public wxHtmlTagHandler @@ -903,23 +904,23 @@ public: private: wxString *m_retval; - DECLARE_NO_COPY_CLASS(wxMetaTagHandler) + wxDECLARE_NO_COPY_CLASS(wxMetaTagHandler); }; bool wxMetaTagHandler::HandleTag(const wxHtmlTag& tag) { - if (tag.GetName() == _T("BODY")) + if (tag.GetName() == wxT("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"))) + if (tag.HasParam(wxT("HTTP-EQUIV")) && + tag.GetParam(wxT("HTTP-EQUIV")).IsSameAs(wxT("Content-Type"), false) && + tag.HasParam(wxT("CONTENT"))) { - wxString content = tag.GetParam(_T("CONTENT")).Lower(); - if (content.Left(19) == _T("text/html; charset=")) + wxString content = tag.GetParam(wxT("CONTENT")).Lower(); + if (content.Left(19) == wxT("text/html; charset=")) { *m_retval = content.Mid(19); m_Parser->StopParsing(); @@ -948,12 +949,12 @@ bool wxHtmlParser::SkipCommentTag(wxString::const_iterator& start, wxString::const_iterator end) { - wxASSERT_MSG( *start == '<', _T("should be called on the tag start") ); + wxASSERT_MSG( *start == '<', wxT("should be called on the tag start") ); wxString::const_iterator p = start; // comments begin with "