diff options
Diffstat (limited to 'chromium/third_party')
65 files changed, 7286 insertions, 2937 deletions
diff --git a/chromium/third_party/WebKit/Source/core/css/parser/CSSParser.cpp b/chromium/third_party/WebKit/Source/core/css/parser/CSSParser.cpp index 58d12275e2e..746cbfdde9a 100644 --- a/chromium/third_party/WebKit/Source/core/css/parser/CSSParser.cpp +++ b/chromium/third_party/WebKit/Source/core/css/parser/CSSParser.cpp @@ -43,7 +43,7 @@ void CSSParser::parseSelector(const CSSParserContext& context, const String& sel PassRefPtrWillBeRawPtr<StyleRuleBase> CSSParser::parseRule(const CSSParserContext& context, StyleSheetContents* styleSheet, const String& rule) { if (RuntimeEnabledFeatures::newCSSParserEnabled()) - return CSSParserImpl::parseRule(rule, context, CSSParserImpl::AllowImportRules); + return CSSParserImpl::parseRule(rule, context, styleSheet, CSSParserImpl::AllowImportRules); return BisonCSSParser(context).parseRule(styleSheet, rule); } @@ -109,7 +109,7 @@ PassOwnPtr<Vector<double>> CSSParser::parseKeyframeKeyList(const String& keyList PassRefPtrWillBeRawPtr<StyleRuleKeyframe> CSSParser::parseKeyframeRule(const CSSParserContext& context, StyleSheetContents* styleSheet, const String& rule) { if (RuntimeEnabledFeatures::newCSSParserEnabled()) { - RefPtrWillBeRawPtr<StyleRuleBase> keyframe = CSSParserImpl::parseRule(rule, context, CSSParserImpl::KeyframeRules); + RefPtrWillBeRawPtr<StyleRuleBase> keyframe = CSSParserImpl::parseRule(rule, context, nullptr, CSSParserImpl::KeyframeRules); return toStyleRuleKeyframe(keyframe.get()); } return BisonCSSParser(context).parseKeyframeRule(styleSheet, rule); diff --git a/chromium/third_party/WebKit/Source/core/css/parser/CSSParserImpl.cpp b/chromium/third_party/WebKit/Source/core/css/parser/CSSParserImpl.cpp index 96da29cb822..e1509b5695e 100644 --- a/chromium/third_party/WebKit/Source/core/css/parser/CSSParserImpl.cpp +++ b/chromium/third_party/WebKit/Source/core/css/parser/CSSParserImpl.cpp @@ -101,9 +101,9 @@ bool CSSParserImpl::parseDeclarationList(MutableStylePropertySet* declaration, c return declaration->addParsedProperties(parser.m_parsedProperties); } -PassRefPtrWillBeRawPtr<StyleRuleBase> CSSParserImpl::parseRule(const String& string, const CSSParserContext& context, AllowedRulesType allowedRules) +PassRefPtrWillBeRawPtr<StyleRuleBase> CSSParserImpl::parseRule(const String& string, const CSSParserContext& context, StyleSheetContents* styleSheet, AllowedRulesType allowedRules) { - CSSParserImpl parser(context); + CSSParserImpl parser(context, styleSheet); CSSTokenizer::Scope scope(string); CSSParserTokenRange range = scope.tokenRange(); range.consumeWhitespace(); diff --git a/chromium/third_party/WebKit/Source/core/css/parser/CSSParserImpl.h b/chromium/third_party/WebKit/Source/core/css/parser/CSSParserImpl.h index daedf0b4a28..b3e81f90bbe 100644 --- a/chromium/third_party/WebKit/Source/core/css/parser/CSSParserImpl.h +++ b/chromium/third_party/WebKit/Source/core/css/parser/CSSParserImpl.h @@ -57,7 +57,7 @@ public: static bool parseValue(MutableStylePropertySet*, CSSPropertyID, const String&, bool important, const CSSParserContext&); static PassRefPtrWillBeRawPtr<ImmutableStylePropertySet> parseInlineStyleDeclaration(const String&, Element*); static bool parseDeclarationList(MutableStylePropertySet*, const String&, const CSSParserContext&); - static PassRefPtrWillBeRawPtr<StyleRuleBase> parseRule(const String&, const CSSParserContext&, AllowedRulesType); + static PassRefPtrWillBeRawPtr<StyleRuleBase> parseRule(const String&, const CSSParserContext&, StyleSheetContents*, AllowedRulesType); static void parseStyleSheet(const String&, const CSSParserContext&, StyleSheetContents*); static PassOwnPtr<Vector<double>> parseKeyframeKeyList(const String&); diff --git a/chromium/third_party/WebKit/Source/core/dom/NodeRareData.cpp b/chromium/third_party/WebKit/Source/core/dom/NodeRareData.cpp index 65d14a36a2c..6365a9caeeb 100644 --- a/chromium/third_party/WebKit/Source/core/dom/NodeRareData.cpp +++ b/chromium/third_party/WebKit/Source/core/dom/NodeRareData.cpp @@ -73,6 +73,12 @@ void NodeRareData::finalizeGarbageCollectedObject() this->~NodeRareData(); } +void NodeRareData::incrementConnectedSubframeCount(unsigned amount) +{ + RELEASE_ASSERT_WITH_SECURITY_IMPLICATION((m_connectedFrameCount + amount) <= FrameHost::maxNumberOfFrames); + m_connectedFrameCount += amount; +} + // Ensure the 10 bits reserved for the m_connectedFrameCount cannot overflow static_assert(FrameHost::maxNumberOfFrames < (1 << NodeRareData::ConnectedFrameCountBits), "Frame limit should fit in rare data count"); diff --git a/chromium/third_party/WebKit/Source/core/dom/NodeRareData.h b/chromium/third_party/WebKit/Source/core/dom/NodeRareData.h index f19088c088d..2f43c45bba6 100644 --- a/chromium/third_party/WebKit/Source/core/dom/NodeRareData.h +++ b/chromium/third_party/WebKit/Source/core/dom/NodeRareData.h @@ -82,10 +82,7 @@ public: } unsigned connectedSubframeCount() const { return m_connectedFrameCount; } - void incrementConnectedSubframeCount(unsigned amount) - { - m_connectedFrameCount += amount; - } + void incrementConnectedSubframeCount(unsigned amount); void decrementConnectedSubframeCount(unsigned amount) { ASSERT(m_connectedFrameCount); diff --git a/chromium/third_party/WebKit/Source/core/editing/htmlediting.cpp b/chromium/third_party/WebKit/Source/core/editing/htmlediting.cpp index f874952bfcb..5931c1f11ca 100644 --- a/chromium/third_party/WebKit/Source/core/editing/htmlediting.cpp +++ b/chromium/third_party/WebKit/Source/core/editing/htmlediting.cpp @@ -958,11 +958,11 @@ void updatePositionForNodeRemoval(Position& position, Node& node) return; switch (position.anchorType()) { case Position::PositionIsBeforeChildren: - if (position.containerNode() == node) + if (node.containsIncludingShadowDOM(position.containerNode())) position = positionInParentBeforeNode(node); break; case Position::PositionIsAfterChildren: - if (position.containerNode() == node) + if (node.containsIncludingShadowDOM(position.containerNode())) position = positionInParentAfterNode(node); break; case Position::PositionIsOffsetInAnchor: diff --git a/chromium/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/chromium/third_party/WebKit/Source/core/frame/LocalFrame.cpp index 103113f0c0b..3397abcacff 100644 --- a/chromium/third_party/WebKit/Source/core/frame/LocalFrame.cpp +++ b/chromium/third_party/WebKit/Source/core/frame/LocalFrame.cpp @@ -693,8 +693,6 @@ bool LocalFrame::isURLAllowed(const KURL& url) const { // We allow one level of self-reference because some sites depend on that, // but we don't allow more than one. - if (host()->subframeCount() >= FrameHost::maxNumberOfFrames) - return false; bool foundSelfReference = false; for (const Frame* frame = this; frame; frame = frame->tree().parent()) { if (!frame->isLocalFrame()) diff --git a/chromium/third_party/WebKit/Source/core/frame/UseCounter.cpp b/chromium/third_party/WebKit/Source/core/frame/UseCounter.cpp index 380aea184e1..1c8e52f49e4 100644 --- a/chromium/third_party/WebKit/Source/core/frame/UseCounter.cpp +++ b/chromium/third_party/WebKit/Source/core/frame/UseCounter.cpp @@ -884,7 +884,7 @@ String UseCounter::deprecationMessage(Feature feature) return "'SVGSVGElement.unsuspendRedrawAll()' is deprecated, please do not use it. It is a no-op, as per SVG2 (https://svgwg.org/svg2-draft/struct.html#__svg__SVGSVGElement__unsuspendRedrawAll)."; case ServiceWorkerClientPostMessage: - return "'Client.postMessage()' is an experimental API and may change. See https://github.com/slightlyoff/ServiceWorker/issues/609."; + return "'Client.postMessage()' will change to fire an event on 'navigator.serviceWorker' instead of 'window' in M45 (see: https://www.chromestatus.com/feature/5163630974730240)."; case AttrChildAccess: case AttrChildChange: diff --git a/chromium/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.cpp b/chromium/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.cpp index 259bf143cf9..3f278b43f71 100644 --- a/chromium/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.cpp +++ b/chromium/third_party/WebKit/Source/core/html/HTMLFrameOwnerElement.cpp @@ -26,6 +26,7 @@ #include "core/dom/AXObjectCache.h" #include "core/dom/ExceptionCode.h" #include "core/events/Event.h" +#include "core/frame/FrameHost.h" #include "core/frame/FrameView.h" #include "core/frame/LocalFrame.h" #include "core/layout/LayoutPart.h" @@ -257,6 +258,9 @@ bool HTMLFrameOwnerElement::loadOrRedirectSubframe(const KURL& url, const Atomic if (!SubframeLoadingDisabler::canLoadFrame(*this)) return false; + if (document().frame()->host()->subframeCount() >= FrameHost::maxNumberOfFrames) + return false; + return parentFrame->loader().client()->createFrame(FrameLoadRequest(&document(), url, "_self", CheckContentSecurityPolicy), frameName, this); } diff --git a/chromium/third_party/WebKit/Source/core/html/HTMLImageElement.cpp b/chromium/third_party/WebKit/Source/core/html/HTMLImageElement.cpp index b725d6b2984..d46f9de78ae 100644 --- a/chromium/third_party/WebKit/Source/core/html/HTMLImageElement.cpp +++ b/chromium/third_party/WebKit/Source/core/html/HTMLImageElement.cpp @@ -330,12 +330,12 @@ ImageCandidate HTMLImageElement::findBestFitImageFromPictureParent() LayoutObject* HTMLImageElement::createLayoutObject(const ComputedStyle& style) { - if (style.hasContent()) - return LayoutObject::createObject(this, style); - if (m_useFallbackContent) return new LayoutBlockFlow(this); + if (style.hasContent()) + return LayoutObject::createObject(this, style); + LayoutImage* image = new LayoutImage(this); image->setImageResource(LayoutImageResource::create()); image->setImageDevicePixelRatio(m_imageDevicePixelRatio); diff --git a/chromium/third_party/WebKit/Source/core/loader/FrameLoader.cpp b/chromium/third_party/WebKit/Source/core/loader/FrameLoader.cpp index 669439d1da1..78d81b97ca4 100644 --- a/chromium/third_party/WebKit/Source/core/loader/FrameLoader.cpp +++ b/chromium/third_party/WebKit/Source/core/loader/FrameLoader.cpp @@ -562,8 +562,6 @@ bool FrameLoader::allowPlugins(ReasonForCallingAllowPlugins reason) void FrameLoader::updateForSameDocumentNavigation(const KURL& newURL, SameDocumentNavigationSource sameDocumentNavigationSource, PassRefPtr<SerializedScriptValue> data, HistoryScrollRestorationType scrollRestorationType, FrameLoadType type) { - saveScrollState(); - // Update the data source's request with the new URL to fake the URL change m_frame->document()->setURL(newURL); documentLoader()->setReplacesCurrentHistoryItem(type != FrameLoadTypeStandard); @@ -601,6 +599,7 @@ void FrameLoader::loadInSameDocument(const KURL& url, PassRefPtr<SerializedScrip return; } m_loadType = type; + saveScrollState(); KURL oldURL = m_frame->document()->url(); // If we were in the autoscroll/panScroll mode we want to stop it before following the link to the anchor diff --git a/chromium/third_party/WebKit/Source/core/timing/ConsoleMemory.cpp b/chromium/third_party/WebKit/Source/core/timing/ConsoleMemory.cpp index a44ff10768f..bd1bdf74812 100644 --- a/chromium/third_party/WebKit/Source/core/timing/ConsoleMemory.cpp +++ b/chromium/third_party/WebKit/Source/core/timing/ConsoleMemory.cpp @@ -10,12 +10,6 @@ namespace blink { -DEFINE_TRACE(ConsoleMemory) -{ - visitor->trace(m_memory); - HeapSupplement<Console>::trace(visitor); -} - // static ConsoleMemory& ConsoleMemory::from(Console& console) { @@ -35,10 +29,7 @@ MemoryInfo* ConsoleMemory::memory(Console& console) MemoryInfo* ConsoleMemory::memory() { - if (!m_memory) - m_memory = MemoryInfo::create(); - - return m_memory.get(); + return MemoryInfo::create(); } } // namespace blink diff --git a/chromium/third_party/WebKit/Source/core/timing/ConsoleMemory.h b/chromium/third_party/WebKit/Source/core/timing/ConsoleMemory.h index 0276bc70eaa..ad7c0dbadad 100644 --- a/chromium/third_party/WebKit/Source/core/timing/ConsoleMemory.h +++ b/chromium/third_party/WebKit/Source/core/timing/ConsoleMemory.h @@ -19,13 +19,11 @@ public: static MemoryInfo* memory(Console&); static void setMemory(Console&, MemoryInfo*) { } - DECLARE_VIRTUAL_TRACE(); + DEFINE_INLINE_VIRTUAL_TRACE() { HeapSupplement<Console>::trace(visitor); } private: static const char* supplementName() { return "ConsoleMemory"; } MemoryInfo* memory(); - - Member<MemoryInfo> m_memory; }; } // namespace blink diff --git a/chromium/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp b/chromium/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp index 286a38138ac..12ac82bcadd 100644 --- a/chromium/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp +++ b/chromium/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp @@ -665,6 +665,9 @@ void XMLHttpRequest::dispatchReadyStateChangeEvent() if (!executionContext()) return; + // We need this protection because dispatchReadyStateChangeEvent may + // dispatch multiple events. + ScopedEventDispatchProtect protect(&m_eventDispatchRecursionLevel); if (m_async || (m_state <= OPENED || m_state == DONE)) { TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "XHRReadyStateChange", "data", InspectorXhrReadyStateChangeEvent::data(executionContext(), this)); XMLHttpRequestProgressEventThrottle::DeferredEventAction action = XMLHttpRequestProgressEventThrottle::Ignore; diff --git a/chromium/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequestProgressEventThrottle.cpp b/chromium/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequestProgressEventThrottle.cpp index 5f9e9d15cb0..f3d619673fd 100644 --- a/chromium/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequestProgressEventThrottle.cpp +++ b/chromium/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequestProgressEventThrottle.cpp @@ -28,7 +28,7 @@ #include "core/xmlhttprequest/XMLHttpRequestProgressEventThrottle.h" #include "core/EventTypeNames.h" -#include "core/events/EventTarget.h" +#include "core/xmlhttprequest/XMLHttpRequest.h" #include "core/xmlhttprequest/XMLHttpRequestProgressEvent.h" #include "wtf/Assertions.h" #include "wtf/text/AtomicString.h" @@ -66,7 +66,7 @@ private: const double XMLHttpRequestProgressEventThrottle::minimumProgressEventDispatchingIntervalInSeconds = .05; // 50 ms per specification. -XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle(EventTarget* target) +XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle(XMLHttpRequest* target) : m_target(target) , m_deferred(adoptPtr(new DeferredEvent)) { @@ -96,17 +96,25 @@ void XMLHttpRequestProgressEventThrottle::dispatchProgressEvent(const AtomicStri void XMLHttpRequestProgressEventThrottle::dispatchReadyStateChangeEvent(PassRefPtrWillBeRawPtr<Event> event, DeferredEventAction action) { + XMLHttpRequest::State state = m_target->readyState(); // Given that ResourceDispatcher doesn't deliver an event when suspended, // we don't have to worry about event dispatching while suspended. if (action == Flush) { dispatchDeferredEvent(); + // |m_target| is protected by the caller. stop(); } else if (action == Clear) { m_deferred->clear(); stop(); } - m_target->dispatchEvent(event); + if (state == m_target->readyState()) { + // We don't dispatch the event when an event handler associated with + // the previously dispatched event changes the readyState (e.g. when + // the event handler calls xhr.abort()). In such cases a + // readystatechange should have been already dispatched if necessary. + m_target->dispatchEvent(event); + } } void XMLHttpRequestProgressEventThrottle::dispatchDeferredEvent() diff --git a/chromium/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequestProgressEventThrottle.h b/chromium/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequestProgressEventThrottle.h index edb3e13bf48..839d14e348f 100644 --- a/chromium/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequestProgressEventThrottle.h +++ b/chromium/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequestProgressEventThrottle.h @@ -36,7 +36,7 @@ namespace blink { class Event; -class EventTarget; +class XMLHttpRequest; // This class implements the XHR2 ProgressEvent dispatching: // "dispatch a progress event named progress about every 50ms or for every @@ -58,7 +58,7 @@ public: Flush, }; - explicit XMLHttpRequestProgressEventThrottle(EventTarget*); + explicit XMLHttpRequestProgressEventThrottle(XMLHttpRequest*); virtual ~XMLHttpRequestProgressEventThrottle(); // Dispatches a ProgressEvent. @@ -93,7 +93,7 @@ private: // the one holding us. With Oilpan, a simple strong Member can be used - // this XMLHttpRequestProgressEventThrottle (part) object dies together // with the XMLHttpRequest object. - RawPtrWillBeMember<EventTarget> m_target; + RawPtrWillBeMember<XMLHttpRequest> m_target; // A slot for the deferred "progress" ProgressEvent. When multiple events // arrive, only the last one is stored and others are discarded. diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/elements/spectrum.css b/chromium/third_party/WebKit/Source/devtools/front_end/elements/spectrum.css index 90b8303ff2f..3d5b6bfbc54 100644 --- a/chromium/third_party/WebKit/Source/devtools/front_end/elements/spectrum.css +++ b/chromium/third_party/WebKit/Source/devtools/front_end/elements/spectrum.css @@ -114,7 +114,6 @@ .spectrum-text-value { display: inline-block; width: 36px; - max-width: 36px; overflow: hidden; text-align: center; border: 1px solid #dadada; diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/emulation/EmulatedDevices.js b/chromium/third_party/WebKit/Source/devtools/front_end/emulation/EmulatedDevices.js index 115572f6794..59660fce004 100644 --- a/chromium/third_party/WebKit/Source/devtools/front_end/emulation/EmulatedDevices.js +++ b/chromium/third_party/WebKit/Source/devtools/front_end/emulation/EmulatedDevices.js @@ -424,6 +424,7 @@ WebInspector.EmulatedDevice.Images.prototype = { WebInspector.EmulatedDevicesList = function() { WebInspector.Object.call(this); + WebInspector.settings.createSetting("standardEmulatedDeviceList", []).remove(); /** * @param {!Array.<*>} list diff --git a/chromium/third_party/WebKit/Source/modules/accessibility/AXMenuListOption.cpp b/chromium/third_party/WebKit/Source/modules/accessibility/AXMenuListOption.cpp index 68973631a95..d4fa94d2b96 100644 --- a/chromium/third_party/WebKit/Source/modules/accessibility/AXMenuListOption.cpp +++ b/chromium/third_party/WebKit/Source/modules/accessibility/AXMenuListOption.cpp @@ -102,9 +102,13 @@ bool AXMenuListOption::computeAccessibilityIsIgnored(IgnoredReasons* ignoredReas LayoutRect AXMenuListOption::elementRect() const { AXObject* parent = parentObject(); + if (!parent) + return LayoutRect(); ASSERT(parent->isMenuListPopup()); AXObject* grandparent = parent->parentObject(); + if (!grandparent) + return LayoutRect(); ASSERT(grandparent->isMenuList()); return grandparent->elementRect(); diff --git a/chromium/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.cpp b/chromium/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.cpp index 53c5cb0c534..5719923e76d 100644 --- a/chromium/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.cpp +++ b/chromium/third_party/WebKit/Source/platform/graphics/ImageFrameGenerator.cpp @@ -138,6 +138,7 @@ bool ImageFrameGenerator::decodeAndScale(const SkImageInfo& info, size_t index, ASSERT(bitmap.height() == scaledSize.height()); bool result = true; + SkAutoLockPixels bitmapLock(bitmap); // Check to see if decoder has written directly to the memory provided // by Skia. If not make a copy. if (bitmap.getPixels() != pixels) diff --git a/chromium/third_party/ashmem/BUILD.gn b/chromium/third_party/ashmem/BUILD.gn deleted file mode 100644 index 5e92b3773f5..00000000000 --- a/chromium/third_party/ashmem/BUILD.gn +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -assert(is_android) - -source_set("ashmem") { - sources = [ - "ashmem-dev.c", - "ashmem.h", - ] -} diff --git a/chromium/third_party/ashmem/LICENSE b/chromium/third_party/ashmem/LICENSE deleted file mode 100644 index d6456956733..00000000000 --- a/chromium/third_party/ashmem/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/chromium/third_party/ashmem/OWNERS b/chromium/third_party/ashmem/OWNERS deleted file mode 100644 index 938d2b5c483..00000000000 --- a/chromium/third_party/ashmem/OWNERS +++ /dev/null @@ -1 +0,0 @@ -digit@chromium.org diff --git a/chromium/third_party/ashmem/README.chromium b/chromium/third_party/ashmem/README.chromium deleted file mode 100644 index 833e1f5b017..00000000000 --- a/chromium/third_party/ashmem/README.chromium +++ /dev/null @@ -1,6 +0,0 @@ -Name: Android -URL: http://source.android.com -Description: Android shared memory implementation. Only applies to OS_ANDROID. -Version: 7203eb2a8a29a7b721a48cd291700f38f3da1456 -Security Critical: yes -License: Apache 2.0 diff --git a/chromium/third_party/ashmem/ashmem-dev.c b/chromium/third_party/ashmem/ashmem-dev.c deleted file mode 100644 index 2303369d816..00000000000 --- a/chromium/third_party/ashmem/ashmem-dev.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Implementation of the user-space ashmem API for devices, which have our - * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version, - * used by the simulator. - */ - -#include <unistd.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <fcntl.h> - -#include <linux/ashmem.h> -#include "ashmem.h" - -#define ASHMEM_DEVICE "/dev/ashmem" - -/* - * ashmem_create_region - creates a new ashmem region and returns the file - * descriptor, or <0 on error - * - * `name' is an optional label to give the region (visible in /proc/pid/maps) - * `size' is the size of the region, in page-aligned bytes - */ -int ashmem_create_region(const char *name, size_t size) -{ - int fd, ret; - - fd = open(ASHMEM_DEVICE, O_RDWR); - if (fd < 0) - return fd; - - if (name) { - char buf[ASHMEM_NAME_LEN]; - - strlcpy(buf, name, sizeof(buf)); - ret = ioctl(fd, ASHMEM_SET_NAME, buf); - if (ret < 0) - goto error; - } - - ret = ioctl(fd, ASHMEM_SET_SIZE, size); - if (ret < 0) - goto error; - - return fd; - -error: - close(fd); - return ret; -} - -int ashmem_set_prot_region(int fd, int prot) -{ - return ioctl(fd, ASHMEM_SET_PROT_MASK, prot); -} - -int ashmem_pin_region(int fd, size_t offset, size_t len) -{ - struct ashmem_pin pin = { offset, len }; - return ioctl(fd, ASHMEM_PIN, &pin); -} - -int ashmem_unpin_region(int fd, size_t offset, size_t len) -{ - struct ashmem_pin pin = { offset, len }; - return ioctl(fd, ASHMEM_UNPIN, &pin); -} - -int ashmem_get_size_region(int fd) -{ - return ioctl(fd, ASHMEM_GET_SIZE, NULL); -} - -int ashmem_purge_all(void) -{ - const int fd = open(ASHMEM_DEVICE, O_RDWR); - if (fd < 0) - return fd; - const int ret = ioctl(fd, ASHMEM_PURGE_ALL_CACHES, 0); - close(fd); - return ret; -} diff --git a/chromium/third_party/ashmem/ashmem.h b/chromium/third_party/ashmem/ashmem.h deleted file mode 100644 index 7d411cc064b..00000000000 --- a/chromium/third_party/ashmem/ashmem.h +++ /dev/null @@ -1,46 +0,0 @@ -/* third_party/ashmem/ashmem.h - ** - ** Copyright 2008 The Android Open Source Project - ** - ** This file is dual licensed. It may be redistributed and/or modified - ** under the terms of the Apache 2.0 License OR version 2 of the GNU - ** General Public License. - */ - -#ifndef _THIRD_PARTY_ASHMEM_H -#define _THIRD_PARTY_ASHMEM_H - -#include <stddef.h> - -#ifdef __cplusplus -extern "C" { -#endif - -int ashmem_create_region(const char *name, size_t size); -int ashmem_set_prot_region(int fd, int prot); -int ashmem_pin_region(int fd, size_t offset, size_t len); -int ashmem_unpin_region(int fd, size_t offset, size_t len); -int ashmem_get_size_region(int fd); -int ashmem_purge_all(void); - -#ifdef __cplusplus -} -#endif - -#ifndef __ASHMEMIOC /* in case someone included <linux/ashmem.h> too */ - -#define ASHMEM_NAME_LEN 256 - -#define ASHMEM_NAME_DEF "dev/ashmem" - -/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */ -#define ASHMEM_NOT_PURGED 0 -#define ASHMEM_WAS_PURGED 1 - -/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */ -#define ASHMEM_IS_UNPINNED 0 -#define ASHMEM_IS_PINNED 1 - -#endif /* ! __ASHMEMIOC */ - -#endif /* _THIRD_PARTY_ASHMEM_H */ diff --git a/chromium/third_party/expat/README.chromium b/chromium/third_party/expat/README.chromium index 7b47f5f7f1d..a0af1e2d310 100644 --- a/chromium/third_party/expat/README.chromium +++ b/chromium/third_party/expat/README.chromium @@ -4,7 +4,7 @@ URL: http://sourceforge.net/projects/expat/ Version: 2.1.0 License: MIT License File: files/COPYING -Security Critical: no +Security Critical: yes Description: This is Expat XML parser - very lightweight C library for parsing XML. @@ -38,5 +38,8 @@ Local Modifications: lib/xmltok_impl.c (see xmltok_imp.c.original for unmodified version) * Prevent a compiler warning when compiling with WIN32_LEAN_AND_MEAN predefined. + lib/xmlparse.c (see xmlparse.c.original for unmodified version) + * Apply https://hg.mozilla.org/releases/mozilla-esr31/rev/2f3e78643f5c + to prevent an integer overflow. Added files: lib/expat_config.h (a generated config file) diff --git a/chromium/third_party/expat/files/lib/xmlparse.c b/chromium/third_party/expat/files/lib/xmlparse.c index f35aa36ba8a..ede7b5bb667 100644 --- a/chromium/third_party/expat/files/lib/xmlparse.c +++ b/chromium/third_party/expat/files/lib/xmlparse.c @@ -1678,6 +1678,12 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) void * XMLCALL XML_GetBuffer(XML_Parser parser, int len) { +/* BEGIN MOZILLA CHANGE (sanity check len) */ + if (len < 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } +/* END MOZILLA CHANGE */ switch (ps_parsing) { case XML_SUSPENDED: errorCode = XML_ERROR_SUSPENDED; @@ -1689,8 +1695,13 @@ XML_GetBuffer(XML_Parser parser, int len) } if (len > bufferLim - bufferEnd) { - /* FIXME avoid integer overflow */ int neededSize = len + (int)(bufferEnd - bufferPtr); +/* BEGIN MOZILLA CHANGE (sanity check neededSize) */ + if (neededSize < 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } +/* END MOZILLA CHANGE */ #ifdef XML_CONTEXT_BYTES int keep = (int)(bufferPtr - buffer); @@ -1719,7 +1730,15 @@ XML_GetBuffer(XML_Parser parser, int len) bufferSize = INIT_BUFFER_SIZE; do { bufferSize *= 2; - } while (bufferSize < neededSize); +/* BEGIN MOZILLA CHANGE (prevent infinite loop on overflow) */ + } while (bufferSize < neededSize && bufferSize > 0); +/* END MOZILLA CHANGE */ +/* BEGIN MOZILLA CHANGE (sanity check bufferSize) */ + if (bufferSize <= 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } +/* END MOZILLA CHANGE */ newBuf = (char *)MALLOC(bufferSize); if (newBuf == 0) { errorCode = XML_ERROR_NO_MEMORY; diff --git a/chromium/third_party/expat/files/lib/xmlparse.c.original b/chromium/third_party/expat/files/lib/xmlparse.c.original new file mode 100644 index 00000000000..f35aa36ba8a --- /dev/null +++ b/chromium/third_party/expat/files/lib/xmlparse.c.original @@ -0,0 +1,6403 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include <stddef.h> +#include <string.h> /* memset(), memcpy() */ +#include <assert.h> +#include <limits.h> /* UINT_MAX */ +#include <time.h> /* time() */ + +#define XML_BUILDING_EXPAT 1 + +#ifdef COMPILED_FROM_DSP +#include "winconfig.h" +#elif defined(MACOS_CLASSIC) +#include "macconfig.h" +#elif defined(__amigaos__) +#include "amigaconfig.h" +#elif defined(__WATCOMC__) +#include "watcomconfig.h" +#elif defined(HAVE_EXPAT_CONFIG_H) +#include <expat_config.h> +#endif /* ndef COMPILED_FROM_DSP */ + +#include "ascii.h" +#include "expat.h" + +#ifdef XML_UNICODE +#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX +#define XmlConvert XmlUtf16Convert +#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS +#define XmlEncode XmlUtf16Encode +/* Using pointer subtraction to convert to integer type. */ +#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((char *)(s) - (char *)NULL) & 1)) +typedef unsigned short ICHAR; +#else +#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX +#define XmlConvert XmlUtf8Convert +#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS +#define XmlEncode XmlUtf8Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf8) +typedef char ICHAR; +#endif + + +#ifndef XML_NS + +#define XmlInitEncodingNS XmlInitEncoding +#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding +#undef XmlGetInternalEncodingNS +#define XmlGetInternalEncodingNS XmlGetInternalEncoding +#define XmlParseXmlDeclNS XmlParseXmlDecl + +#endif + +#ifdef XML_UNICODE + +#ifdef XML_UNICODE_WCHAR_T +#define XML_T(x) (const wchar_t)x +#define XML_L(x) L ## x +#else +#define XML_T(x) (const unsigned short)x +#define XML_L(x) x +#endif + +#else + +#define XML_T(x) x +#define XML_L(x) x + +#endif + +/* Round up n to be a multiple of sz, where sz is a power of 2. */ +#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1)) + +/* Handle the case where memmove() doesn't exist. */ +#ifndef HAVE_MEMMOVE +#ifdef HAVE_BCOPY +#define memmove(d,s,l) bcopy((s),(d),(l)) +#else +#error memmove does not exist on this platform, nor is a substitute available +#endif /* HAVE_BCOPY */ +#endif /* HAVE_MEMMOVE */ + +#include "internal.h" +#include "xmltok.h" +#include "xmlrole.h" + +typedef const XML_Char *KEY; + +typedef struct { + KEY name; +} NAMED; + +typedef struct { + NAMED **v; + unsigned char power; + size_t size; + size_t used; + const XML_Memory_Handling_Suite *mem; +} HASH_TABLE; + +/* Basic character hash algorithm, taken from Python's string hash: + h = h * 1000003 ^ character, the constant being a prime number. + +*/ +#ifdef XML_UNICODE +#define CHAR_HASH(h, c) \ + (((h) * 0xF4243) ^ (unsigned short)(c)) +#else +#define CHAR_HASH(h, c) \ + (((h) * 0xF4243) ^ (unsigned char)(c)) +#endif + +/* For probing (after a collision) we need a step size relative prime + to the hash table size, which is a power of 2. We use double-hashing, + since we can calculate a second hash value cheaply by taking those bits + of the first hash value that were discarded (masked out) when the table + index was calculated: index = hash & mask, where mask = table->size - 1. + We limit the maximum step size to table->size / 4 (mask >> 2) and make + it odd, since odd numbers are always relative prime to a power of 2. +*/ +#define SECOND_HASH(hash, mask, power) \ + ((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2)) +#define PROBE_STEP(hash, mask, power) \ + ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1)) + +typedef struct { + NAMED **p; + NAMED **end; +} HASH_TABLE_ITER; + +#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ +#define INIT_DATA_BUF_SIZE 1024 +#define INIT_ATTS_SIZE 16 +#define INIT_ATTS_VERSION 0xFFFFFFFF +#define INIT_BLOCK_SIZE 1024 +#define INIT_BUFFER_SIZE 1024 + +#define EXPAND_SPARE 24 + +typedef struct binding { + struct prefix *prefix; + struct binding *nextTagBinding; + struct binding *prevPrefixBinding; + const struct attribute_id *attId; + XML_Char *uri; + int uriLen; + int uriAlloc; +} BINDING; + +typedef struct prefix { + const XML_Char *name; + BINDING *binding; +} PREFIX; + +typedef struct { + const XML_Char *str; + const XML_Char *localPart; + const XML_Char *prefix; + int strLen; + int uriLen; + int prefixLen; +} TAG_NAME; + +/* TAG represents an open element. + The name of the element is stored in both the document and API + encodings. The memory buffer 'buf' is a separately-allocated + memory area which stores the name. During the XML_Parse()/ + XMLParseBuffer() when the element is open, the memory for the 'raw' + version of the name (in the document encoding) is shared with the + document buffer. If the element is open across calls to + XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to + contain the 'raw' name as well. + + A parser re-uses these structures, maintaining a list of allocated + TAG objects in a free list. +*/ +typedef struct tag { + struct tag *parent; /* parent of this element */ + const char *rawName; /* tagName in the original encoding */ + int rawNameLength; + TAG_NAME name; /* tagName in the API encoding */ + char *buf; /* buffer for name components */ + char *bufEnd; /* end of the buffer */ + BINDING *bindings; +} TAG; + +typedef struct { + const XML_Char *name; + const XML_Char *textPtr; + int textLen; /* length in XML_Chars */ + int processed; /* # of processed bytes - when suspended */ + const XML_Char *systemId; + const XML_Char *base; + const XML_Char *publicId; + const XML_Char *notation; + XML_Bool open; + XML_Bool is_param; + XML_Bool is_internal; /* true if declared in internal subset outside PE */ +} ENTITY; + +typedef struct { + enum XML_Content_Type type; + enum XML_Content_Quant quant; + const XML_Char * name; + int firstchild; + int lastchild; + int childcnt; + int nextsib; +} CONTENT_SCAFFOLD; + +#define INIT_SCAFFOLD_ELEMENTS 32 + +typedef struct block { + struct block *next; + int size; + XML_Char s[1]; +} BLOCK; + +typedef struct { + BLOCK *blocks; + BLOCK *freeBlocks; + const XML_Char *end; + XML_Char *ptr; + XML_Char *start; + const XML_Memory_Handling_Suite *mem; +} STRING_POOL; + +/* The XML_Char before the name is used to determine whether + an attribute has been specified. */ +typedef struct attribute_id { + XML_Char *name; + PREFIX *prefix; + XML_Bool maybeTokenized; + XML_Bool xmlns; +} ATTRIBUTE_ID; + +typedef struct { + const ATTRIBUTE_ID *id; + XML_Bool isCdata; + const XML_Char *value; +} DEFAULT_ATTRIBUTE; + +typedef struct { + unsigned long version; + unsigned long hash; + const XML_Char *uriName; +} NS_ATT; + +typedef struct { + const XML_Char *name; + PREFIX *prefix; + const ATTRIBUTE_ID *idAtt; + int nDefaultAtts; + int allocDefaultAtts; + DEFAULT_ATTRIBUTE *defaultAtts; +} ELEMENT_TYPE; + +typedef struct { + HASH_TABLE generalEntities; + HASH_TABLE elementTypes; + HASH_TABLE attributeIds; + HASH_TABLE prefixes; + STRING_POOL pool; + STRING_POOL entityValuePool; + /* false once a parameter entity reference has been skipped */ + XML_Bool keepProcessing; + /* true once an internal or external PE reference has been encountered; + this includes the reference to an external subset */ + XML_Bool hasParamEntityRefs; + XML_Bool standalone; +#ifdef XML_DTD + /* indicates if external PE has been read */ + XML_Bool paramEntityRead; + HASH_TABLE paramEntities; +#endif /* XML_DTD */ + PREFIX defaultPrefix; + /* === scaffolding for building content model === */ + XML_Bool in_eldecl; + CONTENT_SCAFFOLD *scaffold; + unsigned contentStringLen; + unsigned scaffSize; + unsigned scaffCount; + int scaffLevel; + int *scaffIndex; +} DTD; + +typedef struct open_internal_entity { + const char *internalEventPtr; + const char *internalEventEndPtr; + struct open_internal_entity *next; + ENTITY *entity; + int startTagLevel; + XML_Bool betweenDecl; /* WFC: PE Between Declarations */ +} OPEN_INTERNAL_ENTITY; + +typedef enum XML_Error PTRCALL Processor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr); + +static Processor prologProcessor; +static Processor prologInitProcessor; +static Processor contentProcessor; +static Processor cdataSectionProcessor; +#ifdef XML_DTD +static Processor ignoreSectionProcessor; +static Processor externalParEntProcessor; +static Processor externalParEntInitProcessor; +static Processor entityValueProcessor; +static Processor entityValueInitProcessor; +#endif /* XML_DTD */ +static Processor epilogProcessor; +static Processor errorProcessor; +static Processor externalEntityInitProcessor; +static Processor externalEntityInitProcessor2; +static Processor externalEntityInitProcessor3; +static Processor externalEntityContentProcessor; +static Processor internalEntityProcessor; + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName); +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next); +static enum XML_Error +initializeEncoding(XML_Parser parser); +static enum XML_Error +doProlog(XML_Parser parser, const ENCODING *enc, const char *s, + const char *end, int tok, const char *next, const char **nextPtr, + XML_Bool haveMore); +static enum XML_Error +processInternalEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl); +static enum XML_Error +doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + const char *start, const char *end, const char **endPtr, + XML_Bool haveMore); +static enum XML_Error +doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr, + const char *end, const char **nextPtr, XML_Bool haveMore); +#ifdef XML_DTD +static enum XML_Error +doIgnoreSection(XML_Parser parser, const ENCODING *, const char **startPtr, + const char *end, const char **nextPtr, XML_Bool haveMore); +#endif /* XML_DTD */ + +static enum XML_Error +storeAtts(XML_Parser parser, const ENCODING *, const char *s, + TAG_NAME *tagNamePtr, BINDING **bindingsPtr); +static enum XML_Error +addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + const XML_Char *uri, BINDING **bindingsPtr); +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata, + XML_Bool isId, const XML_Char *dfltValue, XML_Parser parser); +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, + const char *, const char *, STRING_POOL *); +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, + const char *, const char *, STRING_POOL *); +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static int +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); +static enum XML_Error +storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); +static int +reportComment(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static void +reportDefault(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); + +static const XML_Char * getContext(XML_Parser parser); +static XML_Bool +setContext(XML_Parser parser, const XML_Char *context); + +static void FASTCALL normalizePublicId(XML_Char *s); + +static DTD * dtdCreate(const XML_Memory_Handling_Suite *ms); +/* do not call if parentParser != NULL */ +static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); +static void +dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms); +static int +dtdCopy(XML_Parser oldParser, + DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms); +static int +copyEntityTable(XML_Parser oldParser, + HASH_TABLE *, STRING_POOL *, const HASH_TABLE *); +static NAMED * +lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize); +static void FASTCALL +hashTableInit(HASH_TABLE *, const XML_Memory_Handling_Suite *ms); +static void FASTCALL hashTableClear(HASH_TABLE *); +static void FASTCALL hashTableDestroy(HASH_TABLE *); +static void FASTCALL +hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); +static NAMED * FASTCALL hashTableIterNext(HASH_TABLE_ITER *); + +static void FASTCALL +poolInit(STRING_POOL *, const XML_Memory_Handling_Suite *ms); +static void FASTCALL poolClear(STRING_POOL *); +static void FASTCALL poolDestroy(STRING_POOL *); +static XML_Char * +poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Char * +poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Bool FASTCALL poolGrow(STRING_POOL *pool); +static const XML_Char * FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s); +static const XML_Char * +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n); +static const XML_Char * FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s); + +static int FASTCALL nextScaffoldPart(XML_Parser parser); +static XML_Content * build_model(XML_Parser parser); +static ELEMENT_TYPE * +getElementType(XML_Parser parser, const ENCODING *enc, + const char *ptr, const char *end); + +static unsigned long generate_hash_secret_salt(void); +static XML_Bool startParsing(XML_Parser parser); + +static XML_Parser +parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, + DTD *dtd); + +static void +parserInit(XML_Parser parser, const XML_Char *encodingName); + +#define poolStart(pool) ((pool)->start) +#define poolEnd(pool) ((pool)->ptr) +#define poolLength(pool) ((pool)->ptr - (pool)->start) +#define poolChop(pool) ((void)--(pool->ptr)) +#define poolLastChar(pool) (((pool)->ptr)[-1]) +#define poolDiscard(pool) ((pool)->ptr = (pool)->start) +#define poolFinish(pool) ((pool)->start = (pool)->ptr) +#define poolAppendChar(pool, c) \ + (((pool)->ptr == (pool)->end && !poolGrow(pool)) \ + ? 0 \ + : ((*((pool)->ptr)++ = c), 1)) + +struct XML_ParserStruct { + /* The first member must be userData so that the XML_GetUserData + macro works. */ + void *m_userData; + void *m_handlerArg; + char *m_buffer; + const XML_Memory_Handling_Suite m_mem; + /* first character to be parsed */ + const char *m_bufferPtr; + /* past last character to be parsed */ + char *m_bufferEnd; + /* allocated end of buffer */ + const char *m_bufferLim; + XML_Index m_parseEndByteIndex; + const char *m_parseEndPtr; + XML_Char *m_dataBuf; + XML_Char *m_dataBufEnd; + XML_StartElementHandler m_startElementHandler; + XML_EndElementHandler m_endElementHandler; + XML_CharacterDataHandler m_characterDataHandler; + XML_ProcessingInstructionHandler m_processingInstructionHandler; + XML_CommentHandler m_commentHandler; + XML_StartCdataSectionHandler m_startCdataSectionHandler; + XML_EndCdataSectionHandler m_endCdataSectionHandler; + XML_DefaultHandler m_defaultHandler; + XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler; + XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler; + XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler; + XML_NotationDeclHandler m_notationDeclHandler; + XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler; + XML_NotStandaloneHandler m_notStandaloneHandler; + XML_ExternalEntityRefHandler m_externalEntityRefHandler; + XML_Parser m_externalEntityRefHandlerArg; + XML_SkippedEntityHandler m_skippedEntityHandler; + XML_UnknownEncodingHandler m_unknownEncodingHandler; + XML_ElementDeclHandler m_elementDeclHandler; + XML_AttlistDeclHandler m_attlistDeclHandler; + XML_EntityDeclHandler m_entityDeclHandler; + XML_XmlDeclHandler m_xmlDeclHandler; + const ENCODING *m_encoding; + INIT_ENCODING m_initEncoding; + const ENCODING *m_internalEncoding; + const XML_Char *m_protocolEncodingName; + XML_Bool m_ns; + XML_Bool m_ns_triplets; + void *m_unknownEncodingMem; + void *m_unknownEncodingData; + void *m_unknownEncodingHandlerData; + void (XMLCALL *m_unknownEncodingRelease)(void *); + PROLOG_STATE m_prologState; + Processor *m_processor; + enum XML_Error m_errorCode; + const char *m_eventPtr; + const char *m_eventEndPtr; + const char *m_positionPtr; + OPEN_INTERNAL_ENTITY *m_openInternalEntities; + OPEN_INTERNAL_ENTITY *m_freeInternalEntities; + XML_Bool m_defaultExpandInternalEntities; + int m_tagLevel; + ENTITY *m_declEntity; + const XML_Char *m_doctypeName; + const XML_Char *m_doctypeSysid; + const XML_Char *m_doctypePubid; + const XML_Char *m_declAttributeType; + const XML_Char *m_declNotationName; + const XML_Char *m_declNotationPublicId; + ELEMENT_TYPE *m_declElementType; + ATTRIBUTE_ID *m_declAttributeId; + XML_Bool m_declAttributeIsCdata; + XML_Bool m_declAttributeIsId; + DTD *m_dtd; + const XML_Char *m_curBase; + TAG *m_tagStack; + TAG *m_freeTagList; + BINDING *m_inheritedBindings; + BINDING *m_freeBindingList; + int m_attsSize; + int m_nSpecifiedAtts; + int m_idAttIndex; + ATTRIBUTE *m_atts; + NS_ATT *m_nsAtts; + unsigned long m_nsAttsVersion; + unsigned char m_nsAttsPower; +#ifdef XML_ATTR_INFO + XML_AttrInfo *m_attInfo; +#endif + POSITION m_position; + STRING_POOL m_tempPool; + STRING_POOL m_temp2Pool; + char *m_groupConnector; + unsigned int m_groupSize; + XML_Char m_namespaceSeparator; + XML_Parser m_parentParser; + XML_ParsingStatus m_parsingStatus; +#ifdef XML_DTD + XML_Bool m_isParamEntity; + XML_Bool m_useForeignDTD; + enum XML_ParamEntityParsing m_paramEntityParsing; +#endif + unsigned long m_hash_secret_salt; +}; + +#define MALLOC(s) (parser->m_mem.malloc_fcn((s))) +#define REALLOC(p,s) (parser->m_mem.realloc_fcn((p),(s))) +#define FREE(p) (parser->m_mem.free_fcn((p))) + +#define userData (parser->m_userData) +#define handlerArg (parser->m_handlerArg) +#define startElementHandler (parser->m_startElementHandler) +#define endElementHandler (parser->m_endElementHandler) +#define characterDataHandler (parser->m_characterDataHandler) +#define processingInstructionHandler \ + (parser->m_processingInstructionHandler) +#define commentHandler (parser->m_commentHandler) +#define startCdataSectionHandler \ + (parser->m_startCdataSectionHandler) +#define endCdataSectionHandler (parser->m_endCdataSectionHandler) +#define defaultHandler (parser->m_defaultHandler) +#define startDoctypeDeclHandler (parser->m_startDoctypeDeclHandler) +#define endDoctypeDeclHandler (parser->m_endDoctypeDeclHandler) +#define unparsedEntityDeclHandler \ + (parser->m_unparsedEntityDeclHandler) +#define notationDeclHandler (parser->m_notationDeclHandler) +#define startNamespaceDeclHandler \ + (parser->m_startNamespaceDeclHandler) +#define endNamespaceDeclHandler (parser->m_endNamespaceDeclHandler) +#define notStandaloneHandler (parser->m_notStandaloneHandler) +#define externalEntityRefHandler \ + (parser->m_externalEntityRefHandler) +#define externalEntityRefHandlerArg \ + (parser->m_externalEntityRefHandlerArg) +#define internalEntityRefHandler \ + (parser->m_internalEntityRefHandler) +#define skippedEntityHandler (parser->m_skippedEntityHandler) +#define unknownEncodingHandler (parser->m_unknownEncodingHandler) +#define elementDeclHandler (parser->m_elementDeclHandler) +#define attlistDeclHandler (parser->m_attlistDeclHandler) +#define entityDeclHandler (parser->m_entityDeclHandler) +#define xmlDeclHandler (parser->m_xmlDeclHandler) +#define encoding (parser->m_encoding) +#define initEncoding (parser->m_initEncoding) +#define internalEncoding (parser->m_internalEncoding) +#define unknownEncodingMem (parser->m_unknownEncodingMem) +#define unknownEncodingData (parser->m_unknownEncodingData) +#define unknownEncodingHandlerData \ + (parser->m_unknownEncodingHandlerData) +#define unknownEncodingRelease (parser->m_unknownEncodingRelease) +#define protocolEncodingName (parser->m_protocolEncodingName) +#define ns (parser->m_ns) +#define ns_triplets (parser->m_ns_triplets) +#define prologState (parser->m_prologState) +#define processor (parser->m_processor) +#define errorCode (parser->m_errorCode) +#define eventPtr (parser->m_eventPtr) +#define eventEndPtr (parser->m_eventEndPtr) +#define positionPtr (parser->m_positionPtr) +#define position (parser->m_position) +#define openInternalEntities (parser->m_openInternalEntities) +#define freeInternalEntities (parser->m_freeInternalEntities) +#define defaultExpandInternalEntities \ + (parser->m_defaultExpandInternalEntities) +#define tagLevel (parser->m_tagLevel) +#define buffer (parser->m_buffer) +#define bufferPtr (parser->m_bufferPtr) +#define bufferEnd (parser->m_bufferEnd) +#define parseEndByteIndex (parser->m_parseEndByteIndex) +#define parseEndPtr (parser->m_parseEndPtr) +#define bufferLim (parser->m_bufferLim) +#define dataBuf (parser->m_dataBuf) +#define dataBufEnd (parser->m_dataBufEnd) +#define _dtd (parser->m_dtd) +#define curBase (parser->m_curBase) +#define declEntity (parser->m_declEntity) +#define doctypeName (parser->m_doctypeName) +#define doctypeSysid (parser->m_doctypeSysid) +#define doctypePubid (parser->m_doctypePubid) +#define declAttributeType (parser->m_declAttributeType) +#define declNotationName (parser->m_declNotationName) +#define declNotationPublicId (parser->m_declNotationPublicId) +#define declElementType (parser->m_declElementType) +#define declAttributeId (parser->m_declAttributeId) +#define declAttributeIsCdata (parser->m_declAttributeIsCdata) +#define declAttributeIsId (parser->m_declAttributeIsId) +#define freeTagList (parser->m_freeTagList) +#define freeBindingList (parser->m_freeBindingList) +#define inheritedBindings (parser->m_inheritedBindings) +#define tagStack (parser->m_tagStack) +#define atts (parser->m_atts) +#define attsSize (parser->m_attsSize) +#define nSpecifiedAtts (parser->m_nSpecifiedAtts) +#define idAttIndex (parser->m_idAttIndex) +#define nsAtts (parser->m_nsAtts) +#define nsAttsVersion (parser->m_nsAttsVersion) +#define nsAttsPower (parser->m_nsAttsPower) +#define attInfo (parser->m_attInfo) +#define tempPool (parser->m_tempPool) +#define temp2Pool (parser->m_temp2Pool) +#define groupConnector (parser->m_groupConnector) +#define groupSize (parser->m_groupSize) +#define namespaceSeparator (parser->m_namespaceSeparator) +#define parentParser (parser->m_parentParser) +#define ps_parsing (parser->m_parsingStatus.parsing) +#define ps_finalBuffer (parser->m_parsingStatus.finalBuffer) +#ifdef XML_DTD +#define isParamEntity (parser->m_isParamEntity) +#define useForeignDTD (parser->m_useForeignDTD) +#define paramEntityParsing (parser->m_paramEntityParsing) +#endif /* XML_DTD */ +#define hash_secret_salt (parser->m_hash_secret_salt) + +XML_Parser XMLCALL +XML_ParserCreate(const XML_Char *encodingName) +{ + return XML_ParserCreate_MM(encodingName, NULL, NULL); +} + +XML_Parser XMLCALL +XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) +{ + XML_Char tmp[2]; + *tmp = nsSep; + return XML_ParserCreate_MM(encodingName, NULL, tmp); +} + +static const XML_Char implicitContext[] = { + ASCII_x, ASCII_m, ASCII_l, ASCII_EQUALS, ASCII_h, ASCII_t, ASCII_t, ASCII_p, + ASCII_COLON, ASCII_SLASH, ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, + ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, ASCII_g, + ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, ASCII_SLASH, ASCII_1, ASCII_9, + ASCII_9, ASCII_8, ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, ASCII_e, + ASCII_s, ASCII_p, ASCII_a, ASCII_c, ASCII_e, '\0' +}; + +static unsigned long +generate_hash_secret_salt(void) +{ + unsigned int seed = time(NULL) % UINT_MAX; + srand(seed); + return rand(); +} + +static XML_Bool /* only valid for root parser */ +startParsing(XML_Parser parser) +{ + /* hash functions must be initialized before setContext() is called */ + if (hash_secret_salt == 0) + hash_secret_salt = generate_hash_secret_salt(); + if (ns) { + /* implicit context only set for root parser, since child + parsers (i.e. external entity parsers) will inherit it + */ + return setContext(parser, implicitContext); + } + return XML_TRUE; +} + +XML_Parser XMLCALL +XML_ParserCreate_MM(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep) +{ + return parserCreate(encodingName, memsuite, nameSep, NULL); +} + +static XML_Parser +parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, + DTD *dtd) +{ + XML_Parser parser; + + if (memsuite) { + XML_Memory_Handling_Suite *mtemp; + parser = (XML_Parser) + memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = memsuite->malloc_fcn; + mtemp->realloc_fcn = memsuite->realloc_fcn; + mtemp->free_fcn = memsuite->free_fcn; + } + } + else { + XML_Memory_Handling_Suite *mtemp; + parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = malloc; + mtemp->realloc_fcn = realloc; + mtemp->free_fcn = free; + } + } + + if (!parser) + return parser; + + buffer = NULL; + bufferLim = NULL; + + attsSize = INIT_ATTS_SIZE; + atts = (ATTRIBUTE *)MALLOC(attsSize * sizeof(ATTRIBUTE)); + if (atts == NULL) { + FREE(parser); + return NULL; + } +#ifdef XML_ATTR_INFO + attInfo = (XML_AttrInfo*)MALLOC(attsSize * sizeof(XML_AttrInfo)); + if (attInfo == NULL) { + FREE(atts); + FREE(parser); + return NULL; + } +#endif + dataBuf = (XML_Char *)MALLOC(INIT_DATA_BUF_SIZE * sizeof(XML_Char)); + if (dataBuf == NULL) { + FREE(atts); +#ifdef XML_ATTR_INFO + FREE(attInfo); +#endif + FREE(parser); + return NULL; + } + dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE; + + if (dtd) + _dtd = dtd; + else { + _dtd = dtdCreate(&parser->m_mem); + if (_dtd == NULL) { + FREE(dataBuf); + FREE(atts); +#ifdef XML_ATTR_INFO + FREE(attInfo); +#endif + FREE(parser); + return NULL; + } + } + + freeBindingList = NULL; + freeTagList = NULL; + freeInternalEntities = NULL; + + groupSize = 0; + groupConnector = NULL; + + unknownEncodingHandler = NULL; + unknownEncodingHandlerData = NULL; + + namespaceSeparator = ASCII_EXCL; + ns = XML_FALSE; + ns_triplets = XML_FALSE; + + nsAtts = NULL; + nsAttsVersion = 0; + nsAttsPower = 0; + + poolInit(&tempPool, &(parser->m_mem)); + poolInit(&temp2Pool, &(parser->m_mem)); + parserInit(parser, encodingName); + + if (encodingName && !protocolEncodingName) { + XML_ParserFree(parser); + return NULL; + } + + if (nameSep) { + ns = XML_TRUE; + internalEncoding = XmlGetInternalEncodingNS(); + namespaceSeparator = *nameSep; + } + else { + internalEncoding = XmlGetInternalEncoding(); + } + + return parser; +} + +static void +parserInit(XML_Parser parser, const XML_Char *encodingName) +{ + processor = prologInitProcessor; + XmlPrologStateInit(&prologState); + protocolEncodingName = (encodingName != NULL + ? poolCopyString(&tempPool, encodingName) + : NULL); + curBase = NULL; + XmlInitEncoding(&initEncoding, &encoding, 0); + userData = NULL; + handlerArg = NULL; + startElementHandler = NULL; + endElementHandler = NULL; + characterDataHandler = NULL; + processingInstructionHandler = NULL; + commentHandler = NULL; + startCdataSectionHandler = NULL; + endCdataSectionHandler = NULL; + defaultHandler = NULL; + startDoctypeDeclHandler = NULL; + endDoctypeDeclHandler = NULL; + unparsedEntityDeclHandler = NULL; + notationDeclHandler = NULL; + startNamespaceDeclHandler = NULL; + endNamespaceDeclHandler = NULL; + notStandaloneHandler = NULL; + externalEntityRefHandler = NULL; + externalEntityRefHandlerArg = parser; + skippedEntityHandler = NULL; + elementDeclHandler = NULL; + attlistDeclHandler = NULL; + entityDeclHandler = NULL; + xmlDeclHandler = NULL; + bufferPtr = buffer; + bufferEnd = buffer; + parseEndByteIndex = 0; + parseEndPtr = NULL; + declElementType = NULL; + declAttributeId = NULL; + declEntity = NULL; + doctypeName = NULL; + doctypeSysid = NULL; + doctypePubid = NULL; + declAttributeType = NULL; + declNotationName = NULL; + declNotationPublicId = NULL; + declAttributeIsCdata = XML_FALSE; + declAttributeIsId = XML_FALSE; + memset(&position, 0, sizeof(POSITION)); + errorCode = XML_ERROR_NONE; + eventPtr = NULL; + eventEndPtr = NULL; + positionPtr = NULL; + openInternalEntities = NULL; + defaultExpandInternalEntities = XML_TRUE; + tagLevel = 0; + tagStack = NULL; + inheritedBindings = NULL; + nSpecifiedAtts = 0; + unknownEncodingMem = NULL; + unknownEncodingRelease = NULL; + unknownEncodingData = NULL; + parentParser = NULL; + ps_parsing = XML_INITIALIZED; +#ifdef XML_DTD + isParamEntity = XML_FALSE; + useForeignDTD = XML_FALSE; + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif + hash_secret_salt = 0; +} + +/* moves list of bindings to freeBindingList */ +static void FASTCALL +moveToFreeBindingList(XML_Parser parser, BINDING *bindings) +{ + while (bindings) { + BINDING *b = bindings; + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + } +} + +XML_Bool XMLCALL +XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) +{ + TAG *tStk; + OPEN_INTERNAL_ENTITY *openEntityList; + if (parentParser) + return XML_FALSE; + /* move tagStack to freeTagList */ + tStk = tagStack; + while (tStk) { + TAG *tag = tStk; + tStk = tStk->parent; + tag->parent = freeTagList; + moveToFreeBindingList(parser, tag->bindings); + tag->bindings = NULL; + freeTagList = tag; + } + /* move openInternalEntities to freeInternalEntities */ + openEntityList = openInternalEntities; + while (openEntityList) { + OPEN_INTERNAL_ENTITY *openEntity = openEntityList; + openEntityList = openEntity->next; + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + moveToFreeBindingList(parser, inheritedBindings); + FREE(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + poolClear(&tempPool); + poolClear(&temp2Pool); + parserInit(parser, encodingName); + dtdReset(_dtd, &parser->m_mem); + return XML_TRUE; +} + +enum XML_Status XMLCALL +XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + /* Block after XML_Parse()/XML_ParseBuffer() has been called. + XXX There's no way for the caller to determine which of the + XXX possible error cases caused the XML_STATUS_ERROR return. + */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return XML_STATUS_ERROR; + if (encodingName == NULL) + protocolEncodingName = NULL; + else { + protocolEncodingName = poolCopyString(&tempPool, encodingName); + if (!protocolEncodingName) + return XML_STATUS_ERROR; + } + return XML_STATUS_OK; +} + +XML_Parser XMLCALL +XML_ExternalEntityParserCreate(XML_Parser oldParser, + const XML_Char *context, + const XML_Char *encodingName) +{ + XML_Parser parser = oldParser; + DTD *newDtd = NULL; + DTD *oldDtd = _dtd; + XML_StartElementHandler oldStartElementHandler = startElementHandler; + XML_EndElementHandler oldEndElementHandler = endElementHandler; + XML_CharacterDataHandler oldCharacterDataHandler = characterDataHandler; + XML_ProcessingInstructionHandler oldProcessingInstructionHandler + = processingInstructionHandler; + XML_CommentHandler oldCommentHandler = commentHandler; + XML_StartCdataSectionHandler oldStartCdataSectionHandler + = startCdataSectionHandler; + XML_EndCdataSectionHandler oldEndCdataSectionHandler + = endCdataSectionHandler; + XML_DefaultHandler oldDefaultHandler = defaultHandler; + XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler + = unparsedEntityDeclHandler; + XML_NotationDeclHandler oldNotationDeclHandler = notationDeclHandler; + XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler + = startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler + = endNamespaceDeclHandler; + XML_NotStandaloneHandler oldNotStandaloneHandler = notStandaloneHandler; + XML_ExternalEntityRefHandler oldExternalEntityRefHandler + = externalEntityRefHandler; + XML_SkippedEntityHandler oldSkippedEntityHandler = skippedEntityHandler; + XML_UnknownEncodingHandler oldUnknownEncodingHandler + = unknownEncodingHandler; + XML_ElementDeclHandler oldElementDeclHandler = elementDeclHandler; + XML_AttlistDeclHandler oldAttlistDeclHandler = attlistDeclHandler; + XML_EntityDeclHandler oldEntityDeclHandler = entityDeclHandler; + XML_XmlDeclHandler oldXmlDeclHandler = xmlDeclHandler; + ELEMENT_TYPE * oldDeclElementType = declElementType; + + void *oldUserData = userData; + void *oldHandlerArg = handlerArg; + XML_Bool oldDefaultExpandInternalEntities = defaultExpandInternalEntities; + XML_Parser oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg; +#ifdef XML_DTD + enum XML_ParamEntityParsing oldParamEntityParsing = paramEntityParsing; + int oldInEntityValue = prologState.inEntityValue; +#endif + XML_Bool oldns_triplets = ns_triplets; + /* Note that the new parser shares the same hash secret as the old + parser, so that dtdCopy and copyEntityTable can lookup values + from hash tables associated with either parser without us having + to worry which hash secrets each table has. + */ + unsigned long oldhash_secret_salt = hash_secret_salt; + +#ifdef XML_DTD + if (!context) + newDtd = oldDtd; +#endif /* XML_DTD */ + + /* Note that the magical uses of the pre-processor to make field + access look more like C++ require that `parser' be overwritten + here. This makes this function more painful to follow than it + would be otherwise. + */ + if (ns) { + XML_Char tmp[2]; + *tmp = namespaceSeparator; + parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); + } + else { + parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); + } + + if (!parser) + return NULL; + + startElementHandler = oldStartElementHandler; + endElementHandler = oldEndElementHandler; + characterDataHandler = oldCharacterDataHandler; + processingInstructionHandler = oldProcessingInstructionHandler; + commentHandler = oldCommentHandler; + startCdataSectionHandler = oldStartCdataSectionHandler; + endCdataSectionHandler = oldEndCdataSectionHandler; + defaultHandler = oldDefaultHandler; + unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler; + notationDeclHandler = oldNotationDeclHandler; + startNamespaceDeclHandler = oldStartNamespaceDeclHandler; + endNamespaceDeclHandler = oldEndNamespaceDeclHandler; + notStandaloneHandler = oldNotStandaloneHandler; + externalEntityRefHandler = oldExternalEntityRefHandler; + skippedEntityHandler = oldSkippedEntityHandler; + unknownEncodingHandler = oldUnknownEncodingHandler; + elementDeclHandler = oldElementDeclHandler; + attlistDeclHandler = oldAttlistDeclHandler; + entityDeclHandler = oldEntityDeclHandler; + xmlDeclHandler = oldXmlDeclHandler; + declElementType = oldDeclElementType; + userData = oldUserData; + if (oldUserData == oldHandlerArg) + handlerArg = userData; + else + handlerArg = parser; + if (oldExternalEntityRefHandlerArg != oldParser) + externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg; + defaultExpandInternalEntities = oldDefaultExpandInternalEntities; + ns_triplets = oldns_triplets; + hash_secret_salt = oldhash_secret_salt; + parentParser = oldParser; +#ifdef XML_DTD + paramEntityParsing = oldParamEntityParsing; + prologState.inEntityValue = oldInEntityValue; + if (context) { +#endif /* XML_DTD */ + if (!dtdCopy(oldParser, _dtd, oldDtd, &parser->m_mem) + || !setContext(parser, context)) { + XML_ParserFree(parser); + return NULL; + } + processor = externalEntityInitProcessor; +#ifdef XML_DTD + } + else { + /* The DTD instance referenced by _dtd is shared between the document's + root parser and external PE parsers, therefore one does not need to + call setContext. In addition, one also *must* not call setContext, + because this would overwrite existing prefix->binding pointers in + _dtd with ones that get destroyed with the external PE parser. + This would leave those prefixes with dangling pointers. + */ + isParamEntity = XML_TRUE; + XmlPrologStateInitExternalEntity(&prologState); + processor = externalParEntInitProcessor; + } +#endif /* XML_DTD */ + return parser; +} + +static void FASTCALL +destroyBindings(BINDING *bindings, XML_Parser parser) +{ + for (;;) { + BINDING *b = bindings; + if (!b) + break; + bindings = b->nextTagBinding; + FREE(b->uri); + FREE(b); + } +} + +void XMLCALL +XML_ParserFree(XML_Parser parser) +{ + TAG *tagList; + OPEN_INTERNAL_ENTITY *entityList; + if (parser == NULL) + return; + /* free tagStack and freeTagList */ + tagList = tagStack; + for (;;) { + TAG *p; + if (tagList == NULL) { + if (freeTagList == NULL) + break; + tagList = freeTagList; + freeTagList = NULL; + } + p = tagList; + tagList = tagList->parent; + FREE(p->buf); + destroyBindings(p->bindings, parser); + FREE(p); + } + /* free openInternalEntities and freeInternalEntities */ + entityList = openInternalEntities; + for (;;) { + OPEN_INTERNAL_ENTITY *openEntity; + if (entityList == NULL) { + if (freeInternalEntities == NULL) + break; + entityList = freeInternalEntities; + freeInternalEntities = NULL; + } + openEntity = entityList; + entityList = entityList->next; + FREE(openEntity); + } + + destroyBindings(freeBindingList, parser); + destroyBindings(inheritedBindings, parser); + poolDestroy(&tempPool); + poolDestroy(&temp2Pool); +#ifdef XML_DTD + /* external parameter entity parsers share the DTD structure + parser->m_dtd with the root parser, so we must not destroy it + */ + if (!isParamEntity && _dtd) +#else + if (_dtd) +#endif /* XML_DTD */ + dtdDestroy(_dtd, (XML_Bool)!parentParser, &parser->m_mem); + FREE((void *)atts); +#ifdef XML_ATTR_INFO + FREE((void *)attInfo); +#endif + FREE(groupConnector); + FREE(buffer); + FREE(dataBuf); + FREE(nsAtts); + FREE(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + FREE(parser); +} + +void XMLCALL +XML_UseParserAsHandlerArg(XML_Parser parser) +{ + handlerArg = parser; +} + +enum XML_Error XMLCALL +XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) +{ +#ifdef XML_DTD + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING; + useForeignDTD = useDTD; + return XML_ERROR_NONE; +#else + return XML_ERROR_FEATURE_REQUIRES_XML_DTD; +#endif +} + +void XMLCALL +XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return; + ns_triplets = do_nst ? XML_TRUE : XML_FALSE; +} + +void XMLCALL +XML_SetUserData(XML_Parser parser, void *p) +{ + if (handlerArg == userData) + handlerArg = userData = p; + else + userData = p; +} + +enum XML_Status XMLCALL +XML_SetBase(XML_Parser parser, const XML_Char *p) +{ + if (p) { + p = poolCopyString(&_dtd->pool, p); + if (!p) + return XML_STATUS_ERROR; + curBase = p; + } + else + curBase = NULL; + return XML_STATUS_OK; +} + +const XML_Char * XMLCALL +XML_GetBase(XML_Parser parser) +{ + return curBase; +} + +int XMLCALL +XML_GetSpecifiedAttributeCount(XML_Parser parser) +{ + return nSpecifiedAtts; +} + +int XMLCALL +XML_GetIdAttributeIndex(XML_Parser parser) +{ + return idAttIndex; +} + +#ifdef XML_ATTR_INFO +const XML_AttrInfo * XMLCALL +XML_GetAttributeInfo(XML_Parser parser) +{ + return attInfo; +} +#endif + +void XMLCALL +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end) +{ + startElementHandler = start; + endElementHandler = end; +} + +void XMLCALL +XML_SetStartElementHandler(XML_Parser parser, + XML_StartElementHandler start) { + startElementHandler = start; +} + +void XMLCALL +XML_SetEndElementHandler(XML_Parser parser, + XML_EndElementHandler end) { + endElementHandler = end; +} + +void XMLCALL +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler) +{ + characterDataHandler = handler; +} + +void XMLCALL +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler) +{ + processingInstructionHandler = handler; +} + +void XMLCALL +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler) +{ + commentHandler = handler; +} + +void XMLCALL +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end) +{ + startCdataSectionHandler = start; + endCdataSectionHandler = end; +} + +void XMLCALL +XML_SetStartCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start) { + startCdataSectionHandler = start; +} + +void XMLCALL +XML_SetEndCdataSectionHandler(XML_Parser parser, + XML_EndCdataSectionHandler end) { + endCdataSectionHandler = end; +} + +void XMLCALL +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = XML_FALSE; +} + +void XMLCALL +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = XML_TRUE; +} + +void XMLCALL +XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end) +{ + startDoctypeDeclHandler = start; + endDoctypeDeclHandler = end; +} + +void XMLCALL +XML_SetStartDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start) { + startDoctypeDeclHandler = start; +} + +void XMLCALL +XML_SetEndDoctypeDeclHandler(XML_Parser parser, + XML_EndDoctypeDeclHandler end) { + endDoctypeDeclHandler = end; +} + +void XMLCALL +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler) +{ + unparsedEntityDeclHandler = handler; +} + +void XMLCALL +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler) +{ + notationDeclHandler = handler; +} + +void XMLCALL +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end) +{ + startNamespaceDeclHandler = start; + endNamespaceDeclHandler = end; +} + +void XMLCALL +XML_SetStartNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start) { + startNamespaceDeclHandler = start; +} + +void XMLCALL +XML_SetEndNamespaceDeclHandler(XML_Parser parser, + XML_EndNamespaceDeclHandler end) { + endNamespaceDeclHandler = end; +} + +void XMLCALL +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler) +{ + notStandaloneHandler = handler; +} + +void XMLCALL +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler) +{ + externalEntityRefHandler = handler; +} + +void XMLCALL +XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg) +{ + if (arg) + externalEntityRefHandlerArg = (XML_Parser)arg; + else + externalEntityRefHandlerArg = parser; +} + +void XMLCALL +XML_SetSkippedEntityHandler(XML_Parser parser, + XML_SkippedEntityHandler handler) +{ + skippedEntityHandler = handler; +} + +void XMLCALL +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *data) +{ + unknownEncodingHandler = handler; + unknownEncodingHandlerData = data; +} + +void XMLCALL +XML_SetElementDeclHandler(XML_Parser parser, + XML_ElementDeclHandler eldecl) +{ + elementDeclHandler = eldecl; +} + +void XMLCALL +XML_SetAttlistDeclHandler(XML_Parser parser, + XML_AttlistDeclHandler attdecl) +{ + attlistDeclHandler = attdecl; +} + +void XMLCALL +XML_SetEntityDeclHandler(XML_Parser parser, + XML_EntityDeclHandler handler) +{ + entityDeclHandler = handler; +} + +void XMLCALL +XML_SetXmlDeclHandler(XML_Parser parser, + XML_XmlDeclHandler handler) { + xmlDeclHandler = handler; +} + +int XMLCALL +XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing peParsing) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return 0; +#ifdef XML_DTD + paramEntityParsing = peParsing; + return 1; +#else + return peParsing == XML_PARAM_ENTITY_PARSING_NEVER; +#endif +} + +int XMLCALL +XML_SetHashSalt(XML_Parser parser, + unsigned long hash_salt) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return 0; + hash_secret_salt = hash_salt; + return 1; +} + +enum XML_Status XMLCALL +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + case XML_INITIALIZED: + if (parentParser == NULL && !startParsing(parser)) { + errorCode = XML_ERROR_NO_MEMORY; + return XML_STATUS_ERROR; + } + default: + ps_parsing = XML_PARSING; + } + + if (len == 0) { + ps_finalBuffer = (XML_Bool)isFinal; + if (!isFinal) + return XML_STATUS_OK; + positionPtr = bufferPtr; + parseEndPtr = bufferEnd; + + /* If data are left over from last buffer, and we now know that these + data are the final chunk of input, then we have to check them again + to detect errors based on that fact. + */ + errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); + + if (errorCode == XML_ERROR_NONE) { + switch (ps_parsing) { + case XML_SUSPENDED: + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return XML_STATUS_SUSPENDED; + case XML_INITIALIZED: + case XML_PARSING: + ps_parsing = XML_FINISHED; + /* fall through */ + default: + return XML_STATUS_OK; + } + } + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } +#ifndef XML_CONTEXT_BYTES + else if (bufferPtr == bufferEnd) { + const char *end; + int nLeftOver; + enum XML_Error result; + parseEndByteIndex += len; + positionPtr = s; + ps_finalBuffer = (XML_Bool)isFinal; + + errorCode = processor(parser, s, parseEndPtr = s + len, &end); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (isFinal) { + ps_parsing = XML_FINISHED; + return XML_STATUS_OK; + } + /* fall through */ + default: + result = XML_STATUS_OK; + } + } + + XmlUpdatePosition(encoding, positionPtr, end, &position); + nLeftOver = s + len - end; + if (nLeftOver) { + if (buffer == NULL || nLeftOver > bufferLim - buffer) { + /* FIXME avoid integer overflow */ + char *temp; + temp = (buffer == NULL + ? (char *)MALLOC(len * 2) + : (char *)REALLOC(buffer, len * 2)); + if (temp == NULL) { + errorCode = XML_ERROR_NO_MEMORY; + eventPtr = eventEndPtr = NULL; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + buffer = temp; + bufferLim = buffer + len * 2; + } + memcpy(buffer, end, nLeftOver); + } + bufferPtr = buffer; + bufferEnd = buffer + nLeftOver; + positionPtr = bufferPtr; + parseEndPtr = bufferEnd; + eventPtr = bufferPtr; + eventEndPtr = bufferPtr; + return result; + } +#endif /* not defined XML_CONTEXT_BYTES */ + else { + void *buff = XML_GetBuffer(parser, len); + if (buff == NULL) + return XML_STATUS_ERROR; + else { + memcpy(buff, s, len); + return XML_ParseBuffer(parser, len, isFinal); + } + } +} + +enum XML_Status XMLCALL +XML_ParseBuffer(XML_Parser parser, int len, int isFinal) +{ + const char *start; + enum XML_Status result = XML_STATUS_OK; + + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + case XML_INITIALIZED: + if (parentParser == NULL && !startParsing(parser)) { + errorCode = XML_ERROR_NO_MEMORY; + return XML_STATUS_ERROR; + } + default: + ps_parsing = XML_PARSING; + } + + start = bufferPtr; + positionPtr = start; + bufferEnd += len; + parseEndPtr = bufferEnd; + parseEndByteIndex += len; + ps_finalBuffer = (XML_Bool)isFinal; + + errorCode = processor(parser, start, parseEndPtr, &bufferPtr); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (isFinal) { + ps_parsing = XML_FINISHED; + return result; + } + default: ; /* should not happen */ + } + } + + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return result; +} + +void * XMLCALL +XML_GetBuffer(XML_Parser parser, int len) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return NULL; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return NULL; + default: ; + } + + if (len > bufferLim - bufferEnd) { + /* FIXME avoid integer overflow */ + int neededSize = len + (int)(bufferEnd - bufferPtr); +#ifdef XML_CONTEXT_BYTES + int keep = (int)(bufferPtr - buffer); + + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; + neededSize += keep; +#endif /* defined XML_CONTEXT_BYTES */ + if (neededSize <= bufferLim - buffer) { +#ifdef XML_CONTEXT_BYTES + if (keep < bufferPtr - buffer) { + int offset = (int)(bufferPtr - buffer) - keep; + memmove(buffer, &buffer[offset], bufferEnd - bufferPtr + keep); + bufferEnd -= offset; + bufferPtr -= offset; + } +#else + memmove(buffer, bufferPtr, bufferEnd - bufferPtr); + bufferEnd = buffer + (bufferEnd - bufferPtr); + bufferPtr = buffer; +#endif /* not defined XML_CONTEXT_BYTES */ + } + else { + char *newBuf; + int bufferSize = (int)(bufferLim - bufferPtr); + if (bufferSize == 0) + bufferSize = INIT_BUFFER_SIZE; + do { + bufferSize *= 2; + } while (bufferSize < neededSize); + newBuf = (char *)MALLOC(bufferSize); + if (newBuf == 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } + bufferLim = newBuf + bufferSize; +#ifdef XML_CONTEXT_BYTES + if (bufferPtr) { + int keep = (int)(bufferPtr - buffer); + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; + memcpy(newBuf, &bufferPtr[-keep], bufferEnd - bufferPtr + keep); + FREE(buffer); + buffer = newBuf; + bufferEnd = buffer + (bufferEnd - bufferPtr) + keep; + bufferPtr = buffer + keep; + } + else { + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; + } +#else + if (bufferPtr) { + memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr); + FREE(buffer); + } + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; +#endif /* not defined XML_CONTEXT_BYTES */ + } + eventPtr = eventEndPtr = NULL; + positionPtr = NULL; + } + return bufferEnd; +} + +enum XML_Status XMLCALL +XML_StopParser(XML_Parser parser, XML_Bool resumable) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + if (resumable) { + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + } + ps_parsing = XML_FINISHED; + break; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + default: + if (resumable) { +#ifdef XML_DTD + if (isParamEntity) { + errorCode = XML_ERROR_SUSPEND_PE; + return XML_STATUS_ERROR; + } +#endif + ps_parsing = XML_SUSPENDED; + } + else + ps_parsing = XML_FINISHED; + } + return XML_STATUS_OK; +} + +enum XML_Status XMLCALL +XML_ResumeParser(XML_Parser parser) +{ + enum XML_Status result = XML_STATUS_OK; + + if (ps_parsing != XML_SUSPENDED) { + errorCode = XML_ERROR_NOT_SUSPENDED; + return XML_STATUS_ERROR; + } + ps_parsing = XML_PARSING; + + errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (ps_finalBuffer) { + ps_parsing = XML_FINISHED; + return result; + } + default: ; + } + } + + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return result; +} + +void XMLCALL +XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status) +{ + assert(status != NULL); + *status = parser->m_parsingStatus; +} + +enum XML_Error XMLCALL +XML_GetErrorCode(XML_Parser parser) +{ + return errorCode; +} + +XML_Index XMLCALL +XML_GetCurrentByteIndex(XML_Parser parser) +{ + if (eventPtr) + return parseEndByteIndex - (parseEndPtr - eventPtr); + return -1; +} + +int XMLCALL +XML_GetCurrentByteCount(XML_Parser parser) +{ + if (eventEndPtr && eventPtr) + return (int)(eventEndPtr - eventPtr); + return 0; +} + +const char * XMLCALL +XML_GetInputContext(XML_Parser parser, int *offset, int *size) +{ +#ifdef XML_CONTEXT_BYTES + if (eventPtr && buffer) { + *offset = (int)(eventPtr - buffer); + *size = (int)(bufferEnd - buffer); + return buffer; + } +#endif /* defined XML_CONTEXT_BYTES */ + return (char *) 0; +} + +XML_Size XMLCALL +XML_GetCurrentLineNumber(XML_Parser parser) +{ + if (eventPtr && eventPtr >= positionPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.lineNumber + 1; +} + +XML_Size XMLCALL +XML_GetCurrentColumnNumber(XML_Parser parser) +{ + if (eventPtr && eventPtr >= positionPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.columnNumber; +} + +void XMLCALL +XML_FreeContentModel(XML_Parser parser, XML_Content *model) +{ + FREE(model); +} + +void * XMLCALL +XML_MemMalloc(XML_Parser parser, size_t size) +{ + return MALLOC(size); +} + +void * XMLCALL +XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) +{ + return REALLOC(ptr, size); +} + +void XMLCALL +XML_MemFree(XML_Parser parser, void *ptr) +{ + FREE(ptr); +} + +void XMLCALL +XML_DefaultCurrent(XML_Parser parser) +{ + if (defaultHandler) { + if (openInternalEntities) + reportDefault(parser, + internalEncoding, + openInternalEntities->internalEventPtr, + openInternalEntities->internalEventEndPtr); + else + reportDefault(parser, encoding, eventPtr, eventEndPtr); + } +} + +const XML_LChar * XMLCALL +XML_ErrorString(enum XML_Error code) +{ + static const XML_LChar* const message[] = { + 0, + XML_L("out of memory"), + XML_L("syntax error"), + XML_L("no element found"), + XML_L("not well-formed (invalid token)"), + XML_L("unclosed token"), + XML_L("partial character"), + XML_L("mismatched tag"), + XML_L("duplicate attribute"), + XML_L("junk after document element"), + XML_L("illegal parameter entity reference"), + XML_L("undefined entity"), + XML_L("recursive entity reference"), + XML_L("asynchronous entity"), + XML_L("reference to invalid character number"), + XML_L("reference to binary entity"), + XML_L("reference to external entity in attribute"), + XML_L("XML or text declaration not at start of entity"), + XML_L("unknown encoding"), + XML_L("encoding specified in XML declaration is incorrect"), + XML_L("unclosed CDATA section"), + XML_L("error in processing external entity reference"), + XML_L("document is not standalone"), + XML_L("unexpected parser state - please send a bug report"), + XML_L("entity declared in parameter entity"), + XML_L("requested feature requires XML_DTD support in Expat"), + XML_L("cannot change setting once parsing has begun"), + XML_L("unbound prefix"), + XML_L("must not undeclare prefix"), + XML_L("incomplete markup in parameter entity"), + XML_L("XML declaration not well-formed"), + XML_L("text declaration not well-formed"), + XML_L("illegal character(s) in public id"), + XML_L("parser suspended"), + XML_L("parser not suspended"), + XML_L("parsing aborted"), + XML_L("parsing finished"), + XML_L("cannot suspend in external parameter entity"), + XML_L("reserved prefix (xml) must not be undeclared or bound to another namespace name"), + XML_L("reserved prefix (xmlns) must not be declared or undeclared"), + XML_L("prefix must not be bound to one of the reserved namespace names") + }; + if (code > 0 && code < sizeof(message)/sizeof(message[0])) + return message[code]; + return NULL; +} + +const XML_LChar * XMLCALL +XML_ExpatVersion(void) { + + /* V1 is used to string-ize the version number. However, it would + string-ize the actual version macro *names* unless we get them + substituted before being passed to V1. CPP is defined to expand + a macro, then rescan for more expansions. Thus, we use V2 to expand + the version macros, then CPP will expand the resulting V1() macro + with the correct numerals. */ + /* ### I'm assuming cpp is portable in this respect... */ + +#define V1(a,b,c) XML_L(#a)XML_L(".")XML_L(#b)XML_L(".")XML_L(#c) +#define V2(a,b,c) XML_L("expat_")V1(a,b,c) + + return V2(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION); + +#undef V1 +#undef V2 +} + +XML_Expat_Version XMLCALL +XML_ExpatVersionInfo(void) +{ + XML_Expat_Version version; + + version.major = XML_MAJOR_VERSION; + version.minor = XML_MINOR_VERSION; + version.micro = XML_MICRO_VERSION; + + return version; +} + +const XML_Feature * XMLCALL +XML_GetFeatureList(void) +{ + static const XML_Feature features[] = { + {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"), + sizeof(XML_Char)}, + {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"), + sizeof(XML_LChar)}, +#ifdef XML_UNICODE + {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0}, +#endif +#ifdef XML_UNICODE_WCHAR_T + {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0}, +#endif +#ifdef XML_DTD + {XML_FEATURE_DTD, XML_L("XML_DTD"), 0}, +#endif +#ifdef XML_CONTEXT_BYTES + {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), + XML_CONTEXT_BYTES}, +#endif +#ifdef XML_MIN_SIZE + {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, +#endif +#ifdef XML_NS + {XML_FEATURE_NS, XML_L("XML_NS"), 0}, +#endif +#ifdef XML_LARGE_SIZE + {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0}, +#endif +#ifdef XML_ATTR_INFO + {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0}, +#endif + {XML_FEATURE_END, NULL, 0} + }; + + return features; +} + +/* Initially tag->rawName always points into the parse buffer; + for those TAG instances opened while the current parse buffer was + processed, and not yet closed, we need to store tag->rawName in a more + permanent location, since the parse buffer is about to be discarded. +*/ +static XML_Bool +storeRawNames(XML_Parser parser) +{ + TAG *tag = tagStack; + while (tag) { + int bufSize; + int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); + char *rawNameBuf = tag->buf + nameLen; + /* Stop if already stored. Since tagStack is a stack, we can stop + at the first entry that has already been copied; everything + below it in the stack is already been accounted for in a + previous call to this function. + */ + if (tag->rawName == rawNameBuf) + break; + /* For re-use purposes we need to ensure that the + size of tag->buf is a multiple of sizeof(XML_Char). + */ + bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)); + if (bufSize > tag->bufEnd - tag->buf) { + char *temp = (char *)REALLOC(tag->buf, bufSize); + if (temp == NULL) + return XML_FALSE; + /* if tag->name.str points to tag->buf (only when namespace + processing is off) then we have to update it + */ + if (tag->name.str == (XML_Char *)tag->buf) + tag->name.str = (XML_Char *)temp; + /* if tag->name.localPart is set (when namespace processing is on) + then update it as well, since it will always point into tag->buf + */ + if (tag->name.localPart) + tag->name.localPart = (XML_Char *)temp + (tag->name.localPart - + (XML_Char *)tag->buf); + tag->buf = temp; + tag->bufEnd = temp + bufSize; + rawNameBuf = temp + nameLen; + } + memcpy(rawNameBuf, tag->rawName, tag->rawNameLength); + tag->rawName = rawNameBuf; + tag = tag->parent; + } + return XML_TRUE; +} + +static enum XML_Error PTRCALL +contentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doContent(parser, 0, encoding, start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result == XML_ERROR_NONE) { + if (!storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + } + return result; +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = externalEntityInitProcessor2; + return externalEntityInitProcessor2(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor2(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(encoding, start, end, &next); + switch (tok) { + case XML_TOK_BOM: + /* If we are at the end of the buffer, this would cause the next stage, + i.e. externalEntityInitProcessor3, to pass control directly to + doContent (by detecting XML_TOK_NONE) without processing any xml text + declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent. + */ + if (next == end && !ps_finalBuffer) { + *endPtr = next; + return XML_ERROR_NONE; + } + start = next; + break; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityInitProcessor3; + return externalEntityInitProcessor3(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor3(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + int tok; + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + eventPtr = start; + tok = XmlContentTok(encoding, start, end, &next); + eventEndPtr = next; + + switch (tok) { + case XML_TOK_XML_DECL: + { + enum XML_Error result; + result = processXmlDecl(parser, 1, start, next); + if (result != XML_ERROR_NONE) + return result; + switch (ps_parsing) { + case XML_SUSPENDED: + *endPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + start = next; + } + } + break; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityContentProcessor; + tagLevel = 1; + return externalEntityContentProcessor(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityContentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doContent(parser, 1, encoding, start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result == XML_ERROR_NONE) { + if (!storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + } + return result; +} + +static enum XML_Error +doContent(XML_Parser parser, + int startTagLevel, + const ENCODING *enc, + const char *s, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + /* save one level of indirection */ + DTD * const dtd = _dtd; + + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + + for (;;) { + const char *next = s; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_TRAILING_CR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + *eventEndPP = end; + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + /* We are at the end of the final buffer, should we check for + XML_SUSPENDED, XML_FINISHED? + */ + if (startTagLevel == 0) + return XML_ERROR_NO_ELEMENTS; + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + *nextPtr = end; + return XML_ERROR_NONE; + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (startTagLevel > 0) { + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_NO_ELEMENTS; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (characterDataHandler) + characterDataHandler(handlerArg, &ch, 1); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + name = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); + poolDiscard(&dtd->pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity or default handler. + */ + if (!dtd->hasParamEntityRefs || dtd->standalone) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->notation) + return XML_ERROR_BINARY_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + if (!defaultExpandInternalEntities) { + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, entity->name, 0); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + result = processInternalEntity(parser, entity, XML_FALSE); + if (result != XML_ERROR_NONE) + return result; + } + else if (externalEntityRefHandler) { + const XML_Char *context; + entity->open = XML_TRUE; + context = getContext(parser); + entity->open = XML_FALSE; + if (!context) + return XML_ERROR_NO_MEMORY; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + context, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + poolDiscard(&tempPool); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + case XML_TOK_START_TAG_NO_ATTS: + /* fall through */ + case XML_TOK_START_TAG_WITH_ATTS: + { + TAG *tag; + enum XML_Error result; + XML_Char *toPtr; + if (freeTagList) { + tag = freeTagList; + freeTagList = freeTagList->parent; + } + else { + tag = (TAG *)MALLOC(sizeof(TAG)); + if (!tag) + return XML_ERROR_NO_MEMORY; + tag->buf = (char *)MALLOC(INIT_TAG_BUF_SIZE); + if (!tag->buf) { + FREE(tag); + return XML_ERROR_NO_MEMORY; + } + tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE; + } + tag->bindings = NULL; + tag->parent = tagStack; + tagStack = tag; + tag->name.localPart = NULL; + tag->name.prefix = NULL; + tag->rawName = s + enc->minBytesPerChar; + tag->rawNameLength = XmlNameLength(enc, tag->rawName); + ++tagLevel; + { + const char *rawNameEnd = tag->rawName + tag->rawNameLength; + const char *fromPtr = tag->rawName; + toPtr = (XML_Char *)tag->buf; + for (;;) { + int bufSize; + int convLen; + XmlConvert(enc, + &fromPtr, rawNameEnd, + (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1); + convLen = (int)(toPtr - (XML_Char *)tag->buf); + if (fromPtr == rawNameEnd) { + tag->name.strLen = convLen; + break; + } + bufSize = (int)(tag->bufEnd - tag->buf) << 1; + { + char *temp = (char *)REALLOC(tag->buf, bufSize); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + tag->buf = temp; + tag->bufEnd = temp + bufSize; + toPtr = (XML_Char *)temp + convLen; + } + } + } + tag->name.str = (XML_Char *)tag->buf; + *toPtr = XML_T('\0'); + result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings)); + if (result) + return result; + if (startElementHandler) + startElementHandler(handlerArg, tag->name.str, + (const XML_Char **)atts); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&tempPool); + break; + } + case XML_TOK_EMPTY_ELEMENT_NO_ATTS: + /* fall through */ + case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: + { + const char *rawName = s + enc->minBytesPerChar; + enum XML_Error result; + BINDING *bindings = NULL; + XML_Bool noElmHandlers = XML_TRUE; + TAG_NAME name; + name.str = poolStoreString(&tempPool, enc, rawName, + rawName + XmlNameLength(enc, rawName)); + if (!name.str) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + result = storeAtts(parser, enc, s, &name, &bindings); + if (result) + return result; + poolFinish(&tempPool); + if (startElementHandler) { + startElementHandler(handlerArg, name.str, (const XML_Char **)atts); + noElmHandlers = XML_FALSE; + } + if (endElementHandler) { + if (startElementHandler) + *eventPP = *eventEndPP; + endElementHandler(handlerArg, name.str); + noElmHandlers = XML_FALSE; + } + if (noElmHandlers && defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&tempPool); + while (bindings) { + BINDING *b = bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + break; + case XML_TOK_END_TAG: + if (tagLevel == startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + else { + int len; + const char *rawName; + TAG *tag = tagStack; + tagStack = tag->parent; + tag->parent = freeTagList; + freeTagList = tag; + rawName = s + enc->minBytesPerChar*2; + len = XmlNameLength(enc, rawName); + if (len != tag->rawNameLength + || memcmp(tag->rawName, rawName, len) != 0) { + *eventPP = rawName; + return XML_ERROR_TAG_MISMATCH; + } + --tagLevel; + if (endElementHandler) { + const XML_Char *localPart; + const XML_Char *prefix; + XML_Char *uri; + localPart = tag->name.localPart; + if (ns && localPart) { + /* localPart and prefix may have been overwritten in + tag->name.str, since this points to the binding->uri + buffer which gets re-used; so we have to add them again + */ + uri = (XML_Char *)tag->name.str + tag->name.uriLen; + /* don't need to check for space - already done in storeAtts() */ + while (*localPart) *uri++ = *localPart++; + prefix = (XML_Char *)tag->name.prefix; + if (ns_triplets && prefix) { + *uri++ = namespaceSeparator; + while (*prefix) *uri++ = *prefix++; + } + *uri = XML_T('\0'); + } + endElementHandler(handlerArg, tag->name.str); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + while (tag->bindings) { + BINDING *b = tag->bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + tag->bindings = tag->bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + } + break; + case XML_TOK_CHAR_REF: + { + int n = XmlCharRefNumber(enc, s); + if (n < 0) + return XML_ERROR_BAD_CHAR_REF; + if (characterDataHandler) { + XML_Char buf[XML_ENCODE_MAX]; + characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_CDATA_SECT_OPEN: + { + enum XML_Error result; + if (startCdataSectionHandler) + startCdataSectionHandler(handlerArg); +#if 0 + /* Suppose you doing a transformation on a document that involves + changing only the character data. You set up a defaultHandler + and a characterDataHandler. The defaultHandler simply copies + characters through. The characterDataHandler does the + transformation and writes the characters out escaping them as + necessary. This case will fail to work if we leave out the + following two lines (because & and < inside CDATA sections will + be incorrectly escaped). + + However, now we have a start/endCdataSectionHandler, so it seems + easier to let the user deal with this. + */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore); + if (result != XML_ERROR_NONE) + return result; + else if (!next) { + processor = cdataSectionProcessor; + return result; + } + } + break; + case XML_TOK_TRAILING_RSQB: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + characterDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)end - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + /* We are at the end of the final buffer, should we check for + XML_SUSPENDED, XML_FINISHED? + */ + if (startTagLevel == 0) { + *eventPP = end; + return XML_ERROR_NO_ELEMENTS; + } + if (tagLevel != startTagLevel) { + *eventPP = end; + return XML_ERROR_ASYNC_ENTITY; + } + *nextPtr = end; + return XML_ERROR_NONE; + case XML_TOK_DATA_CHARS: + { + XML_CharacterDataHandler charDataHandler = characterDataHandler; + if (charDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + charDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + if (s == next) + break; + *eventPP = s; + } + } + else + charDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)next - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + default: + if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + *eventPP = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } + /* not reached */ +} + +/* Precondition: all arguments must be non-NULL; + Purpose: + - normalize attributes + - check attributes for well-formedness + - generate namespace aware attribute names (URI, prefix) + - build list of attributes for startElementHandler + - default attributes + - process namespace declarations (check and report them) + - generate namespace aware element name (URI, prefix) +*/ +static enum XML_Error +storeAtts(XML_Parser parser, const ENCODING *enc, + const char *attStr, TAG_NAME *tagNamePtr, + BINDING **bindingsPtr) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + ELEMENT_TYPE *elementType; + int nDefaultAtts; + const XML_Char **appAtts; /* the attribute list for the application */ + int attIndex = 0; + int prefixLen; + int i; + int n; + XML_Char *uri; + int nPrefixes = 0; + BINDING *binding; + const XML_Char *localPart; + + /* lookup the element type name */ + elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, tagNamePtr->str,0); + if (!elementType) { + const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str); + if (!name) + return XML_ERROR_NO_MEMORY; + elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name, + sizeof(ELEMENT_TYPE)); + if (!elementType) + return XML_ERROR_NO_MEMORY; + if (ns && !setElementTypePrefix(parser, elementType)) + return XML_ERROR_NO_MEMORY; + } + nDefaultAtts = elementType->nDefaultAtts; + + /* get the attributes from the tokenizer */ + n = XmlGetAttributes(enc, attStr, attsSize, atts); + if (n + nDefaultAtts > attsSize) { + int oldAttsSize = attsSize; + ATTRIBUTE *temp; +#ifdef XML_ATTR_INFO + XML_AttrInfo *temp2; +#endif + attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; + temp = (ATTRIBUTE *)REALLOC((void *)atts, attsSize * sizeof(ATTRIBUTE)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + atts = temp; +#ifdef XML_ATTR_INFO + temp2 = (XML_AttrInfo *)REALLOC((void *)attInfo, attsSize * sizeof(XML_AttrInfo)); + if (temp2 == NULL) + return XML_ERROR_NO_MEMORY; + attInfo = temp2; +#endif + if (n > oldAttsSize) + XmlGetAttributes(enc, attStr, n, atts); + } + + appAtts = (const XML_Char **)atts; + for (i = 0; i < n; i++) { + ATTRIBUTE *currAtt = &atts[i]; +#ifdef XML_ATTR_INFO + XML_AttrInfo *currAttInfo = &attInfo[i]; +#endif + /* add the name and value to the attribute list */ + ATTRIBUTE_ID *attId = getAttributeId(parser, enc, currAtt->name, + currAtt->name + + XmlNameLength(enc, currAtt->name)); + if (!attId) + return XML_ERROR_NO_MEMORY; +#ifdef XML_ATTR_INFO + currAttInfo->nameStart = parseEndByteIndex - (parseEndPtr - currAtt->name); + currAttInfo->nameEnd = currAttInfo->nameStart + + XmlNameLength(enc, currAtt->name); + currAttInfo->valueStart = parseEndByteIndex - + (parseEndPtr - currAtt->valuePtr); + currAttInfo->valueEnd = parseEndByteIndex - (parseEndPtr - currAtt->valueEnd); +#endif + /* Detect duplicate attributes by their QNames. This does not work when + namespace processing is turned on and different prefixes for the same + namespace are used. For this case we have a check further down. + */ + if ((attId->name)[-1]) { + if (enc == encoding) + eventPtr = atts[i].name; + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + (attId->name)[-1] = 1; + appAtts[attIndex++] = attId->name; + if (!atts[i].normalized) { + enum XML_Error result; + XML_Bool isCdata = XML_TRUE; + + /* figure out whether declared as other than CDATA */ + if (attId->maybeTokenized) { + int j; + for (j = 0; j < nDefaultAtts; j++) { + if (attId == elementType->defaultAtts[j].id) { + isCdata = elementType->defaultAtts[j].isCdata; + break; + } + } + } + + /* normalize the attribute value */ + result = storeAttributeValue(parser, enc, isCdata, + atts[i].valuePtr, atts[i].valueEnd, + &tempPool); + if (result) + return result; + appAtts[attIndex] = poolStart(&tempPool); + poolFinish(&tempPool); + } + else { + /* the value did not need normalizing */ + appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr, + atts[i].valueEnd); + if (appAtts[attIndex] == 0) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + } + /* handle prefixed attribute names */ + if (attId->prefix) { + if (attId->xmlns) { + /* deal with namespace declarations here */ + enum XML_Error result = addBinding(parser, attId->prefix, attId, + appAtts[attIndex], bindingsPtr); + if (result) + return result; + --attIndex; + } + else { + /* deal with other prefixed names later */ + attIndex++; + nPrefixes++; + (attId->name)[-1] = 2; + } + } + else + attIndex++; + } + + /* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */ + nSpecifiedAtts = attIndex; + if (elementType->idAtt && (elementType->idAtt->name)[-1]) { + for (i = 0; i < attIndex; i += 2) + if (appAtts[i] == elementType->idAtt->name) { + idAttIndex = i; + break; + } + } + else + idAttIndex = -1; + + /* do attribute defaulting */ + for (i = 0; i < nDefaultAtts; i++) { + const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + i; + if (!(da->id->name)[-1] && da->value) { + if (da->id->prefix) { + if (da->id->xmlns) { + enum XML_Error result = addBinding(parser, da->id->prefix, da->id, + da->value, bindingsPtr); + if (result) + return result; + } + else { + (da->id->name)[-1] = 2; + nPrefixes++; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + else { + (da->id->name)[-1] = 1; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + } + appAtts[attIndex] = 0; + + /* expand prefixed attribute names, check for duplicates, + and clear flags that say whether attributes were specified */ + i = 0; + if (nPrefixes) { + int j; /* hash table index */ + unsigned long version = nsAttsVersion; + int nsAttsSize = (int)1 << nsAttsPower; + /* size of hash table must be at least 2 * (# of prefixed attributes) */ + if ((nPrefixes << 1) >> nsAttsPower) { /* true for nsAttsPower = 0 */ + NS_ATT *temp; + /* hash table size must also be a power of 2 and >= 8 */ + while (nPrefixes >> nsAttsPower++); + if (nsAttsPower < 3) + nsAttsPower = 3; + nsAttsSize = (int)1 << nsAttsPower; + temp = (NS_ATT *)REALLOC(nsAtts, nsAttsSize * sizeof(NS_ATT)); + if (!temp) + return XML_ERROR_NO_MEMORY; + nsAtts = temp; + version = 0; /* force re-initialization of nsAtts hash table */ + } + /* using a version flag saves us from initializing nsAtts every time */ + if (!version) { /* initialize version flags when version wraps around */ + version = INIT_ATTS_VERSION; + for (j = nsAttsSize; j != 0; ) + nsAtts[--j].version = version; + } + nsAttsVersion = --version; + + /* expand prefixed names and check for duplicates */ + for (; i < attIndex; i += 2) { + const XML_Char *s = appAtts[i]; + if (s[-1] == 2) { /* prefixed */ + ATTRIBUTE_ID *id; + const BINDING *b; + unsigned long uriHash = hash_secret_salt; + ((XML_Char *)s)[-1] = 0; /* clear flag */ + id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, s, 0); + b = id->prefix->binding; + if (!b) + return XML_ERROR_UNBOUND_PREFIX; + + /* as we expand the name we also calculate its hash value */ + for (j = 0; j < b->uriLen; j++) { + const XML_Char c = b->uri[j]; + if (!poolAppendChar(&tempPool, c)) + return XML_ERROR_NO_MEMORY; + uriHash = CHAR_HASH(uriHash, c); + } + while (*s++ != XML_T(ASCII_COLON)) + ; + do { /* copies null terminator */ + const XML_Char c = *s; + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + uriHash = CHAR_HASH(uriHash, c); + } while (*s++); + + { /* Check hash table for duplicate of expanded name (uriName). + Derived from code in lookup(parser, HASH_TABLE *table, ...). + */ + unsigned char step = 0; + unsigned long mask = nsAttsSize - 1; + j = uriHash & mask; /* index into hash table */ + while (nsAtts[j].version == version) { + /* for speed we compare stored hash values first */ + if (uriHash == nsAtts[j].hash) { + const XML_Char *s1 = poolStart(&tempPool); + const XML_Char *s2 = nsAtts[j].uriName; + /* s1 is null terminated, but not s2 */ + for (; *s1 == *s2 && *s1 != 0; s1++, s2++); + if (*s1 == 0) + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + if (!step) + step = PROBE_STEP(uriHash, mask, nsAttsPower); + j < step ? (j += nsAttsSize - step) : (j -= step); + } + } + + if (ns_triplets) { /* append namespace separator and prefix */ + tempPool.ptr[-1] = namespaceSeparator; + s = b->prefix->name; + do { + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + } while (*s++); + } + + /* store expanded name in attribute list */ + s = poolStart(&tempPool); + poolFinish(&tempPool); + appAtts[i] = s; + + /* fill empty slot with new version, uriName and hash value */ + nsAtts[j].version = version; + nsAtts[j].hash = uriHash; + nsAtts[j].uriName = s; + + if (!--nPrefixes) { + i += 2; + break; + } + } + else /* not prefixed */ + ((XML_Char *)s)[-1] = 0; /* clear flag */ + } + } + /* clear flags for the remaining attributes */ + for (; i < attIndex; i += 2) + ((XML_Char *)(appAtts[i]))[-1] = 0; + for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding) + binding->attId->name[-1] = 0; + + if (!ns) + return XML_ERROR_NONE; + + /* expand the element type name */ + if (elementType->prefix) { + binding = elementType->prefix->binding; + if (!binding) + return XML_ERROR_UNBOUND_PREFIX; + localPart = tagNamePtr->str; + while (*localPart++ != XML_T(ASCII_COLON)) + ; + } + else if (dtd->defaultPrefix.binding) { + binding = dtd->defaultPrefix.binding; + localPart = tagNamePtr->str; + } + else + return XML_ERROR_NONE; + prefixLen = 0; + if (ns_triplets && binding->prefix->name) { + for (; binding->prefix->name[prefixLen++];) + ; /* prefixLen includes null terminator */ + } + tagNamePtr->localPart = localPart; + tagNamePtr->uriLen = binding->uriLen; + tagNamePtr->prefix = binding->prefix->name; + tagNamePtr->prefixLen = prefixLen; + for (i = 0; localPart[i++];) + ; /* i includes null terminator */ + n = i + binding->uriLen + prefixLen; + if (n > binding->uriAlloc) { + TAG *p; + uri = (XML_Char *)MALLOC((n + EXPAND_SPARE) * sizeof(XML_Char)); + if (!uri) + return XML_ERROR_NO_MEMORY; + binding->uriAlloc = n + EXPAND_SPARE; + memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char)); + for (p = tagStack; p; p = p->parent) + if (p->name.str == binding->uri) + p->name.str = uri; + FREE(binding->uri); + binding->uri = uri; + } + /* if namespaceSeparator != '\0' then uri includes it already */ + uri = binding->uri + binding->uriLen; + memcpy(uri, localPart, i * sizeof(XML_Char)); + /* we always have a namespace separator between localPart and prefix */ + if (prefixLen) { + uri += i - 1; + *uri = namespaceSeparator; /* replace null terminator */ + memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char)); + } + tagNamePtr->str = binding->uri; + return XML_ERROR_NONE; +} + +/* addBinding() overwrites the value of prefix->binding without checking. + Therefore one must keep track of the old value outside of addBinding(). +*/ +static enum XML_Error +addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + const XML_Char *uri, BINDING **bindingsPtr) +{ + static const XML_Char xmlNamespace[] = { + ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, + ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, + ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, + ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, ASCII_8, ASCII_SLASH, + ASCII_n, ASCII_a, ASCII_m, ASCII_e, ASCII_s, ASCII_p, ASCII_a, ASCII_c, + ASCII_e, '\0' + }; + static const int xmlLen = + (int)sizeof(xmlNamespace)/sizeof(XML_Char) - 1; + static const XML_Char xmlnsNamespace[] = { + ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, + ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, + ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_2, ASCII_0, ASCII_0, + ASCII_0, ASCII_SLASH, ASCII_x, ASCII_m, ASCII_l, ASCII_n, ASCII_s, + ASCII_SLASH, '\0' + }; + static const int xmlnsLen = + (int)sizeof(xmlnsNamespace)/sizeof(XML_Char) - 1; + + XML_Bool mustBeXML = XML_FALSE; + XML_Bool isXML = XML_TRUE; + XML_Bool isXMLNS = XML_TRUE; + + BINDING *b; + int len; + + /* empty URI is only valid for default namespace per XML NS 1.0 (not 1.1) */ + if (*uri == XML_T('\0') && prefix->name) + return XML_ERROR_UNDECLARING_PREFIX; + + if (prefix->name + && prefix->name[0] == XML_T(ASCII_x) + && prefix->name[1] == XML_T(ASCII_m) + && prefix->name[2] == XML_T(ASCII_l)) { + + /* Not allowed to bind xmlns */ + if (prefix->name[3] == XML_T(ASCII_n) + && prefix->name[4] == XML_T(ASCII_s) + && prefix->name[5] == XML_T('\0')) + return XML_ERROR_RESERVED_PREFIX_XMLNS; + + if (prefix->name[3] == XML_T('\0')) + mustBeXML = XML_TRUE; + } + + for (len = 0; uri[len]; len++) { + if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len])) + isXML = XML_FALSE; + + if (!mustBeXML && isXMLNS + && (len > xmlnsLen || uri[len] != xmlnsNamespace[len])) + isXMLNS = XML_FALSE; + } + isXML = isXML && len == xmlLen; + isXMLNS = isXMLNS && len == xmlnsLen; + + if (mustBeXML != isXML) + return mustBeXML ? XML_ERROR_RESERVED_PREFIX_XML + : XML_ERROR_RESERVED_NAMESPACE_URI; + + if (isXMLNS) + return XML_ERROR_RESERVED_NAMESPACE_URI; + + if (namespaceSeparator) + len++; + if (freeBindingList) { + b = freeBindingList; + if (len > b->uriAlloc) { + XML_Char *temp = (XML_Char *)REALLOC(b->uri, + sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + b->uri = temp; + b->uriAlloc = len + EXPAND_SPARE; + } + freeBindingList = b->nextTagBinding; + } + else { + b = (BINDING *)MALLOC(sizeof(BINDING)); + if (!b) + return XML_ERROR_NO_MEMORY; + b->uri = (XML_Char *)MALLOC(sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (!b->uri) { + FREE(b); + return XML_ERROR_NO_MEMORY; + } + b->uriAlloc = len + EXPAND_SPARE; + } + b->uriLen = len; + memcpy(b->uri, uri, len * sizeof(XML_Char)); + if (namespaceSeparator) + b->uri[len - 1] = namespaceSeparator; + b->prefix = prefix; + b->attId = attId; + b->prevPrefixBinding = prefix->binding; + /* NULL binding when default namespace undeclared */ + if (*uri == XML_T('\0') && prefix == &_dtd->defaultPrefix) + prefix->binding = NULL; + else + prefix->binding = b; + b->nextTagBinding = *bindingsPtr; + *bindingsPtr = b; + /* if attId == NULL then we are not starting a namespace scope */ + if (attId && startNamespaceDeclHandler) + startNamespaceDeclHandler(handlerArg, prefix->name, + prefix->binding ? uri : 0); + return XML_ERROR_NONE; +} + +/* The idea here is to avoid using stack for each CDATA section when + the whole file is parsed with one call. +*/ +static enum XML_Error PTRCALL +cdataSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doCdataSection(parser, encoding, &start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result != XML_ERROR_NONE) + return result; + if (start) { + if (parentParser) { /* we are parsing an external entity */ + processor = externalEntityContentProcessor; + return externalEntityContentProcessor(parser, start, end, endPtr); + } + else { + processor = contentProcessor; + return contentProcessor(parser, start, end, endPtr); + } + } + return result; +} + +/* startPtr gets set to non-null if the section is closed, and to null if + the section is not yet closed. +*/ +static enum XML_Error +doCdataSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = NULL; + + for (;;) { + const char *next; + int tok = XmlCdataSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_CDATA_SECT_CLOSE: + if (endCdataSectionHandler) + endCdataSectionHandler(handlerArg); +#if 0 + /* see comment under XML_TOK_CDATA_SECT_OPEN */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + *nextPtr = next; + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + else + return XML_ERROR_NONE; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_DATA_CHARS: + { + XML_CharacterDataHandler charDataHandler = characterDataHandler; + if (charDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = next; + charDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + if (s == next) + break; + *eventPP = s; + } + } + else + charDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)next - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_CDATA_SECTION; + default: + *eventPP = next; + return XML_ERROR_UNEXPECTED_STATE; + } + + *eventPP = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } + /* not reached */ +} + +#ifdef XML_DTD + +/* The idea here is to avoid using stack for each IGNORE section when + the whole file is parsed with one call. +*/ +static enum XML_Error PTRCALL +ignoreSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doIgnoreSection(parser, encoding, &start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result != XML_ERROR_NONE) + return result; + if (start) { + processor = prologProcessor; + return prologProcessor(parser, start, end, endPtr); + } + return result; +} + +/* startPtr gets set to non-null is the section is closed, and to null + if the section is not yet closed. +*/ +static enum XML_Error +doIgnoreSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + const char *next; + int tok; + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = NULL; + tok = XmlIgnoreSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_IGNORE_SECT: + if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + *nextPtr = next; + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + else + return XML_ERROR_NONE; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */ + default: + *eventPP = next; + return XML_ERROR_UNEXPECTED_STATE; + } + /* not reached */ +} + +#endif /* XML_DTD */ + +static enum XML_Error +initializeEncoding(XML_Parser parser) +{ + const char *s; +#ifdef XML_UNICODE + char encodingBuf[128]; + if (!protocolEncodingName) + s = NULL; + else { + int i; + for (i = 0; protocolEncodingName[i]; i++) { + if (i == sizeof(encodingBuf) - 1 + || (protocolEncodingName[i] & ~0x7f) != 0) { + encodingBuf[0] = '\0'; + break; + } + encodingBuf[i] = (char)protocolEncodingName[i]; + } + encodingBuf[i] = '\0'; + s = encodingBuf; + } +#else + s = protocolEncodingName; +#endif + if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s)) + return XML_ERROR_NONE; + return handleUnknownEncoding(parser, protocolEncodingName); +} + +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next) +{ + const char *encodingName = NULL; + const XML_Char *storedEncName = NULL; + const ENCODING *newEncoding = NULL; + const char *version = NULL; + const char *versionend; + const XML_Char *storedversion = NULL; + int standalone = -1; + if (!(ns + ? XmlParseXmlDeclNS + : XmlParseXmlDecl)(isGeneralTextEntity, + encoding, + s, + next, + &eventPtr, + &version, + &versionend, + &encodingName, + &newEncoding, + &standalone)) { + if (isGeneralTextEntity) + return XML_ERROR_TEXT_DECL; + else + return XML_ERROR_XML_DECL; + } + if (!isGeneralTextEntity && standalone == 1) { + _dtd->standalone = XML_TRUE; +#ifdef XML_DTD + if (paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE) + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif /* XML_DTD */ + } + if (xmlDeclHandler) { + if (encodingName != NULL) { + storedEncName = poolStoreString(&temp2Pool, + encoding, + encodingName, + encodingName + + XmlNameLength(encoding, encodingName)); + if (!storedEncName) + return XML_ERROR_NO_MEMORY; + poolFinish(&temp2Pool); + } + if (version) { + storedversion = poolStoreString(&temp2Pool, + encoding, + version, + versionend - encoding->minBytesPerChar); + if (!storedversion) + return XML_ERROR_NO_MEMORY; + } + xmlDeclHandler(handlerArg, storedversion, storedEncName, standalone); + } + else if (defaultHandler) + reportDefault(parser, encoding, s, next); + if (protocolEncodingName == NULL) { + if (newEncoding) { + if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) { + eventPtr = encodingName; + return XML_ERROR_INCORRECT_ENCODING; + } + encoding = newEncoding; + } + else if (encodingName) { + enum XML_Error result; + if (!storedEncName) { + storedEncName = poolStoreString( + &temp2Pool, encoding, encodingName, + encodingName + XmlNameLength(encoding, encodingName)); + if (!storedEncName) + return XML_ERROR_NO_MEMORY; + } + result = handleUnknownEncoding(parser, storedEncName); + poolClear(&temp2Pool); + if (result == XML_ERROR_UNKNOWN_ENCODING) + eventPtr = encodingName; + return result; + } + } + + if (storedEncName || storedversion) + poolClear(&temp2Pool); + + return XML_ERROR_NONE; +} + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + if (unknownEncodingHandler) { + XML_Encoding info; + int i; + for (i = 0; i < 256; i++) + info.map[i] = -1; + info.convert = NULL; + info.data = NULL; + info.release = NULL; + if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName, + &info)) { + ENCODING *enc; + unknownEncodingMem = MALLOC(XmlSizeOfUnknownEncoding()); + if (!unknownEncodingMem) { + if (info.release) + info.release(info.data); + return XML_ERROR_NO_MEMORY; + } + enc = (ns + ? XmlInitUnknownEncodingNS + : XmlInitUnknownEncoding)(unknownEncodingMem, + info.map, + info.convert, + info.data); + if (enc) { + unknownEncodingData = info.data; + unknownEncodingRelease = info.release; + encoding = enc; + return XML_ERROR_NONE; + } + } + if (info.release != NULL) + info.release(info.data); + } + return XML_ERROR_UNKNOWN_ENCODING; +} + +static enum XML_Error PTRCALL +prologInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = prologProcessor; + return prologProcessor(parser, s, end, nextPtr); +} + +#ifdef XML_DTD + +static enum XML_Error PTRCALL +externalParEntInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + + /* we know now that XML_Parse(Buffer) has been called, + so we consider the external parameter entity read */ + _dtd->paramEntityRead = XML_TRUE; + + if (prologState.inEntityValue) { + processor = entityValueInitProcessor; + return entityValueInitProcessor(parser, s, end, nextPtr); + } + else { + processor = externalParEntProcessor; + return externalParEntProcessor(parser, s, end, nextPtr); + } +} + +static enum XML_Error PTRCALL +entityValueInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + int tok; + const char *start = s; + const char *next = start; + eventPtr = start; + + for (;;) { + tok = XmlPrologTok(encoding, start, end, &next); + eventEndPtr = next; + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + /* found end of entity value - can store it now */ + return storeEntityValue(parser, encoding, s, end); + } + else if (tok == XML_TOK_XML_DECL) { + enum XML_Error result; + result = processXmlDecl(parser, 0, start, next); + if (result != XML_ERROR_NONE) + return result; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + *nextPtr = next; + } + /* stop scanning for text declaration - we found one */ + processor = entityValueProcessor; + return entityValueProcessor(parser, next, end, nextPtr); + } + /* If we are at the end of the buffer, this would cause XmlPrologTok to + return XML_TOK_NONE on the next call, which would then cause the + function to exit with *nextPtr set to s - that is what we want for other + tokens, but not for the BOM - we would rather like to skip it; + then, when this routine is entered the next time, XmlPrologTok will + return XML_TOK_INVALID, since the BOM is still in the buffer + */ + else if (tok == XML_TOK_BOM && next == end && !ps_finalBuffer) { + *nextPtr = next; + return XML_ERROR_NONE; + } + start = next; + eventPtr = start; + } +} + +static enum XML_Error PTRCALL +externalParEntProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *next = s; + int tok; + + tok = XmlPrologTok(encoding, s, end, &next); + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + } + /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM. + However, when parsing an external subset, doProlog will not accept a BOM + as valid, and report a syntax error, so we have to skip the BOM + */ + else if (tok == XML_TOK_BOM) { + s = next; + tok = XmlPrologTok(encoding, s, end, &next); + } + + processor = prologProcessor; + return doProlog(parser, encoding, s, end, tok, next, + nextPtr, (XML_Bool)!ps_finalBuffer); +} + +static enum XML_Error PTRCALL +entityValueProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *start = s; + const char *next = s; + const ENCODING *enc = encoding; + int tok; + + for (;;) { + tok = XmlPrologTok(enc, start, end, &next); + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + /* found end of entity value - can store it now */ + return storeEntityValue(parser, enc, s, end); + } + start = next; + } +} + +#endif /* XML_DTD */ + +static enum XML_Error PTRCALL +prologProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *next = s; + int tok = XmlPrologTok(encoding, s, end, &next); + return doProlog(parser, encoding, s, end, tok, next, + nextPtr, (XML_Bool)!ps_finalBuffer); +} + +static enum XML_Error +doProlog(XML_Parser parser, + const ENCODING *enc, + const char *s, + const char *end, + int tok, + const char *next, + const char **nextPtr, + XML_Bool haveMore) +{ +#ifdef XML_DTD + static const XML_Char externalSubsetName[] = { ASCII_HASH , '\0' }; +#endif /* XML_DTD */ + static const XML_Char atypeCDATA[] = + { ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; + static const XML_Char atypeID[] = { ASCII_I, ASCII_D, '\0' }; + static const XML_Char atypeIDREF[] = + { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; + static const XML_Char atypeIDREFS[] = + { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; + static const XML_Char atypeENTITY[] = + { ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; + static const XML_Char atypeENTITIES[] = { ASCII_E, ASCII_N, + ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, '\0' }; + static const XML_Char atypeNMTOKEN[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; + static const XML_Char atypeNMTOKENS[] = { ASCII_N, ASCII_M, ASCII_T, + ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, '\0' }; + static const XML_Char notationPrefix[] = { ASCII_N, ASCII_O, ASCII_T, + ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, ASCII_LPAREN, '\0' }; + static const XML_Char enumValueSep[] = { ASCII_PIPE, '\0' }; + static const XML_Char enumValueStart[] = { ASCII_LPAREN, '\0' }; + + /* save one level of indirection */ + DTD * const dtd = _dtd; + + const char **eventPP; + const char **eventEndPP; + enum XML_Content_Quant quant; + + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + + for (;;) { + int role; + XML_Bool handleDefault = XML_TRUE; + *eventPP = s; + *eventEndPP = next; + if (tok <= 0) { + if (haveMore && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case -XML_TOK_PROLOG_S: + tok = -tok; + break; + case XML_TOK_NONE: +#ifdef XML_DTD + /* for internal PE NOT referenced between declarations */ + if (enc != encoding && !openInternalEntities->betweenDecl) { + *nextPtr = s; + return XML_ERROR_NONE; + } + /* WFC: PE Between Declarations - must check that PE contains + complete markup, not only for external PEs, but also for + internal PEs if the reference occurs between declarations. + */ + if (isParamEntity || enc != encoding) { + if (XmlTokenRole(&prologState, XML_TOK_NONE, end, end, enc) + == XML_ROLE_ERROR) + return XML_ERROR_INCOMPLETE_PE; + *nextPtr = s; + return XML_ERROR_NONE; + } +#endif /* XML_DTD */ + return XML_ERROR_NO_ELEMENTS; + default: + tok = -tok; + next = end; + break; + } + } + role = XmlTokenRole(&prologState, tok, s, next, enc); + switch (role) { + case XML_ROLE_XML_DECL: + { + enum XML_Error result = processXmlDecl(parser, 0, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_DOCTYPE_NAME: + if (startDoctypeDeclHandler) { + doctypeName = poolStoreString(&tempPool, enc, s, next); + if (!doctypeName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + doctypePubid = NULL; + handleDefault = XML_FALSE; + } + doctypeSysid = NULL; /* always initialize to NULL */ + break; + case XML_ROLE_DOCTYPE_INTERNAL_SUBSET: + if (startDoctypeDeclHandler) { + startDoctypeDeclHandler(handlerArg, doctypeName, doctypeSysid, + doctypePubid, 1); + doctypeName = NULL; + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + break; +#ifdef XML_DTD + case XML_ROLE_TEXT_DECL: + { + enum XML_Error result = processXmlDecl(parser, 1, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + handleDefault = XML_FALSE; + } + break; +#endif /* XML_DTD */ + case XML_ROLE_DOCTYPE_PUBLIC_ID: +#ifdef XML_DTD + useForeignDTD = XML_FALSE; + declEntity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; +#endif /* XML_DTD */ + dtd->hasParamEntityRefs = XML_TRUE; + if (startDoctypeDeclHandler) { + XML_Char *pubId; + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + pubId = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!pubId) + return XML_ERROR_NO_MEMORY; + normalizePublicId(pubId); + poolFinish(&tempPool); + doctypePubid = pubId; + handleDefault = XML_FALSE; + goto alreadyChecked; + } + /* fall through */ + case XML_ROLE_ENTITY_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + alreadyChecked: + if (dtd->keepProcessing && declEntity) { + XML_Char *tem = poolStoreString(&dtd->pool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declEntity->publicId = tem; + poolFinish(&dtd->pool); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_DOCTYPE_CLOSE: + if (doctypeName) { + startDoctypeDeclHandler(handlerArg, doctypeName, + doctypeSysid, doctypePubid, 0); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + /* doctypeSysid will be non-NULL in the case of a previous + XML_ROLE_DOCTYPE_SYSTEM_ID, even if startDoctypeDeclHandler + was not set, indicating an external subset + */ +#ifdef XML_DTD + if (doctypeSysid || useForeignDTD) { + XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; + dtd->hasParamEntityRefs = XML_TRUE; + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!entity) + return XML_ERROR_NO_MEMORY; + if (useForeignDTD) + entity->base = curBase; + dtd->paramEntityRead = XML_FALSE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + if (dtd->paramEntityRead) { + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + } + /* if we didn't read the foreign DTD then this means that there + is no external subset and we must reset dtd->hasParamEntityRefs + */ + else if (!doctypeSysid) + dtd->hasParamEntityRefs = hadParamEntityRefs; + /* end of DTD - no need to update dtd->keepProcessing */ + } + useForeignDTD = XML_FALSE; + } +#endif /* XML_DTD */ + if (endDoctypeDeclHandler) { + endDoctypeDeclHandler(handlerArg); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_INSTANCE_START: +#ifdef XML_DTD + /* if there is no DOCTYPE declaration then now is the + last chance to read the foreign DTD + */ + if (useForeignDTD) { + XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; + dtd->hasParamEntityRefs = XML_TRUE; + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!entity) + return XML_ERROR_NO_MEMORY; + entity->base = curBase; + dtd->paramEntityRead = XML_FALSE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + if (dtd->paramEntityRead) { + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + } + /* if we didn't read the foreign DTD then this means that there + is no external subset and we must reset dtd->hasParamEntityRefs + */ + else + dtd->hasParamEntityRefs = hadParamEntityRefs; + /* end of DTD - no need to update dtd->keepProcessing */ + } + } +#endif /* XML_DTD */ + processor = contentProcessor; + return contentProcessor(parser, s, end, nextPtr); + case XML_ROLE_ATTLIST_ELEMENT_NAME: + declElementType = getElementType(parser, enc, s, next); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_NAME: + declAttributeId = getAttributeId(parser, enc, s, next); + if (!declAttributeId) + return XML_ERROR_NO_MEMORY; + declAttributeIsCdata = XML_FALSE; + declAttributeType = NULL; + declAttributeIsId = XML_FALSE; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_CDATA: + declAttributeIsCdata = XML_TRUE; + declAttributeType = atypeCDATA; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ID: + declAttributeIsId = XML_TRUE; + declAttributeType = atypeID; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_IDREF: + declAttributeType = atypeIDREF; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_IDREFS: + declAttributeType = atypeIDREFS; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ENTITY: + declAttributeType = atypeENTITY; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES: + declAttributeType = atypeENTITIES; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN: + declAttributeType = atypeNMTOKEN; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS: + declAttributeType = atypeNMTOKENS; + checkAttListDeclHandler: + if (dtd->keepProcessing && attlistDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ATTRIBUTE_ENUM_VALUE: + case XML_ROLE_ATTRIBUTE_NOTATION_VALUE: + if (dtd->keepProcessing && attlistDeclHandler) { + const XML_Char *prefix; + if (declAttributeType) { + prefix = enumValueSep; + } + else { + prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE + ? notationPrefix + : enumValueStart); + } + if (!poolAppendString(&tempPool, prefix)) + return XML_ERROR_NO_MEMORY; + if (!poolAppend(&tempPool, enc, s, next)) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE: + case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE: + if (dtd->keepProcessing) { + if (!defineAttribute(declElementType, declAttributeId, + declAttributeIsCdata, declAttributeIsId, + 0, parser)) + return XML_ERROR_NO_MEMORY; + if (attlistDeclHandler && declAttributeType) { + if (*declAttributeType == XML_T(ASCII_LPAREN) + || (*declAttributeType == XML_T(ASCII_N) + && declAttributeType[1] == XML_T(ASCII_O))) { + /* Enumerated or Notation type */ + if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN)) + || !poolAppendChar(&tempPool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + poolFinish(&tempPool); + } + *eventEndPP = s; + attlistDeclHandler(handlerArg, declElementType->name, + declAttributeId->name, declAttributeType, + 0, role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE: + case XML_ROLE_FIXED_ATTRIBUTE_VALUE: + if (dtd->keepProcessing) { + const XML_Char *attVal; + enum XML_Error result = + storeAttributeValue(parser, enc, declAttributeIsCdata, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar, + &dtd->pool); + if (result) + return result; + attVal = poolStart(&dtd->pool); + poolFinish(&dtd->pool); + /* ID attributes aren't allowed to have a default */ + if (!defineAttribute(declElementType, declAttributeId, + declAttributeIsCdata, XML_FALSE, attVal, parser)) + return XML_ERROR_NO_MEMORY; + if (attlistDeclHandler && declAttributeType) { + if (*declAttributeType == XML_T(ASCII_LPAREN) + || (*declAttributeType == XML_T(ASCII_N) + && declAttributeType[1] == XML_T(ASCII_O))) { + /* Enumerated or Notation type */ + if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN)) + || !poolAppendChar(&tempPool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + poolFinish(&tempPool); + } + *eventEndPP = s; + attlistDeclHandler(handlerArg, declElementType->name, + declAttributeId->name, declAttributeType, + attVal, + role == XML_ROLE_FIXED_ATTRIBUTE_VALUE); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_ENTITY_VALUE: + if (dtd->keepProcessing) { + enum XML_Error result = storeEntityValue(parser, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (declEntity) { + declEntity->textPtr = poolStart(&dtd->entityValuePool); + declEntity->textLen = (int)(poolLength(&dtd->entityValuePool)); + poolFinish(&dtd->entityValuePool); + if (entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + declEntity->is_param, + declEntity->textPtr, + declEntity->textLen, + curBase, 0, 0, 0); + handleDefault = XML_FALSE; + } + } + else + poolDiscard(&dtd->entityValuePool); + if (result != XML_ERROR_NONE) + return result; + } + break; + case XML_ROLE_DOCTYPE_SYSTEM_ID: +#ifdef XML_DTD + useForeignDTD = XML_FALSE; +#endif /* XML_DTD */ + dtd->hasParamEntityRefs = XML_TRUE; + if (startDoctypeDeclHandler) { + doctypeSysid = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (doctypeSysid == NULL) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } +#ifdef XML_DTD + else + /* use externalSubsetName to make doctypeSysid non-NULL + for the case where no startDoctypeDeclHandler is set */ + doctypeSysid = externalSubsetName; +#endif /* XML_DTD */ + if (!dtd->standalone +#ifdef XML_DTD + && !paramEntityParsing +#endif /* XML_DTD */ + && notStandaloneHandler + && !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; +#ifndef XML_DTD + break; +#else /* XML_DTD */ + if (!declEntity) { + declEntity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + declEntity->publicId = NULL; + } + /* fall through */ +#endif /* XML_DTD */ + case XML_ROLE_ENTITY_SYSTEM_ID: + if (dtd->keepProcessing && declEntity) { + declEntity->systemId = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!declEntity->systemId) + return XML_ERROR_NO_MEMORY; + declEntity->base = curBase; + poolFinish(&dtd->pool); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_ENTITY_COMPLETE: + if (dtd->keepProcessing && declEntity && entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + declEntity->is_param, + 0,0, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + 0); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_ENTITY_NOTATION_NAME: + if (dtd->keepProcessing && declEntity) { + declEntity->notation = poolStoreString(&dtd->pool, enc, s, next); + if (!declEntity->notation) + return XML_ERROR_NO_MEMORY; + poolFinish(&dtd->pool); + if (unparsedEntityDeclHandler) { + *eventEndPP = s; + unparsedEntityDeclHandler(handlerArg, + declEntity->name, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + handleDefault = XML_FALSE; + } + else if (entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + 0,0,0, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_GENERAL_ENTITY_NAME: + { + if (XmlPredefinedEntityName(enc, s, next)) { + declEntity = NULL; + break; + } + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + else { + poolFinish(&dtd->pool); + declEntity->publicId = NULL; + declEntity->is_param = XML_FALSE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + declEntity->is_internal = !(parentParser || openInternalEntities); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + } + else { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + } + break; + case XML_ROLE_PARAM_ENTITY_NAME: +#ifdef XML_DTD + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(parser, &dtd->paramEntities, + name, sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + else { + poolFinish(&dtd->pool); + declEntity->publicId = NULL; + declEntity->is_param = XML_TRUE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + declEntity->is_internal = !(parentParser || openInternalEntities); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + } + else { + poolDiscard(&dtd->pool); + declEntity = NULL; + } +#else /* not XML_DTD */ + declEntity = NULL; +#endif /* XML_DTD */ + break; + case XML_ROLE_NOTATION_NAME: + declNotationPublicId = NULL; + declNotationName = NULL; + if (notationDeclHandler) { + declNotationName = poolStoreString(&tempPool, enc, s, next); + if (!declNotationName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_NOTATION_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + if (declNotationName) { /* means notationDeclHandler != NULL */ + XML_Char *tem = poolStoreString(&tempPool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declNotationPublicId = tem; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_NOTATION_SYSTEM_ID: + if (declNotationName && notationDeclHandler) { + const XML_Char *systemId + = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!systemId) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + systemId, + declNotationPublicId); + handleDefault = XML_FALSE; + } + poolClear(&tempPool); + break; + case XML_ROLE_NOTATION_NO_SYSTEM_ID: + if (declNotationPublicId && notationDeclHandler) { + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + 0, + declNotationPublicId); + handleDefault = XML_FALSE; + } + poolClear(&tempPool); + break; + case XML_ROLE_ERROR: + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: + /* PE references in internal subset are + not allowed within declarations. */ + return XML_ERROR_PARAM_ENTITY_REF; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + default: + return XML_ERROR_SYNTAX; + } +#ifdef XML_DTD + case XML_ROLE_IGNORE_SECT: + { + enum XML_Error result; + if (defaultHandler) + reportDefault(parser, enc, s, next); + handleDefault = XML_FALSE; + result = doIgnoreSection(parser, enc, &next, end, nextPtr, haveMore); + if (result != XML_ERROR_NONE) + return result; + else if (!next) { + processor = ignoreSectionProcessor; + return result; + } + } + break; +#endif /* XML_DTD */ + case XML_ROLE_GROUP_OPEN: + if (prologState.level >= groupSize) { + if (groupSize) { + char *temp = (char *)REALLOC(groupConnector, groupSize *= 2); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + groupConnector = temp; + if (dtd->scaffIndex) { + int *temp = (int *)REALLOC(dtd->scaffIndex, + groupSize * sizeof(int)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + dtd->scaffIndex = temp; + } + } + else { + groupConnector = (char *)MALLOC(groupSize = 32); + if (!groupConnector) + return XML_ERROR_NO_MEMORY; + } + } + groupConnector[prologState.level] = 0; + if (dtd->in_eldecl) { + int myindex = nextScaffoldPart(parser); + if (myindex < 0) + return XML_ERROR_NO_MEMORY; + dtd->scaffIndex[dtd->scaffLevel] = myindex; + dtd->scaffLevel++; + dtd->scaffold[myindex].type = XML_CTYPE_SEQ; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_GROUP_SEQUENCE: + if (groupConnector[prologState.level] == ASCII_PIPE) + return XML_ERROR_SYNTAX; + groupConnector[prologState.level] = ASCII_COMMA; + if (dtd->in_eldecl && elementDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_GROUP_CHOICE: + if (groupConnector[prologState.level] == ASCII_COMMA) + return XML_ERROR_SYNTAX; + if (dtd->in_eldecl + && !groupConnector[prologState.level] + && (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + != XML_CTYPE_MIXED) + ) { + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + = XML_CTYPE_CHOICE; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + groupConnector[prologState.level] = ASCII_PIPE; + break; + case XML_ROLE_PARAM_ENTITY_REF: +#ifdef XML_DTD + case XML_ROLE_INNER_PARAM_ENTITY_REF: + dtd->hasParamEntityRefs = XML_TRUE; + if (!paramEntityParsing) + dtd->keepProcessing = dtd->standalone; + else { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); + poolDiscard(&dtd->pool); + /* first, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity handler + */ + if (prologState.documentEntity && + (dtd->standalone + ? !openInternalEntities + : !dtd->hasParamEntityRefs)) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + dtd->keepProcessing = dtd->standalone; + /* cannot report skipped entities in declarations */ + if ((role == XML_ROLE_PARAM_ENTITY_REF) && skippedEntityHandler) { + skippedEntityHandler(handlerArg, name, 1); + handleDefault = XML_FALSE; + } + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + XML_Bool betweenDecl = + (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); + result = processInternalEntity(parser, entity, betweenDecl); + if (result != XML_ERROR_NONE) + return result; + handleDefault = XML_FALSE; + break; + } + if (externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = XML_FALSE; + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + } + entity->open = XML_FALSE; + handleDefault = XML_FALSE; + if (!dtd->paramEntityRead) { + dtd->keepProcessing = dtd->standalone; + break; + } + } + else { + dtd->keepProcessing = dtd->standalone; + break; + } + } +#endif /* XML_DTD */ + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + break; + + /* Element declaration stuff */ + + case XML_ROLE_ELEMENT_NAME: + if (elementDeclHandler) { + declElementType = getElementType(parser, enc, s, next); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + dtd->scaffLevel = 0; + dtd->scaffCount = 0; + dtd->in_eldecl = XML_TRUE; + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_ANY: + case XML_ROLE_CONTENT_EMPTY: + if (dtd->in_eldecl) { + if (elementDeclHandler) { + XML_Content * content = (XML_Content *) MALLOC(sizeof(XML_Content)); + if (!content) + return XML_ERROR_NO_MEMORY; + content->quant = XML_CQUANT_NONE; + content->name = NULL; + content->numchildren = 0; + content->children = NULL; + content->type = ((role == XML_ROLE_CONTENT_ANY) ? + XML_CTYPE_ANY : + XML_CTYPE_EMPTY); + *eventEndPP = s; + elementDeclHandler(handlerArg, declElementType->name, content); + handleDefault = XML_FALSE; + } + dtd->in_eldecl = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_PCDATA: + if (dtd->in_eldecl) { + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + = XML_CTYPE_MIXED; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_ELEMENT: + quant = XML_CQUANT_NONE; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_OPT: + quant = XML_CQUANT_OPT; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_REP: + quant = XML_CQUANT_REP; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_PLUS: + quant = XML_CQUANT_PLUS; + elementContent: + if (dtd->in_eldecl) { + ELEMENT_TYPE *el; + const XML_Char *name; + int nameLen; + const char *nxt = (quant == XML_CQUANT_NONE + ? next + : next - enc->minBytesPerChar); + int myindex = nextScaffoldPart(parser); + if (myindex < 0) + return XML_ERROR_NO_MEMORY; + dtd->scaffold[myindex].type = XML_CTYPE_NAME; + dtd->scaffold[myindex].quant = quant; + el = getElementType(parser, enc, s, nxt); + if (!el) + return XML_ERROR_NO_MEMORY; + name = el->name; + dtd->scaffold[myindex].name = name; + nameLen = 0; + for (; name[nameLen++]; ); + dtd->contentStringLen += nameLen; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_GROUP_CLOSE: + quant = XML_CQUANT_NONE; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_OPT: + quant = XML_CQUANT_OPT; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_REP: + quant = XML_CQUANT_REP; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_PLUS: + quant = XML_CQUANT_PLUS; + closeGroup: + if (dtd->in_eldecl) { + if (elementDeclHandler) + handleDefault = XML_FALSE; + dtd->scaffLevel--; + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant; + if (dtd->scaffLevel == 0) { + if (!handleDefault) { + XML_Content *model = build_model(parser); + if (!model) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + elementDeclHandler(handlerArg, declElementType->name, model); + } + dtd->in_eldecl = XML_FALSE; + dtd->contentStringLen = 0; + } + } + break; + /* End element declaration stuff */ + + case XML_ROLE_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + handleDefault = XML_FALSE; + break; + case XML_ROLE_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + handleDefault = XML_FALSE; + break; + case XML_ROLE_NONE: + switch (tok) { + case XML_TOK_BOM: + handleDefault = XML_FALSE; + break; + } + break; + case XML_ROLE_DOCTYPE_NONE: + if (startDoctypeDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ENTITY_NONE: + if (dtd->keepProcessing && entityDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_NOTATION_NONE: + if (notationDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ATTLIST_NONE: + if (dtd->keepProcessing && attlistDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ELEMENT_NONE: + if (elementDeclHandler) + handleDefault = XML_FALSE; + break; + } /* end of big switch */ + + if (handleDefault && defaultHandler) + reportDefault(parser, enc, s, next); + + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + s = next; + tok = XmlPrologTok(enc, s, end, &next); + } + } + /* not reached */ +} + +static enum XML_Error PTRCALL +epilogProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + processor = epilogProcessor; + eventPtr = s; + for (;;) { + const char *next = NULL; + int tok = XmlPrologTok(encoding, s, end, &next); + eventEndPtr = next; + switch (tok) { + /* report partial linebreak - it might be the last token */ + case -XML_TOK_PROLOG_S: + if (defaultHandler) { + reportDefault(parser, encoding, s, next); + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + } + *nextPtr = next; + return XML_ERROR_NONE; + case XML_TOK_NONE: + *nextPtr = s; + return XML_ERROR_NONE; + case XML_TOK_PROLOG_S: + if (defaultHandler) + reportDefault(parser, encoding, s, next); + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_INVALID: + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + default: + return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; + } + eventPtr = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } +} + +static enum XML_Error +processInternalEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl) +{ + const char *textStart, *textEnd; + const char *next; + enum XML_Error result; + OPEN_INTERNAL_ENTITY *openEntity; + + if (freeInternalEntities) { + openEntity = freeInternalEntities; + freeInternalEntities = openEntity->next; + } + else { + openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(sizeof(OPEN_INTERNAL_ENTITY)); + if (!openEntity) + return XML_ERROR_NO_MEMORY; + } + entity->open = XML_TRUE; + entity->processed = 0; + openEntity->next = openInternalEntities; + openInternalEntities = openEntity; + openEntity->entity = entity; + openEntity->startTagLevel = tagLevel; + openEntity->betweenDecl = betweenDecl; + openEntity->internalEventPtr = NULL; + openEntity->internalEventEndPtr = NULL; + textStart = (char *)entity->textPtr; + textEnd = (char *)(entity->textPtr + entity->textLen); + +#ifdef XML_DTD + if (entity->is_param) { + int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, internalEncoding, textStart, textEnd, tok, + next, &next, XML_FALSE); + } + else +#endif /* XML_DTD */ + result = doContent(parser, tagLevel, internalEncoding, textStart, + textEnd, &next, XML_FALSE); + + if (result == XML_ERROR_NONE) { + if (textEnd != next && ps_parsing == XML_SUSPENDED) { + entity->processed = (int)(next - textStart); + processor = internalEntityProcessor; + } + else { + entity->open = XML_FALSE; + openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + } + return result; +} + +static enum XML_Error PTRCALL +internalEntityProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + ENTITY *entity; + const char *textStart, *textEnd; + const char *next; + enum XML_Error result; + OPEN_INTERNAL_ENTITY *openEntity = openInternalEntities; + if (!openEntity) + return XML_ERROR_UNEXPECTED_STATE; + + entity = openEntity->entity; + textStart = ((char *)entity->textPtr) + entity->processed; + textEnd = (char *)(entity->textPtr + entity->textLen); + +#ifdef XML_DTD + if (entity->is_param) { + int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, internalEncoding, textStart, textEnd, tok, + next, &next, XML_FALSE); + } + else +#endif /* XML_DTD */ + result = doContent(parser, openEntity->startTagLevel, internalEncoding, + textStart, textEnd, &next, XML_FALSE); + + if (result != XML_ERROR_NONE) + return result; + else if (textEnd != next && ps_parsing == XML_SUSPENDED) { + entity->processed = (int)(next - (char *)entity->textPtr); + return result; + } + else { + entity->open = XML_FALSE; + openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + +#ifdef XML_DTD + if (entity->is_param) { + int tok; + processor = prologProcessor; + tok = XmlPrologTok(encoding, s, end, &next); + return doProlog(parser, encoding, s, end, tok, next, nextPtr, + (XML_Bool)!ps_finalBuffer); + } + else +#endif /* XML_DTD */ + { + processor = contentProcessor; + /* see externalEntityContentProcessor vs contentProcessor */ + return doContent(parser, parentParser ? 1 : 0, encoding, s, end, + nextPtr, (XML_Bool)!ps_finalBuffer); + } +} + +static enum XML_Error PTRCALL +errorProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + return errorCode; +} + +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, + end, pool); + if (result) + return result; + if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) + poolChop(pool); + if (!poolAppendChar(pool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + return XML_ERROR_NONE; +} + +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + for (;;) { + const char *next; + int tok = XmlAttributeValueTok(enc, ptr, end, &next); + switch (tok) { + case XML_TOK_NONE: + return XML_ERROR_NONE; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, ptr); + if (n < 0) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + if (!isCdata + && n == 0x20 /* space */ + && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + for (i = 0; i < n; i++) { + if (!poolAppendChar(pool, buf[i])) + return XML_ERROR_NO_MEMORY; + } + } + break; + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, ptr, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_TRAILING_CR: + next = ptr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_ATTRIBUTE_VALUE_S: + case XML_TOK_DATA_NEWLINE: + if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + if (!poolAppendChar(pool, 0x20)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + char checkEntityDecl; + XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (!poolAppendChar(pool, ch)) + return XML_ERROR_NO_MEMORY; + break; + } + name = poolStoreString(&temp2Pool, enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); + poolDiscard(&temp2Pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal. + */ + if (pool == &dtd->pool) /* are we called from prolog? */ + checkEntityDecl = +#ifdef XML_DTD + prologState.documentEntity && +#endif /* XML_DTD */ + (dtd->standalone + ? !openInternalEntities + : !dtd->hasParamEntityRefs); + else /* if (pool == &tempPool): we are called from content */ + checkEntityDecl = !dtd->hasParamEntityRefs || dtd->standalone; + if (checkEntityDecl) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + /* Cannot report skipped entity here - see comments on + skippedEntityHandler. + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + */ + /* Cannot call the default handler because this would be + out of sync with the call to the startElementHandler. + if ((pool == &tempPool) && defaultHandler) + reportDefault(parser, enc, ptr, next); + */ + break; + } + if (entity->open) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_RECURSIVE_ENTITY_REF; + } + if (entity->notation) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BINARY_ENTITY_REF; + } + if (!entity->textPtr) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; + } + else { + enum XML_Error result; + const XML_Char *textEnd = entity->textPtr + entity->textLen; + entity->open = XML_TRUE; + result = appendAttributeValue(parser, internalEncoding, isCdata, + (char *)entity->textPtr, + (char *)textEnd, pool); + entity->open = XML_FALSE; + if (result) + return result; + } + } + break; + default: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_UNEXPECTED_STATE; + } + ptr = next; + } + /* not reached */ +} + +static enum XML_Error +storeEntityValue(XML_Parser parser, + const ENCODING *enc, + const char *entityTextPtr, + const char *entityTextEnd) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + STRING_POOL *pool = &(dtd->entityValuePool); + enum XML_Error result = XML_ERROR_NONE; +#ifdef XML_DTD + int oldInEntityValue = prologState.inEntityValue; + prologState.inEntityValue = 1; +#endif /* XML_DTD */ + /* never return Null for the value argument in EntityDeclHandler, + since this would indicate an external entity; therefore we + have to make sure that entityValuePool.start is not null */ + if (!pool->blocks) { + if (!poolGrow(pool)) + return XML_ERROR_NO_MEMORY; + } + + for (;;) { + const char *next; + int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: +#ifdef XML_DTD + if (isParamEntity || enc != encoding) { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&tempPool, enc, + entityTextPtr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); + poolDiscard(&tempPool); + if (!entity) { + /* not a well-formedness error - see XML 1.0: WFC Entity Declared */ + /* cannot report skipped entity here - see comments on + skippedEntityHandler + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + */ + dtd->keepProcessing = dtd->standalone; + goto endEntityValue; + } + if (entity->open) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_RECURSIVE_ENTITY_REF; + goto endEntityValue; + } + if (entity->systemId) { + if (externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = XML_FALSE; + result = XML_ERROR_EXTERNAL_ENTITY_HANDLING; + goto endEntityValue; + } + entity->open = XML_FALSE; + if (!dtd->paramEntityRead) + dtd->keepProcessing = dtd->standalone; + } + else + dtd->keepProcessing = dtd->standalone; + } + else { + entity->open = XML_TRUE; + result = storeEntityValue(parser, + internalEncoding, + (char *)entity->textPtr, + (char *)(entity->textPtr + + entity->textLen)); + entity->open = XML_FALSE; + if (result) + goto endEntityValue; + } + break; + } +#endif /* XML_DTD */ + /* In the internal subset, PE references are not legal + within markup declarations, e.g entity values in this case. */ + eventPtr = entityTextPtr; + result = XML_ERROR_PARAM_ENTITY_REF; + goto endEntityValue; + case XML_TOK_NONE: + result = XML_ERROR_NONE; + goto endEntityValue; + case XML_TOK_ENTITY_REF: + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, entityTextPtr, next)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + break; + case XML_TOK_TRAILING_CR: + next = entityTextPtr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_DATA_NEWLINE: + if (pool->end == pool->ptr && !poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + *(pool->ptr)++ = 0xA; + break; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, entityTextPtr); + if (n < 0) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + for (i = 0; i < n; i++) { + if (pool->end == pool->ptr && !poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + *(pool->ptr)++ = buf[i]; + } + } + break; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_INVALID_TOKEN; + goto endEntityValue; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + result = XML_ERROR_INVALID_TOKEN; + goto endEntityValue; + default: + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_UNEXPECTED_STATE; + goto endEntityValue; + } + entityTextPtr = next; + } +endEntityValue: +#ifdef XML_DTD + prologState.inEntityValue = oldInEntityValue; +#endif /* XML_DTD */ + return result; +} + +static void FASTCALL +normalizeLines(XML_Char *s) +{ + XML_Char *p; + for (;; s++) { + if (*s == XML_T('\0')) + return; + if (*s == 0xD) + break; + } + p = s; + do { + if (*s == 0xD) { + *p++ = 0xA; + if (*++s == 0xA) + s++; + } + else + *p++ = *s++; + } while (*s); + *p = XML_T('\0'); +} + +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + const XML_Char *target; + XML_Char *data; + const char *tem; + if (!processingInstructionHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + start += enc->minBytesPerChar * 2; + tem = start + XmlNameLength(enc, start); + target = poolStoreString(&tempPool, enc, start, tem); + if (!target) + return 0; + poolFinish(&tempPool); + data = poolStoreString(&tempPool, enc, + XmlSkipS(enc, tem), + end - enc->minBytesPerChar*2); + if (!data) + return 0; + normalizeLines(data); + processingInstructionHandler(handlerArg, target, data); + poolClear(&tempPool); + return 1; +} + +static int +reportComment(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + XML_Char *data; + if (!commentHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + data = poolStoreString(&tempPool, + enc, + start + enc->minBytesPerChar * 4, + end - enc->minBytesPerChar * 3); + if (!data) + return 0; + normalizeLines(data); + commentHandler(handlerArg, data); + poolClear(&tempPool); + return 1; +} + +static void +reportDefault(XML_Parser parser, const ENCODING *enc, + const char *s, const char *end) +{ + if (MUST_CONVERT(enc, s)) { + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + do { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + defaultHandler(handlerArg, dataBuf, (int)(dataPtr - (ICHAR *)dataBuf)); + *eventPP = s; + } while (s != end); + } + else + defaultHandler(handlerArg, (XML_Char *)s, (int)((XML_Char *)end - (XML_Char *)s)); +} + + +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, + XML_Bool isId, const XML_Char *value, XML_Parser parser) +{ + DEFAULT_ATTRIBUTE *att; + if (value || isId) { + /* The handling of default attributes gets messed up if we have + a default which duplicates a non-default. */ + int i; + for (i = 0; i < type->nDefaultAtts; i++) + if (attId == type->defaultAtts[i].id) + return 1; + if (isId && !type->idAtt && !attId->xmlns) + type->idAtt = attId; + } + if (type->nDefaultAtts == type->allocDefaultAtts) { + if (type->allocDefaultAtts == 0) { + type->allocDefaultAtts = 8; + type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(type->allocDefaultAtts + * sizeof(DEFAULT_ATTRIBUTE)); + if (!type->defaultAtts) + return 0; + } + else { + DEFAULT_ATTRIBUTE *temp; + int count = type->allocDefaultAtts * 2; + temp = (DEFAULT_ATTRIBUTE *) + REALLOC(type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE))); + if (temp == NULL) + return 0; + type->allocDefaultAtts = count; + type->defaultAtts = temp; + } + } + att = type->defaultAtts + type->nDefaultAtts; + att->id = attId; + att->value = value; + att->isCdata = isCdata; + if (!isCdata) + attId->maybeTokenized = XML_TRUE; + type->nDefaultAtts += 1; + return 1; +} + +static int +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *name; + for (name = elementType->name; *name; name++) { + if (*name == XML_T(ASCII_COLON)) { + PREFIX *prefix; + const XML_Char *s; + for (s = elementType->name; s != name; s++) { + if (!poolAppendChar(&dtd->pool, *s)) + return 0; + } + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return 0; + prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), + sizeof(PREFIX)); + if (!prefix) + return 0; + if (prefix->name == poolStart(&dtd->pool)) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + elementType->prefix = prefix; + + } + } + return 1; +} + +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + ATTRIBUTE_ID *id; + const XML_Char *name; + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + name = poolStoreString(&dtd->pool, enc, start, end); + if (!name) + return NULL; + /* skip quotation mark - its storage will be re-used (like in name[-1]) */ + ++name; + id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, name, sizeof(ATTRIBUTE_ID)); + if (!id) + return NULL; + if (id->name != name) + poolDiscard(&dtd->pool); + else { + poolFinish(&dtd->pool); + if (!ns) + ; + else if (name[0] == XML_T(ASCII_x) + && name[1] == XML_T(ASCII_m) + && name[2] == XML_T(ASCII_l) + && name[3] == XML_T(ASCII_n) + && name[4] == XML_T(ASCII_s) + && (name[5] == XML_T('\0') || name[5] == XML_T(ASCII_COLON))) { + if (name[5] == XML_T('\0')) + id->prefix = &dtd->defaultPrefix; + else + id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, name + 6, sizeof(PREFIX)); + id->xmlns = XML_TRUE; + } + else { + int i; + for (i = 0; name[i]; i++) { + /* attributes without prefix are *not* in the default namespace */ + if (name[i] == XML_T(ASCII_COLON)) { + int j; + for (j = 0; j < i; j++) { + if (!poolAppendChar(&dtd->pool, name[j])) + return NULL; + } + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), + sizeof(PREFIX)); + if (id->prefix->name == poolStart(&dtd->pool)) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + break; + } + } + } + } + return id; +} + +#define CONTEXT_SEP XML_T(ASCII_FF) + +static const XML_Char * +getContext(XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + HASH_TABLE_ITER iter; + XML_Bool needSep = XML_FALSE; + + if (dtd->defaultPrefix.binding) { + int i; + int len; + if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS))) + return NULL; + len = dtd->defaultPrefix.binding->uriLen; + if (namespaceSeparator) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, dtd->defaultPrefix.binding->uri[i])) + return NULL; + needSep = XML_TRUE; + } + + hashTableIterInit(&iter, &(dtd->prefixes)); + for (;;) { + int i; + int len; + const XML_Char *s; + PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter); + if (!prefix) + break; + if (!prefix->binding) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return NULL; + for (s = prefix->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return NULL; + if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS))) + return NULL; + len = prefix->binding->uriLen; + if (namespaceSeparator) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, prefix->binding->uri[i])) + return NULL; + needSep = XML_TRUE; + } + + + hashTableIterInit(&iter, &(dtd->generalEntities)); + for (;;) { + const XML_Char *s; + ENTITY *e = (ENTITY *)hashTableIterNext(&iter); + if (!e) + break; + if (!e->open) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return NULL; + for (s = e->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return 0; + needSep = XML_TRUE; + } + + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return NULL; + return tempPool.start; +} + +static XML_Bool +setContext(XML_Parser parser, const XML_Char *context) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *s = context; + + while (*context != XML_T('\0')) { + if (*s == CONTEXT_SEP || *s == XML_T('\0')) { + ENTITY *e; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + e = (ENTITY *)lookup(parser, &dtd->generalEntities, poolStart(&tempPool), 0); + if (e) + e->open = XML_TRUE; + if (*s != XML_T('\0')) + s++; + context = s; + poolDiscard(&tempPool); + } + else if (*s == XML_T(ASCII_EQUALS)) { + PREFIX *prefix; + if (poolLength(&tempPool) == 0) + prefix = &dtd->defaultPrefix; + else { + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&tempPool), + sizeof(PREFIX)); + if (!prefix) + return XML_FALSE; + if (prefix->name == poolStart(&tempPool)) { + prefix->name = poolCopyString(&dtd->pool, prefix->name); + if (!prefix->name) + return XML_FALSE; + } + poolDiscard(&tempPool); + } + for (context = s + 1; + *context != CONTEXT_SEP && *context != XML_T('\0'); + context++) + if (!poolAppendChar(&tempPool, *context)) + return XML_FALSE; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + if (addBinding(parser, prefix, NULL, poolStart(&tempPool), + &inheritedBindings) != XML_ERROR_NONE) + return XML_FALSE; + poolDiscard(&tempPool); + if (*context != XML_T('\0')) + ++context; + s = context; + } + else { + if (!poolAppendChar(&tempPool, *s)) + return XML_FALSE; + s++; + } + } + return XML_TRUE; +} + +static void FASTCALL +normalizePublicId(XML_Char *publicId) +{ + XML_Char *p = publicId; + XML_Char *s; + for (s = publicId; *s; s++) { + switch (*s) { + case 0x20: + case 0xD: + case 0xA: + if (p != publicId && p[-1] != 0x20) + *p++ = 0x20; + break; + default: + *p++ = *s; + } + } + if (p != publicId && p[-1] == 0x20) + --p; + *p = XML_T('\0'); +} + +static DTD * +dtdCreate(const XML_Memory_Handling_Suite *ms) +{ + DTD *p = (DTD *)ms->malloc_fcn(sizeof(DTD)); + if (p == NULL) + return p; + poolInit(&(p->pool), ms); + poolInit(&(p->entityValuePool), ms); + hashTableInit(&(p->generalEntities), ms); + hashTableInit(&(p->elementTypes), ms); + hashTableInit(&(p->attributeIds), ms); + hashTableInit(&(p->prefixes), ms); +#ifdef XML_DTD + p->paramEntityRead = XML_FALSE; + hashTableInit(&(p->paramEntities), ms); +#endif /* XML_DTD */ + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; + + p->in_eldecl = XML_FALSE; + p->scaffIndex = NULL; + p->scaffold = NULL; + p->scaffLevel = 0; + p->scaffSize = 0; + p->scaffCount = 0; + p->contentStringLen = 0; + + p->keepProcessing = XML_TRUE; + p->hasParamEntityRefs = XML_FALSE; + p->standalone = XML_FALSE; + return p; +} + +static void +dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + ms->free_fcn(e->defaultAtts); + } + hashTableClear(&(p->generalEntities)); +#ifdef XML_DTD + p->paramEntityRead = XML_FALSE; + hashTableClear(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableClear(&(p->elementTypes)); + hashTableClear(&(p->attributeIds)); + hashTableClear(&(p->prefixes)); + poolClear(&(p->pool)); + poolClear(&(p->entityValuePool)); + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; + + p->in_eldecl = XML_FALSE; + + ms->free_fcn(p->scaffIndex); + p->scaffIndex = NULL; + ms->free_fcn(p->scaffold); + p->scaffold = NULL; + + p->scaffLevel = 0; + p->scaffSize = 0; + p->scaffCount = 0; + p->contentStringLen = 0; + + p->keepProcessing = XML_TRUE; + p->hasParamEntityRefs = XML_FALSE; + p->standalone = XML_FALSE; +} + +static void +dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + ms->free_fcn(e->defaultAtts); + } + hashTableDestroy(&(p->generalEntities)); +#ifdef XML_DTD + hashTableDestroy(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableDestroy(&(p->elementTypes)); + hashTableDestroy(&(p->attributeIds)); + hashTableDestroy(&(p->prefixes)); + poolDestroy(&(p->pool)); + poolDestroy(&(p->entityValuePool)); + if (isDocEntity) { + ms->free_fcn(p->scaffIndex); + ms->free_fcn(p->scaffold); + } + ms->free_fcn(p); +} + +/* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. + The new DTD has already been initialized. +*/ +static int +dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + + /* Copy the prefix table. */ + + hashTableIterInit(&iter, &(oldDtd->prefixes)); + for (;;) { + const XML_Char *name; + const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter); + if (!oldP) + break; + name = poolCopyString(&(newDtd->pool), oldP->name); + if (!name) + return 0; + if (!lookup(oldParser, &(newDtd->prefixes), name, sizeof(PREFIX))) + return 0; + } + + hashTableIterInit(&iter, &(oldDtd->attributeIds)); + + /* Copy the attribute id table. */ + + for (;;) { + ATTRIBUTE_ID *newA; + const XML_Char *name; + const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter); + + if (!oldA) + break; + /* Remember to allocate the scratch byte before the name. */ + if (!poolAppendChar(&(newDtd->pool), XML_T('\0'))) + return 0; + name = poolCopyString(&(newDtd->pool), oldA->name); + if (!name) + return 0; + ++name; + newA = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds), name, + sizeof(ATTRIBUTE_ID)); + if (!newA) + return 0; + newA->maybeTokenized = oldA->maybeTokenized; + if (oldA->prefix) { + newA->xmlns = oldA->xmlns; + if (oldA->prefix == &oldDtd->defaultPrefix) + newA->prefix = &newDtd->defaultPrefix; + else + newA->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), + oldA->prefix->name, 0); + } + } + + /* Copy the element type table. */ + + hashTableIterInit(&iter, &(oldDtd->elementTypes)); + + for (;;) { + int i; + ELEMENT_TYPE *newE; + const XML_Char *name; + const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(&(newDtd->pool), oldE->name); + if (!name) + return 0; + newE = (ELEMENT_TYPE *)lookup(oldParser, &(newDtd->elementTypes), name, + sizeof(ELEMENT_TYPE)); + if (!newE) + return 0; + if (oldE->nDefaultAtts) { + newE->defaultAtts = (DEFAULT_ATTRIBUTE *) + ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + if (!newE->defaultAtts) { + ms->free_fcn(newE); + return 0; + } + } + if (oldE->idAtt) + newE->idAtt = (ATTRIBUTE_ID *) + lookup(oldParser, &(newDtd->attributeIds), oldE->idAtt->name, 0); + newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts; + if (oldE->prefix) + newE->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), + oldE->prefix->name, 0); + for (i = 0; i < newE->nDefaultAtts; i++) { + newE->defaultAtts[i].id = (ATTRIBUTE_ID *) + lookup(oldParser, &(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0); + newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata; + if (oldE->defaultAtts[i].value) { + newE->defaultAtts[i].value + = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value); + if (!newE->defaultAtts[i].value) + return 0; + } + else + newE->defaultAtts[i].value = NULL; + } + } + + /* Copy the entity tables. */ + if (!copyEntityTable(oldParser, + &(newDtd->generalEntities), + &(newDtd->pool), + &(oldDtd->generalEntities))) + return 0; + +#ifdef XML_DTD + if (!copyEntityTable(oldParser, + &(newDtd->paramEntities), + &(newDtd->pool), + &(oldDtd->paramEntities))) + return 0; + newDtd->paramEntityRead = oldDtd->paramEntityRead; +#endif /* XML_DTD */ + + newDtd->keepProcessing = oldDtd->keepProcessing; + newDtd->hasParamEntityRefs = oldDtd->hasParamEntityRefs; + newDtd->standalone = oldDtd->standalone; + + /* Don't want deep copying for scaffolding */ + newDtd->in_eldecl = oldDtd->in_eldecl; + newDtd->scaffold = oldDtd->scaffold; + newDtd->contentStringLen = oldDtd->contentStringLen; + newDtd->scaffSize = oldDtd->scaffSize; + newDtd->scaffLevel = oldDtd->scaffLevel; + newDtd->scaffIndex = oldDtd->scaffIndex; + + return 1; +} /* End dtdCopy */ + +static int +copyEntityTable(XML_Parser oldParser, + HASH_TABLE *newTable, + STRING_POOL *newPool, + const HASH_TABLE *oldTable) +{ + HASH_TABLE_ITER iter; + const XML_Char *cachedOldBase = NULL; + const XML_Char *cachedNewBase = NULL; + + hashTableIterInit(&iter, oldTable); + + for (;;) { + ENTITY *newE; + const XML_Char *name; + const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(newPool, oldE->name); + if (!name) + return 0; + newE = (ENTITY *)lookup(oldParser, newTable, name, sizeof(ENTITY)); + if (!newE) + return 0; + if (oldE->systemId) { + const XML_Char *tem = poolCopyString(newPool, oldE->systemId); + if (!tem) + return 0; + newE->systemId = tem; + if (oldE->base) { + if (oldE->base == cachedOldBase) + newE->base = cachedNewBase; + else { + cachedOldBase = oldE->base; + tem = poolCopyString(newPool, cachedOldBase); + if (!tem) + return 0; + cachedNewBase = newE->base = tem; + } + } + if (oldE->publicId) { + tem = poolCopyString(newPool, oldE->publicId); + if (!tem) + return 0; + newE->publicId = tem; + } + } + else { + const XML_Char *tem = poolCopyStringN(newPool, oldE->textPtr, + oldE->textLen); + if (!tem) + return 0; + newE->textPtr = tem; + newE->textLen = oldE->textLen; + } + if (oldE->notation) { + const XML_Char *tem = poolCopyString(newPool, oldE->notation); + if (!tem) + return 0; + newE->notation = tem; + } + newE->is_param = oldE->is_param; + newE->is_internal = oldE->is_internal; + } + return 1; +} + +#define INIT_POWER 6 + +static XML_Bool FASTCALL +keyeq(KEY s1, KEY s2) +{ + for (; *s1 == *s2; s1++, s2++) + if (*s1 == 0) + return XML_TRUE; + return XML_FALSE; +} + +static unsigned long FASTCALL +hash(XML_Parser parser, KEY s) +{ + unsigned long h = hash_secret_salt; + while (*s) + h = CHAR_HASH(h, *s++); + return h; +} + +static NAMED * +lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) +{ + size_t i; + if (table->size == 0) { + size_t tsize; + if (!createSize) + return NULL; + table->power = INIT_POWER; + /* table->size is a power of 2 */ + table->size = (size_t)1 << INIT_POWER; + tsize = table->size * sizeof(NAMED *); + table->v = (NAMED **)table->mem->malloc_fcn(tsize); + if (!table->v) { + table->size = 0; + return NULL; + } + memset(table->v, 0, tsize); + i = hash(parser, name) & ((unsigned long)table->size - 1); + } + else { + unsigned long h = hash(parser, name); + unsigned long mask = (unsigned long)table->size - 1; + unsigned char step = 0; + i = h & mask; + while (table->v[i]) { + if (keyeq(name, table->v[i]->name)) + return table->v[i]; + if (!step) + step = PROBE_STEP(h, mask, table->power); + i < step ? (i += table->size - step) : (i -= step); + } + if (!createSize) + return NULL; + + /* check for overflow (table is half full) */ + if (table->used >> (table->power - 1)) { + unsigned char newPower = table->power + 1; + size_t newSize = (size_t)1 << newPower; + unsigned long newMask = (unsigned long)newSize - 1; + size_t tsize = newSize * sizeof(NAMED *); + NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize); + if (!newV) + return NULL; + memset(newV, 0, tsize); + for (i = 0; i < table->size; i++) + if (table->v[i]) { + unsigned long newHash = hash(parser, table->v[i]->name); + size_t j = newHash & newMask; + step = 0; + while (newV[j]) { + if (!step) + step = PROBE_STEP(newHash, newMask, newPower); + j < step ? (j += newSize - step) : (j -= step); + } + newV[j] = table->v[i]; + } + table->mem->free_fcn(table->v); + table->v = newV; + table->power = newPower; + table->size = newSize; + i = h & newMask; + step = 0; + while (table->v[i]) { + if (!step) + step = PROBE_STEP(h, newMask, newPower); + i < step ? (i += newSize - step) : (i -= step); + } + } + } + table->v[i] = (NAMED *)table->mem->malloc_fcn(createSize); + if (!table->v[i]) + return NULL; + memset(table->v[i], 0, createSize); + table->v[i]->name = name; + (table->used)++; + return table->v[i]; +} + +static void FASTCALL +hashTableClear(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) { + table->mem->free_fcn(table->v[i]); + table->v[i] = NULL; + } + table->used = 0; +} + +static void FASTCALL +hashTableDestroy(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) + table->mem->free_fcn(table->v[i]); + table->mem->free_fcn(table->v); +} + +static void FASTCALL +hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) +{ + p->power = 0; + p->size = 0; + p->used = 0; + p->v = NULL; + p->mem = ms; +} + +static void FASTCALL +hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) +{ + iter->p = table->v; + iter->end = iter->p + table->size; +} + +static NAMED * FASTCALL +hashTableIterNext(HASH_TABLE_ITER *iter) +{ + while (iter->p != iter->end) { + NAMED *tem = *(iter->p)++; + if (tem) + return tem; + } + return NULL; +} + +static void FASTCALL +poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) +{ + pool->blocks = NULL; + pool->freeBlocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; + pool->mem = ms; +} + +static void FASTCALL +poolClear(STRING_POOL *pool) +{ + if (!pool->freeBlocks) + pool->freeBlocks = pool->blocks; + else { + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + p->next = pool->freeBlocks; + pool->freeBlocks = p; + p = tem; + } + } + pool->blocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; +} + +static void FASTCALL +poolDestroy(STRING_POOL *pool) +{ + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + pool->mem->free_fcn(p); + p = tem; + } + p = pool->freeBlocks; + while (p) { + BLOCK *tem = p->next; + pool->mem->free_fcn(p); + p = tem; + } +} + +static XML_Char * +poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!pool->ptr && !poolGrow(pool)) + return NULL; + for (;;) { + XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end); + if (ptr == end) + break; + if (!poolGrow(pool)) + return NULL; + } + return pool->start; +} + +static const XML_Char * FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s) +{ + do { + if (!poolAppendChar(pool, *s)) + return NULL; + } while (*s++); + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char * +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) +{ + if (!pool->ptr && !poolGrow(pool)) + return NULL; + for (; n > 0; --n, s++) { + if (!poolAppendChar(pool, *s)) + return NULL; + } + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char * FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s) +{ + while (*s) { + if (!poolAppendChar(pool, *s)) + return NULL; + s++; + } + return pool->start; +} + +static XML_Char * +poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!poolAppend(pool, enc, ptr, end)) + return NULL; + if (pool->ptr == pool->end && !poolGrow(pool)) + return NULL; + *(pool->ptr)++ = 0; + return pool->start; +} + +static XML_Bool FASTCALL +poolGrow(STRING_POOL *pool) +{ + if (pool->freeBlocks) { + if (pool->start == 0) { + pool->blocks = pool->freeBlocks; + pool->freeBlocks = pool->freeBlocks->next; + pool->blocks->next = NULL; + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + pool->ptr = pool->start; + return XML_TRUE; + } + if (pool->end - pool->start < pool->freeBlocks->size) { + BLOCK *tem = pool->freeBlocks->next; + pool->freeBlocks->next = pool->blocks; + pool->blocks = pool->freeBlocks; + pool->freeBlocks = tem; + memcpy(pool->blocks->s, pool->start, + (pool->end - pool->start) * sizeof(XML_Char)); + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + return XML_TRUE; + } + } + if (pool->blocks && pool->start == pool->blocks->s) { + int blockSize = (int)(pool->end - pool->start)*2; + BLOCK *temp = (BLOCK *) + pool->mem->realloc_fcn(pool->blocks, + (offsetof(BLOCK, s) + + blockSize * sizeof(XML_Char))); + if (temp == NULL) + return XML_FALSE; + pool->blocks = temp; + pool->blocks->size = blockSize; + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + blockSize; + } + else { + BLOCK *tem; + int blockSize = (int)(pool->end - pool->start); + if (blockSize < INIT_BLOCK_SIZE) + blockSize = INIT_BLOCK_SIZE; + else + blockSize *= 2; + tem = (BLOCK *)pool->mem->malloc_fcn(offsetof(BLOCK, s) + + blockSize * sizeof(XML_Char)); + if (!tem) + return XML_FALSE; + tem->size = blockSize; + tem->next = pool->blocks; + pool->blocks = tem; + if (pool->ptr != pool->start) + memcpy(tem->s, pool->start, + (pool->ptr - pool->start) * sizeof(XML_Char)); + pool->ptr = tem->s + (pool->ptr - pool->start); + pool->start = tem->s; + pool->end = tem->s + blockSize; + } + return XML_TRUE; +} + +static int FASTCALL +nextScaffoldPart(XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + CONTENT_SCAFFOLD * me; + int next; + + if (!dtd->scaffIndex) { + dtd->scaffIndex = (int *)MALLOC(groupSize * sizeof(int)); + if (!dtd->scaffIndex) + return -1; + dtd->scaffIndex[0] = 0; + } + + if (dtd->scaffCount >= dtd->scaffSize) { + CONTENT_SCAFFOLD *temp; + if (dtd->scaffold) { + temp = (CONTENT_SCAFFOLD *) + REALLOC(dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) + return -1; + dtd->scaffSize *= 2; + } + else { + temp = (CONTENT_SCAFFOLD *)MALLOC(INIT_SCAFFOLD_ELEMENTS + * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) + return -1; + dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS; + } + dtd->scaffold = temp; + } + next = dtd->scaffCount++; + me = &dtd->scaffold[next]; + if (dtd->scaffLevel) { + CONTENT_SCAFFOLD *parent = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel-1]]; + if (parent->lastchild) { + dtd->scaffold[parent->lastchild].nextsib = next; + } + if (!parent->childcnt) + parent->firstchild = next; + parent->lastchild = next; + parent->childcnt++; + } + me->firstchild = me->lastchild = me->childcnt = me->nextsib = 0; + return next; +} + +static void +build_node(XML_Parser parser, + int src_node, + XML_Content *dest, + XML_Content **contpos, + XML_Char **strpos) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + dest->type = dtd->scaffold[src_node].type; + dest->quant = dtd->scaffold[src_node].quant; + if (dest->type == XML_CTYPE_NAME) { + const XML_Char *src; + dest->name = *strpos; + src = dtd->scaffold[src_node].name; + for (;;) { + *(*strpos)++ = *src; + if (!*src) + break; + src++; + } + dest->numchildren = 0; + dest->children = NULL; + } + else { + unsigned int i; + int cn; + dest->numchildren = dtd->scaffold[src_node].childcnt; + dest->children = *contpos; + *contpos += dest->numchildren; + for (i = 0, cn = dtd->scaffold[src_node].firstchild; + i < dest->numchildren; + i++, cn = dtd->scaffold[cn].nextsib) { + build_node(parser, cn, &(dest->children[i]), contpos, strpos); + } + dest->name = NULL; + } +} + +static XML_Content * +build_model (XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + XML_Content *ret; + XML_Content *cpos; + XML_Char * str; + int allocsize = (dtd->scaffCount * sizeof(XML_Content) + + (dtd->contentStringLen * sizeof(XML_Char))); + + ret = (XML_Content *)MALLOC(allocsize); + if (!ret) + return NULL; + + str = (XML_Char *) (&ret[dtd->scaffCount]); + cpos = &ret[1]; + + build_node(parser, 0, ret, &cpos, &str); + return ret; +} + +static ELEMENT_TYPE * +getElementType(XML_Parser parser, + const ENCODING *enc, + const char *ptr, + const char *end) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end); + ELEMENT_TYPE *ret; + + if (!name) + return NULL; + ret = (ELEMENT_TYPE *) lookup(parser, &dtd->elementTypes, name, sizeof(ELEMENT_TYPE)); + if (!ret) + return NULL; + if (ret->name != name) + poolDiscard(&dtd->pool); + else { + poolFinish(&dtd->pool); + if (!setElementTypePrefix(parser, ret)) + return NULL; + } + return ret; +} diff --git a/chromium/third_party/pdfium/pdfium.gyp b/chromium/third_party/pdfium/pdfium.gyp index d8df460c07e..7f1d908b1c7 100644 --- a/chromium/third_party/pdfium/pdfium.gyp +++ b/chromium/third_party/pdfium/pdfium.gyp @@ -840,6 +840,7 @@ 'sources': [ 'core/src/fxcodec/codec/fx_codec_jpx_unittest.cpp', 'core/src/fxcrt/fx_basic_bstring_unittest.cpp', + 'core/src/fxcrt/fx_basic_memmgr_unittest.cpp', 'core/src/fxcrt/fx_basic_wstring_unittest.cpp', 'testing/fx_string_testhelpers.h', 'testing/fx_string_testhelpers.cpp', diff --git a/chromium/third_party/skia/PRESUBMIT.py b/chromium/third_party/skia/PRESUBMIT.py index 6d429dfa1c1..5599c316c36 100644 --- a/chromium/third_party/skia/PRESUBMIT.py +++ b/chromium/third_party/skia/PRESUBMIT.py @@ -343,6 +343,8 @@ def PostUploadHook(cl, change, output_api): need to be gated on the master branch's tree. * Adds 'NOTRY=true' for non master branch changes since trybots do not yet work on them. + * Adds 'NOPRESUBMIT=true' for non master branch changes since those don't + run the presubmit checks. """ results = [] @@ -405,6 +407,12 @@ def PostUploadHook(cl, change, output_api): output_api.PresubmitNotifyResult( 'Trybots do not yet work for non-master branches. ' 'Automatically added \'NOTRY=true\' to the CL\'s description')) + if not re.search( + r'^NOPRESUBMIT=true$', new_description, re.M | re.I): + new_description += "\nNOPRESUBMIT=true" + results.append( + output_api.PresubmitNotifyResult( + 'Branch changes do not run the presubmit checks.')) # Read and process the HASHTAGS file. hashtags_fullpath = os.path.join(change._local_root, 'HASHTAGS') diff --git a/chromium/third_party/skia/include/core/SkMatrix.h b/chromium/third_party/skia/include/core/SkMatrix.h index 0252bc7101c..fa7a63a4e86 100644 --- a/chromium/third_party/skia/include/core/SkMatrix.h +++ b/chromium/third_party/skia/include/core/SkMatrix.h @@ -719,6 +719,12 @@ private: SkScalar fMat[9]; mutable uint32_t fTypeMask; + /** Are all elements of the matrix finite? + */ + bool isFinite() const; + + static void ComputeInv(SkScalar dst[9], const SkScalar src[9], SkScalar invDet, bool isPersp); + void setScaleTranslate(SkScalar sx, SkScalar sy, SkScalar tx, SkScalar ty) { fMat[kMScaleX] = sx; fMat[kMSkewX] = 0; diff --git a/chromium/third_party/skia/include/core/SkPackBits.h b/chromium/third_party/skia/include/core/SkPackBits.h index f0614a08434..1e32ee08755 100644 --- a/chromium/third_party/skia/include/core/SkPackBits.h +++ b/chromium/third_party/skia/include/core/SkPackBits.h @@ -14,11 +14,6 @@ class SkPackBits { public: - /** Given the number of 16bit values that will be passed to Pack16, - returns the worst-case size needed for the dst[] buffer. - */ - static size_t ComputeMaxSize16(int count); - /** Given the number of 8bit values that will be passed to Pack8, returns the worst-case size needed for the dst[] buffer. */ @@ -26,54 +21,26 @@ public: /** Write the src array into a packed format. The packing process may end up writing more bytes than it read, so dst[] must be large enough. - @param src Input array of 16bit values - @param count Number of entries in src[] - @param dst Buffer (allocated by caller) to write the packed data - into - @return the number of bytes written to dst[] - */ - static size_t Pack16(const uint16_t src[], int count, uint8_t dst[]); - - /** Write the src array into a packed format. The packing process may end - up writing more bytes than it read, so dst[] must be large enough. @param src Input array of 8bit values - @param count Number of entries in src[] + @param srcSize Number of entries in src[] @param dst Buffer (allocated by caller) to write the packed data into + @param dstSize Number of bytes in the output buffer. @return the number of bytes written to dst[] */ - static size_t Pack8(const uint8_t src[], int count, uint8_t dst[]); - - /** Unpack the data in src[], and expand it into dst[]. The src[] data was - written by a previous call to Pack16. - @param src Input data to unpack, previously created by Pack16. - @param srcSize Number of bytes of src to unpack - @param dst Buffer (allocated by caller) to expand the src[] into. - @return the number of dst elements (not bytes) written into dst. - */ - static int Unpack16(const uint8_t src[], size_t srcSize, uint16_t dst[]); + static size_t Pack8(const uint8_t src[], size_t srcSize, uint8_t dst[], + size_t dstSize); /** Unpack the data in src[], and expand it into dst[]. The src[] data was written by a previous call to Pack8. @param src Input data to unpack, previously created by Pack8. @param srcSize Number of bytes of src to unpack @param dst Buffer (allocated by caller) to expand the src[] into. + @param dstSize Number of bytes in the output buffer. @return the number of bytes written into dst. */ - static int Unpack8(const uint8_t src[], size_t srcSize, uint8_t dst[]); - - /** Unpack the data from src[], skip the first dstSkip bytes, then write - dstWrite bytes into dst[]. The src[] data was written by a previous - call to Pack8. Return the number of bytes actually writtten into dst[] - @param src Input data to unpack, previously created by Pack8. - @param dst Buffer (allocated by caller) to expand the src[] into. - @param dstSkip Number of bytes of unpacked src to skip before writing - into dst - @param dstWrite Number of bytes of unpacked src to write into dst (after - skipping dstSkip bytes) - */ - static void Unpack8(uint8_t dst[], size_t dstSkip, size_t dstWrite, - const uint8_t src[]); + static int Unpack8(const uint8_t src[], size_t srcSize, uint8_t dst[], + size_t dstSize); }; #endif diff --git a/chromium/third_party/skia/include/core/SkPicture.h b/chromium/third_party/skia/include/core/SkPicture.h index 9a2b65b5c50..f845e942887 100644 --- a/chromium/third_party/skia/include/core/SkPicture.h +++ b/chromium/third_party/skia/include/core/SkPicture.h @@ -242,13 +242,14 @@ private: // V39: Added FilterLevel option to SkPictureImageFilter // V40: Remove UniqueID serialization from SkImageFilter. // V41: Added serialization of SkBitmapSource's filterQuality parameter + // V42: Added a bool to SkPictureShader serialization to indicate did-we-serialize-a-picture? // Note: If the picture version needs to be increased then please follow the // steps to generate new SKPs in (only accessible to Googlers): http://goo.gl/qATVcw // Only SKPs within the min/current picture version range (inclusive) can be read. - static const uint32_t MIN_PICTURE_VERSION = 35; // Produced by Chrome M39. - static const uint32_t CURRENT_PICTURE_VERSION = 41; + static const uint32_t MIN_PICTURE_VERSION = 35; // Produced by Chrome M39. + static const uint32_t CURRENT_PICTURE_VERSION = 42; static_assert(MIN_PICTURE_VERSION <= 41, "Remove kFontFileName and related code from SkFontDescriptor.cpp."); diff --git a/chromium/third_party/skia/src/core/SkData.cpp b/chromium/third_party/skia/src/core/SkData.cpp index dfbd0038497..ad79ce05350 100644 --- a/chromium/third_party/skia/src/core/SkData.cpp +++ b/chromium/third_party/skia/src/core/SkData.cpp @@ -63,7 +63,14 @@ SkData* SkData::PrivateNewWithCopy(const void* srcOrNull, size_t length) { if (0 == length) { return SkData::NewEmpty(); } - char* storage = (char*)sk_malloc_throw(sizeof(SkData) + length); + + const size_t actualLength = length + sizeof(SkData); + if (actualLength < length) { + // we overflowed + sk_throw(); + } + + char* storage = (char*)sk_malloc_throw(actualLength); SkData* data = new (storage) SkData(length); if (srcOrNull) { memcpy(data->writable_data(), srcOrNull, length); diff --git a/chromium/third_party/skia/src/core/SkMatrix.cpp b/chromium/third_party/skia/src/core/SkMatrix.cpp index 9c9c4375f9e..068902eaace 100644 --- a/chromium/third_party/skia/src/core/SkMatrix.cpp +++ b/chromium/third_party/skia/src/core/SkMatrix.cpp @@ -752,6 +752,16 @@ static double sk_inv_determinant(const float mat[9], int isPerspective) { return 1.0 / det; } +bool SkMatrix::isFinite() const { + for (int i = 0; i < 9; ++i) { + if (!SkScalarIsFinite(fMat[i])) { + return false; + } + } + + return true; +} + void SkMatrix::SetAffineIdentity(SkScalar affine[6]) { affine[kAScaleX] = 1; affine[kASkewY] = 0; @@ -776,6 +786,37 @@ bool SkMatrix::asAffine(SkScalar affine[6]) const { return true; } +void SkMatrix::ComputeInv(SkScalar dst[9], const SkScalar src[9], SkScalar invDet, bool isPersp) { + SkASSERT(src != dst); + SkASSERT(src && dst); + + if (isPersp) { + dst[kMScaleX] = scross_dscale(src[kMScaleY], src[kMPersp2], src[kMTransY], src[kMPersp1], invDet); + dst[kMSkewX] = scross_dscale(src[kMTransX], src[kMPersp1], src[kMSkewX], src[kMPersp2], invDet); + dst[kMTransX] = scross_dscale(src[kMSkewX], src[kMTransY], src[kMTransX], src[kMScaleY], invDet); + + dst[kMSkewY] = scross_dscale(src[kMTransY], src[kMPersp0], src[kMSkewY], src[kMPersp2], invDet); + dst[kMScaleY] = scross_dscale(src[kMScaleX], src[kMPersp2], src[kMTransX], src[kMPersp0], invDet); + dst[kMTransY] = scross_dscale(src[kMTransX], src[kMSkewY], src[kMScaleX], src[kMTransY], invDet); + + dst[kMPersp0] = scross_dscale(src[kMSkewY], src[kMPersp1], src[kMScaleY], src[kMPersp0], invDet); + dst[kMPersp1] = scross_dscale(src[kMSkewX], src[kMPersp0], src[kMScaleX], src[kMPersp1], invDet); + dst[kMPersp2] = scross_dscale(src[kMScaleX], src[kMScaleY], src[kMSkewX], src[kMSkewY], invDet); + } else { // not perspective + dst[kMScaleX] = SkDoubleToScalar(src[kMScaleY] * invDet); + dst[kMSkewX] = SkDoubleToScalar(-src[kMSkewX] * invDet); + dst[kMTransX] = dcross_dscale(src[kMSkewX], src[kMTransY], src[kMScaleY], src[kMTransX], invDet); + + dst[kMSkewY] = SkDoubleToScalar(-src[kMSkewY] * invDet); + dst[kMScaleY] = SkDoubleToScalar(src[kMScaleX] * invDet); + dst[kMTransY] = dcross_dscale(src[kMSkewY], src[kMTransX], src[kMScaleX], src[kMTransY], invDet); + + dst[kMPersp0] = 0; + dst[kMPersp1] = 0; + dst[kMPersp2] = 1; + } +} + bool SkMatrix::invertNonIdentity(SkMatrix* inv) const { SkASSERT(!this->isIdentity()); @@ -819,50 +860,32 @@ bool SkMatrix::invertNonIdentity(SkMatrix* inv) const { } int isPersp = mask & kPerspective_Mask; - double scale = sk_inv_determinant(fMat, isPersp); + double invDet = sk_inv_determinant(fMat, isPersp); - if (scale == 0) { // underflow + if (invDet == 0) { // underflow return false; } - if (inv) { - SkMatrix tmp; - if (inv == this) { - inv = &tmp; - } + bool applyingInPlace = (inv == this); - if (isPersp) { - inv->fMat[kMScaleX] = scross_dscale(fMat[kMScaleY], fMat[kMPersp2], fMat[kMTransY], fMat[kMPersp1], scale); - inv->fMat[kMSkewX] = scross_dscale(fMat[kMTransX], fMat[kMPersp1], fMat[kMSkewX], fMat[kMPersp2], scale); - inv->fMat[kMTransX] = scross_dscale(fMat[kMSkewX], fMat[kMTransY], fMat[kMTransX], fMat[kMScaleY], scale); - - inv->fMat[kMSkewY] = scross_dscale(fMat[kMTransY], fMat[kMPersp0], fMat[kMSkewY], fMat[kMPersp2], scale); - inv->fMat[kMScaleY] = scross_dscale(fMat[kMScaleX], fMat[kMPersp2], fMat[kMTransX], fMat[kMPersp0], scale); - inv->fMat[kMTransY] = scross_dscale(fMat[kMTransX], fMat[kMSkewY], fMat[kMScaleX], fMat[kMTransY], scale); - - inv->fMat[kMPersp0] = scross_dscale(fMat[kMSkewY], fMat[kMPersp1], fMat[kMScaleY], fMat[kMPersp0], scale); - inv->fMat[kMPersp1] = scross_dscale(fMat[kMSkewX], fMat[kMPersp0], fMat[kMScaleX], fMat[kMPersp1], scale); - inv->fMat[kMPersp2] = scross_dscale(fMat[kMScaleX], fMat[kMScaleY], fMat[kMSkewX], fMat[kMSkewY], scale); - } else { // not perspective - inv->fMat[kMScaleX] = SkDoubleToScalar(fMat[kMScaleY] * scale); - inv->fMat[kMSkewX] = SkDoubleToScalar(-fMat[kMSkewX] * scale); - inv->fMat[kMTransX] = dcross_dscale(fMat[kMSkewX], fMat[kMTransY], fMat[kMScaleY], fMat[kMTransX], scale); - - inv->fMat[kMSkewY] = SkDoubleToScalar(-fMat[kMSkewY] * scale); - inv->fMat[kMScaleY] = SkDoubleToScalar(fMat[kMScaleX] * scale); - inv->fMat[kMTransY] = dcross_dscale(fMat[kMSkewY], fMat[kMTransX], fMat[kMScaleX], fMat[kMTransY], scale); - - inv->fMat[kMPersp0] = 0; - inv->fMat[kMPersp1] = 0; - inv->fMat[kMPersp2] = 1; - } + SkMatrix* tmp = inv; + + SkMatrix storage; + if (applyingInPlace || NULL == tmp) { + tmp = &storage; // we either need to avoid trampling memory or have no memory + } + + ComputeInv(tmp->fMat, fMat, invDet, isPersp); + if (!tmp->isFinite()) { + return false; + } - inv->setTypeMask(fTypeMask); + tmp->setTypeMask(fTypeMask); - if (inv == &tmp) { - *(SkMatrix*)this = tmp; - } + if (applyingInPlace) { + *inv = storage; // need to copy answer back } + return true; } diff --git a/chromium/third_party/skia/src/core/SkPackBits.cpp b/chromium/third_party/skia/src/core/SkPackBits.cpp index 3c6197b6f27..a3424e2bdcb 100644 --- a/chromium/third_party/skia/src/core/SkPackBits.cpp +++ b/chromium/third_party/skia/src/core/SkPackBits.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * @@ -7,182 +6,27 @@ */ #include "SkPackBits.h" -#define GATHER_STATSx - -static inline void small_memcpy(void* SK_RESTRICT dst, - const void* SK_RESTRICT src, size_t n) { - SkASSERT(n > 0 && n <= 15); - uint8_t* d = (uint8_t*)dst; - const uint8_t* s = (const uint8_t*)src; - switch (n) { - case 15: *d++ = *s++; - case 14: *d++ = *s++; - case 13: *d++ = *s++; - case 12: *d++ = *s++; - case 11: *d++ = *s++; - case 10: *d++ = *s++; - case 9: *d++ = *s++; - case 8: *d++ = *s++; - case 7: *d++ = *s++; - case 6: *d++ = *s++; - case 5: *d++ = *s++; - case 4: *d++ = *s++; - case 3: *d++ = *s++; - case 2: *d++ = *s++; - case 1: *d++ = *s++; - case 0: break; - } -} - -static inline void small_memset(void* dst, uint8_t value, size_t n) { - SkASSERT(n > 0 && n <= 15); - uint8_t* d = (uint8_t*)dst; - switch (n) { - case 15: *d++ = value; - case 14: *d++ = value; - case 13: *d++ = value; - case 12: *d++ = value; - case 11: *d++ = value; - case 10: *d++ = value; - case 9: *d++ = value; - case 8: *d++ = value; - case 7: *d++ = value; - case 6: *d++ = value; - case 5: *d++ = value; - case 4: *d++ = value; - case 3: *d++ = value; - case 2: *d++ = value; - case 1: *d++ = value; - case 0: break; - } -} - -// can we do better for small counts with our own inlined memcpy/memset? - -#define PB_MEMSET(addr, value, count) \ -do { \ -if ((count) > 15) { \ -memset(addr, value, count); \ -} else { \ -small_memset(addr, value, count); \ -} \ -} while (0) - -#define PB_MEMCPY(dst, src, count) \ -do { \ - if ((count) > 15) { \ - memcpy(dst, src, count); \ - } else { \ - small_memcpy(dst, src, count); \ - } \ -} while (0) - -/////////////////////////////////////////////////////////////////////////////// - -#ifdef GATHER_STATS - static int gMemSetBuckets[129]; - static int gMemCpyBuckets[129]; - static int gCounter; - -static void register_memset_count(int n) { - SkASSERT((unsigned)n <= 128); - gMemSetBuckets[n] += 1; - gCounter += 1; - - if ((gCounter & 0xFF) == 0) { - SkDebugf("----- packbits memset stats: "); - for (size_t i = 0; i < SK_ARRAY_COUNT(gMemSetBuckets); i++) { - if (gMemSetBuckets[i]) { - SkDebugf(" %d:%d", i, gMemSetBuckets[i]); - } - } - } -} -static void register_memcpy_count(int n) { - SkASSERT((unsigned)n <= 128); - gMemCpyBuckets[n] += 1; - gCounter += 1; - - if ((gCounter & 0x1FF) == 0) { - SkDebugf("----- packbits memcpy stats: "); - for (size_t i = 0; i < SK_ARRAY_COUNT(gMemCpyBuckets); i++) { - if (gMemCpyBuckets[i]) { - SkDebugf(" %d:%d", i, gMemCpyBuckets[i]); - } - } - } -} -#else -#define register_memset_count(n) -#define register_memcpy_count(n) -#endif - - -/////////////////////////////////////////////////////////////////////////////// - -size_t SkPackBits::ComputeMaxSize16(int count) { - // worst case is the number of 16bit values (times 2) + - // 1 byte per (up to) 128 entries. - return ((count + 127) >> 7) + (count << 1); -} - size_t SkPackBits::ComputeMaxSize8(int count) { // worst case is the number of 8bit values + 1 byte per (up to) 128 entries. return ((count + 127) >> 7) + count; } -static uint8_t* flush_same16(uint8_t dst[], uint16_t value, int count) { +static uint8_t* flush_same8(uint8_t dst[], uint8_t value, size_t count) { while (count > 0) { - int n = count; - if (n > 128) { - n = 128; - } + int n = count > 128 ? 128 : count; *dst++ = (uint8_t)(n - 1); - *dst++ = (uint8_t)(value >> 8); *dst++ = (uint8_t)value; count -= n; } return dst; } -static uint8_t* flush_same8(uint8_t dst[], uint8_t value, int count) { - while (count > 0) { - int n = count; - if (n > 128) { - n = 128; - } - *dst++ = (uint8_t)(n - 1); - *dst++ = (uint8_t)value; - count -= n; - } - return dst; -} - -static uint8_t* flush_diff16(uint8_t* SK_RESTRICT dst, - const uint16_t* SK_RESTRICT src, int count) { - while (count > 0) { - int n = count; - if (n > 128) { - n = 128; - } - *dst++ = (uint8_t)(n + 127); - PB_MEMCPY(dst, src, n * sizeof(uint16_t)); - src += n; - dst += n * sizeof(uint16_t); - count -= n; - } - return dst; -} - static uint8_t* flush_diff8(uint8_t* SK_RESTRICT dst, - const uint8_t* SK_RESTRICT src, int count) { + const uint8_t* SK_RESTRICT src, size_t count) { while (count > 0) { - int n = count; - if (n > 128) { - n = 128; - } + int n = count > 128 ? 128 : count; *dst++ = (uint8_t)(n + 127); - PB_MEMCPY(dst, src, n); + memcpy(dst, src, n); src += n; dst += n; count -= n; @@ -190,64 +34,20 @@ static uint8_t* flush_diff8(uint8_t* SK_RESTRICT dst, return dst; } -size_t SkPackBits::Pack16(const uint16_t* SK_RESTRICT src, int count, - uint8_t* SK_RESTRICT dst) { - uint8_t* origDst = dst; - const uint16_t* stop = src + count; - - for (;;) { - count = SkToInt(stop - src); - SkASSERT(count >= 0); - if (count == 0) { - return dst - origDst; - } - if (1 == count) { - *dst++ = 0; - *dst++ = (uint8_t)(*src >> 8); - *dst++ = (uint8_t)*src; - return dst - origDst; - } - - unsigned value = *src; - const uint16_t* s = src + 1; - - if (*s == value) { // accumulate same values... - do { - s++; - if (s == stop) { - break; - } - } while (*s == value); - dst = flush_same16(dst, value, SkToInt(s - src)); - } else { // accumulate diff values... - do { - if (++s == stop) { - goto FLUSH_DIFF; - } - } while (*s != s[-1]); - s -= 1; // back up so we don't grab one of the "same" values that follow - FLUSH_DIFF: - dst = flush_diff16(dst, src, SkToInt(s - src)); - } - src = s; +size_t SkPackBits::Pack8(const uint8_t* SK_RESTRICT src, size_t srcSize, + uint8_t* SK_RESTRICT dst, size_t dstSize) { + if (dstSize < ComputeMaxSize8(srcSize)) { + return 0; } -} -size_t SkPackBits::Pack8(const uint8_t* SK_RESTRICT src, int count, - uint8_t* SK_RESTRICT dst) { - uint8_t* origDst = dst; - const uint8_t* stop = src + count; + uint8_t* const origDst = dst; + const uint8_t* stop = src + srcSize; - for (;;) { - count = SkToInt(stop - src); - SkASSERT(count >= 0); - if (count == 0) { - return dst - origDst; - } + for (intptr_t count = stop - src; count > 0; count = stop - src) { if (1 == count) { *dst++ = 0; *dst++ = *src; - return dst - origDst; + break; } unsigned value = *src; @@ -275,137 +75,35 @@ size_t SkPackBits::Pack8(const uint8_t* SK_RESTRICT src, int count, } src = s; } + return dst - origDst; } #include "SkUtils.h" -int SkPackBits::Unpack16(const uint8_t* SK_RESTRICT src, size_t srcSize, - uint16_t* SK_RESTRICT dst) { - uint16_t* origDst = dst; - const uint8_t* stop = src + srcSize; - - while (src < stop) { - unsigned n = *src++; - if (n <= 127) { // repeat count (n + 1) - n += 1; - sk_memset16(dst, (src[0] << 8) | src[1], n); - src += 2; - } else { // same count (n - 127) - n -= 127; - PB_MEMCPY(dst, src, n * sizeof(uint16_t)); - src += n * sizeof(uint16_t); - } - dst += n; - } - SkASSERT(src == stop); - return SkToInt(dst - origDst); -} - int SkPackBits::Unpack8(const uint8_t* SK_RESTRICT src, size_t srcSize, - uint8_t* SK_RESTRICT dst) { - uint8_t* origDst = dst; + uint8_t* SK_RESTRICT dst, size_t dstSize) { + uint8_t* const origDst = dst; + uint8_t* const endDst = dst + dstSize; const uint8_t* stop = src + srcSize; while (src < stop) { unsigned n = *src++; if (n <= 127) { // repeat count (n + 1) n += 1; - PB_MEMSET(dst, *src++, n); - } else { // same count (n - 127) - n -= 127; - PB_MEMCPY(dst, src, n); - src += n; - } - dst += n; - } - SkASSERT(src == stop); - return SkToInt(dst - origDst); -} - -enum UnpackState { - CLEAN_STATE, - REPEAT_BYTE_STATE, - COPY_SRC_STATE -}; - -void SkPackBits::Unpack8(uint8_t* SK_RESTRICT dst, size_t dstSkip, - size_t dstWrite, const uint8_t* SK_RESTRICT src) { - if (dstWrite == 0) { - return; - } - - UnpackState state = CLEAN_STATE; - size_t stateCount = 0; - - // state 1: do the skip-loop - while (dstSkip > 0) { - size_t n = *src++; - if (n <= 127) { // repeat count (n + 1) - n += 1; - if (n > dstSkip) { - state = REPEAT_BYTE_STATE; - stateCount = n - dstSkip; - n = dstSkip; - // we don't increment src here, since its needed in stage 2 - } else { - src++; // skip the src byte - } - } else { // same count (n - 127) - n -= 127; - if (n > dstSkip) { - state = COPY_SRC_STATE; - stateCount = n - dstSkip; - n = dstSkip; - } - src += n; - } - dstSkip -= n; - } - - // stage 2: perform any catchup from the skip-stage - if (stateCount > dstWrite) { - stateCount = dstWrite; - } - switch (state) { - case REPEAT_BYTE_STATE: - SkASSERT(stateCount > 0); - register_memset_count(stateCount); - PB_MEMSET(dst, *src++, stateCount); - break; - case COPY_SRC_STATE: - SkASSERT(stateCount > 0); - register_memcpy_count(stateCount); - PB_MEMCPY(dst, src, stateCount); - src += stateCount; - break; - default: - SkASSERT(stateCount == 0); - break; - } - dst += stateCount; - dstWrite -= stateCount; - - // copy at most dstWrite bytes into dst[] - while (dstWrite > 0) { - size_t n = *src++; - if (n <= 127) { // repeat count (n + 1) - n += 1; - if (n > dstWrite) { - n = dstWrite; + if (dst >(endDst - n)) { + return 0; } - register_memset_count(n); - PB_MEMSET(dst, *src++, n); + memset(dst, *src++, n); } else { // same count (n - 127) n -= 127; - if (n > dstWrite) { - n = dstWrite; + if (dst > (endDst - n)) { + return 0; } - register_memcpy_count(n); - PB_MEMCPY(dst, src, n); + memcpy(dst, src, n); src += n; } dst += n; - dstWrite -= n; } - SkASSERT(0 == dstWrite); + SkASSERT(src <= stop); + return SkToInt(dst - origDst); } diff --git a/chromium/third_party/skia/src/core/SkPictureShader.cpp b/chromium/third_party/skia/src/core/SkPictureShader.cpp index c1c47550586..7efef21e70c 100644 --- a/chromium/third_party/skia/src/core/SkPictureShader.cpp +++ b/chromium/third_party/skia/src/core/SkPictureShader.cpp @@ -122,6 +122,8 @@ SkPictureShader* SkPictureShader::Create(const SkPicture* picture, TileMode tmx, return SkNEW_ARGS(SkPictureShader, (picture, tmx, tmy, localMatrix, tile)); } +// TODO: rename SK_DISALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS to SK_DISALLOW_CROSSPROCESS_PICTURES + SkFlattenable* SkPictureShader::CreateProc(SkReadBuffer& buffer) { SkMatrix lm; buffer.readMatrix(&lm); @@ -129,7 +131,27 @@ SkFlattenable* SkPictureShader::CreateProc(SkReadBuffer& buffer) { TileMode my = (TileMode)buffer.read32(); SkRect tile; buffer.readRect(&tile); - SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromBuffer(buffer)); + + SkAutoTUnref<SkPicture> picture; +#ifdef SK_DISALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS + if (buffer.isCrossProcess()) { + if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version)) { + // Older code blindly serialized pictures. We don't trust them. + buffer.validate(false); + return NULL; + } + // Newer code won't serialize pictures in disallow-cross-process-picture mode. + // Assert that they didn't serialize anything except a false here. + buffer.validate(!buffer.readBool()); + } else +#endif + { + // Old code always serialized the picture. New code writes a 'true' first if it did. + if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version) || + buffer.readBool()) { + picture.reset(SkPicture::CreateFromBuffer(buffer)); + } + } return SkPictureShader::Create(picture, mx, my, &lm, &tile); } @@ -138,7 +160,18 @@ void SkPictureShader::flatten(SkWriteBuffer& buffer) const { buffer.write32(fTmx); buffer.write32(fTmy); buffer.writeRect(fTile); - fPicture->flatten(buffer); + +#ifdef SK_DISALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS + // The deserialization code won't trust that our serialized picture is safe to deserialize. + // So write a 'false' telling it that we're not serializing a picture. + if (buffer.isCrossProcess()) { + buffer.writeBool(false); + } else +#endif + { + buffer.writeBool(true); + fPicture->flatten(buffer); + } } SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatrix* localM, diff --git a/chromium/third_party/skia/src/core/SkReadBuffer.h b/chromium/third_party/skia/src/core/SkReadBuffer.h index 1299edaf82b..ba47835daf8 100644 --- a/chromium/third_party/skia/src/core/SkReadBuffer.h +++ b/chromium/third_party/skia/src/core/SkReadBuffer.h @@ -56,7 +56,8 @@ public: kPictureImageFilterResolution_Version = 38, kPictureImageFilterLevel_Version = 39, kImageFilterNoUniqueID_Version = 40, - kBitmapSourceFilterQuality_Version = 41 + kBitmapSourceFilterQuality_Version = 41, + kPictureShaderHasPictureBool_Version = 42, }; /** diff --git a/chromium/third_party/skia/src/effects/SkTableColorFilter.cpp b/chromium/third_party/skia/src/effects/SkTableColorFilter.cpp index 7298960a086..8e54a014779 100644 --- a/chromium/third_party/skia/src/effects/SkTableColorFilter.cpp +++ b/chromium/third_party/skia/src/effects/SkTableColorFilter.cpp @@ -201,8 +201,8 @@ static const uint8_t gCountNibBits[] = { void SkTable_ColorFilter::flatten(SkWriteBuffer& buffer) const { uint8_t storage[5*256]; int count = gCountNibBits[fFlags & 0xF]; - size_t size = SkPackBits::Pack8(fStorage, count * 256, storage); - SkASSERT(size <= sizeof(storage)); + size_t size = SkPackBits::Pack8(fStorage, count * 256, storage, + sizeof(storage)); buffer.write32(fFlags); buffer.writeByteArray(storage, size); @@ -223,7 +223,8 @@ SkFlattenable* SkTable_ColorFilter::CreateProc(SkReadBuffer& buffer) { } uint8_t unpackedStorage[4*256]; - size_t unpackedSize = SkPackBits::Unpack8(packedStorage, packedSize, unpackedStorage); + size_t unpackedSize = SkPackBits::Unpack8(packedStorage, packedSize, + unpackedStorage, sizeof(unpackedStorage)); // now check that we got the size we expected if (!buffer.validate(unpackedSize == count*256)) { return NULL; diff --git a/chromium/third_party/undoview/LICENSE b/chromium/third_party/undoview/LICENSE deleted file mode 100644 index 4362b49151d..00000000000 --- a/chromium/third_party/undoview/LICENSE +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - <one line to give the library's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - 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.1 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 - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - <signature of Ty Coon>, 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/chromium/third_party/undoview/README.chromium b/chromium/third_party/undoview/README.chromium deleted file mode 100644 index 032e2b33c08..00000000000 --- a/chromium/third_party/undoview/README.chromium +++ /dev/null @@ -1,13 +0,0 @@ -Name: undoview -Short Name: undoview -URL: http://projects.gnome.org/gtksourceview, http://www.gtk.org -Version: unknown -License: LGPL 2.1 -Security Critical: yes - -This directory provides a new class, GtkUndoView. It is based on GtkTextView -from the GTK+ project, but with undo/redo support added. The code to add this -support is borrowed from the GtkSourceView project. Since GtkSourceView has a -lot of other stuff we don't want, we borrow only the undo/redo support and add -it to GtkTextView to create GtkUndoView. - diff --git a/chromium/third_party/undoview/undo_manager.c b/chromium/third_party/undoview/undo_manager.c deleted file mode 100644 index 6efba9c848a..00000000000 --- a/chromium/third_party/undoview/undo_manager.c +++ /dev/null @@ -1,1110 +0,0 @@ -/* - * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence - * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi - * Copyright (C) 2002-2005 Paolo Maggi - * - * 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.1 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 - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <glib.h> -#include <stdlib.h> -#include <string.h> - -#include "undo_manager.h" - -#define DEFAULT_MAX_UNDO_LEVELS 25 - -typedef struct _GtkSourceUndoAction GtkSourceUndoAction; -typedef struct _GtkSourceUndoInsertAction GtkSourceUndoInsertAction; -typedef struct _GtkSourceUndoDeleteAction GtkSourceUndoDeleteAction; - -typedef enum { - GTK_SOURCE_UNDO_ACTION_INSERT, - GTK_SOURCE_UNDO_ACTION_DELETE, -} GtkSourceUndoActionType; - -/* - * We use offsets instead of GtkTextIters because the last ones - * require to much memory in this context without giving us any advantage. - */ - -struct _GtkSourceUndoInsertAction { - gint pos; - gchar *text; - gint length; - gint chars; -}; - -struct _GtkSourceUndoDeleteAction { - gint start; - gint end; - gchar *text; - gboolean forward; -}; - -struct _GtkSourceUndoAction { - GtkSourceUndoActionType action_type; - - union { - GtkSourceUndoInsertAction insert; - GtkSourceUndoDeleteAction delete; - } action; - - gint order_in_group; - - /* It is TRUE whether the action can be merged with the following action. */ - guint mergeable : 1; - - /* It is TRUE whether the action is marked as "modified". - * An action is marked as "modified" if it changed the - * state of the buffer from "not modified" to "modified". Only the first - * action of a group can be marked as modified. - * There can be a single action marked as "modified" in the actions list. - */ - guint modified : 1; -}; - -/* INVALID is a pointer to an invalid action */ -#define INVALID ((void *) "IA") - -struct _GtkSourceUndoManagerPrivate { - GtkTextBuffer *document; - - GList* actions; - gint next_redo; - - gint actions_in_current_group; - - gint running_not_undoable_actions; - - gint num_of_groups; - - gint max_undo_levels; - - guint can_undo : 1; - guint can_redo : 1; - - /* It is TRUE whether, while undoing an action of the current group (with order_in_group > 1), - * the state of the buffer changed from "not modified" to "modified". - */ - guint modified_undoing_group : 1; - - /* Pointer to the action (in the action list) marked as "modified". - * It is NULL when no action is marked as "modified". - * It is INVALID when the action marked as "modified" has been removed - * from the action list (freeing the list or resizing it) */ - GtkSourceUndoAction *modified_action; -}; - -enum { - CAN_UNDO, - CAN_REDO, - LAST_SIGNAL -}; - -#if !defined(NDEBUG) -static void -print_state(GtkSourceUndoManager* um) -{ - fprintf(stderr, "\n***\n"); - GList* actions = um->priv->actions; - - for (; actions; actions = g_list_next(actions)) { - GtkSourceUndoAction* act = actions->data; - fprintf(stderr, "* type = %s\n", act->action_type == GTK_SOURCE_UNDO_ACTION_DELETE ? - "delete" : "insert"); - - fprintf(stderr, "\ttext = %s\n", act->action_type == GTK_SOURCE_UNDO_ACTION_DELETE - ? act->action.delete.text : act->action.insert.text); - fprintf(stderr, "\torder = %d\n", act->order_in_group); - } - - fprintf(stderr, "* next redo: %d\n", um->priv->next_redo); - fprintf(stderr, "* num of groups: %d\n", um->priv->num_of_groups); - fprintf(stderr, "* actions in group: %d\n", um->priv->actions_in_current_group); -} -#endif - -static void gtk_source_undo_manager_class_init(GtkSourceUndoManagerClass *klass); -static void gtk_source_undo_manager_init(GtkSourceUndoManager *um); -static void gtk_source_undo_manager_finalize(GObject *object); - -static void gtk_source_undo_manager_insert_text_handler(GtkTextBuffer *buffer, - GtkTextIter *pos, - const gchar *text, - gint length, - GtkSourceUndoManager *um); -static void gtk_source_undo_manager_delete_range_handler(GtkTextBuffer *buffer, - GtkTextIter *start, - GtkTextIter *end, - GtkSourceUndoManager *um); -static void gtk_source_undo_manager_begin_user_action_handler(GtkTextBuffer *buffer, - GtkSourceUndoManager *um); -static void gtk_source_undo_manager_modified_changed_handler(GtkTextBuffer *buffer, - GtkSourceUndoManager *um); - -static void gtk_source_undo_manager_free_action_list(GtkSourceUndoManager *um); - -static void gtk_source_undo_manager_add_action(GtkSourceUndoManager *um, - const GtkSourceUndoAction *undo_action); -static void gtk_source_undo_manager_free_first_n_actions(GtkSourceUndoManager *um, - gint n); -static void gtk_source_undo_manager_check_list_size(GtkSourceUndoManager *um); - -static gboolean gtk_source_undo_manager_merge_action(GtkSourceUndoManager *um, - const GtkSourceUndoAction *undo_action); - -static GObjectClass *parent_class = NULL; -static guint undo_manager_signals [LAST_SIGNAL] = { 0 }; - -GType -gtk_source_undo_manager_get_type(void) { - static GType undo_manager_type = 0; - - if(undo_manager_type == 0) - { - static const GTypeInfo our_info = - { - sizeof(GtkSourceUndoManagerClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) gtk_source_undo_manager_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof(GtkSourceUndoManager), - 0, /* n_preallocs */ - (GInstanceInitFunc) gtk_source_undo_manager_init, - NULL /* value_table */ - }; - - undo_manager_type = g_type_register_static(G_TYPE_OBJECT, - "GtkSourceUndoManager", - &our_info, - 0); - } - - return undo_manager_type; -} - -static void -gtk_source_undo_manager_class_init(GtkSourceUndoManagerClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS(klass); - - parent_class = g_type_class_peek_parent(klass); - - object_class->finalize = gtk_source_undo_manager_finalize; - - klass->can_undo = NULL; - klass->can_redo = NULL; - - undo_manager_signals[CAN_UNDO] = - g_signal_new("can_undo", - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GtkSourceUndoManagerClass, can_undo), - NULL, NULL, - g_cclosure_marshal_VOID__BOOLEAN, - G_TYPE_NONE, - 1, - G_TYPE_BOOLEAN); - - undo_manager_signals[CAN_REDO] = - g_signal_new("can_redo", - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GtkSourceUndoManagerClass, can_redo), - NULL, NULL, - g_cclosure_marshal_VOID__BOOLEAN, - G_TYPE_NONE, - 1, - G_TYPE_BOOLEAN); -} - -static void -gtk_source_undo_manager_init(GtkSourceUndoManager *um) { - um->priv = g_new0(GtkSourceUndoManagerPrivate, 1); - - um->priv->actions = NULL; - um->priv->next_redo = 0; - - um->priv->can_undo = FALSE; - um->priv->can_redo = FALSE; - - um->priv->running_not_undoable_actions = 0; - - um->priv->num_of_groups = 0; - - um->priv->max_undo_levels = DEFAULT_MAX_UNDO_LEVELS; - - um->priv->modified_action = NULL; - - um->priv->modified_undoing_group = FALSE; -} - -static void -gtk_source_undo_manager_finalize(GObject *object) { - GtkSourceUndoManager *um; - - g_return_if_fail(object != NULL); - g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(object)); - - um = GTK_SOURCE_UNDO_MANAGER(object); - - g_return_if_fail(um->priv != NULL); - - if(um->priv->actions != NULL) - gtk_source_undo_manager_free_action_list(um); - - g_signal_handlers_disconnect_by_func(G_OBJECT(um->priv->document), - G_CALLBACK(gtk_source_undo_manager_delete_range_handler), - um); - - g_signal_handlers_disconnect_by_func(G_OBJECT(um->priv->document), - G_CALLBACK(gtk_source_undo_manager_insert_text_handler), - um); - - g_signal_handlers_disconnect_by_func(G_OBJECT(um->priv->document), - G_CALLBACK(gtk_source_undo_manager_begin_user_action_handler), - um); - - g_signal_handlers_disconnect_by_func(G_OBJECT(um->priv->document), - G_CALLBACK(gtk_source_undo_manager_modified_changed_handler), - um); - - g_free(um->priv); - - G_OBJECT_CLASS(parent_class)->finalize(object); -} - -GtkSourceUndoManager* -gtk_source_undo_manager_new(GtkTextBuffer* buffer) { - GtkSourceUndoManager *um; - - um = GTK_SOURCE_UNDO_MANAGER(g_object_new(GTK_SOURCE_TYPE_UNDO_MANAGER, NULL)); - - g_return_val_if_fail(um->priv != NULL, NULL); - um->priv->document = buffer; - - g_signal_connect(G_OBJECT(buffer), "insert_text", - G_CALLBACK(gtk_source_undo_manager_insert_text_handler), - um); - - g_signal_connect(G_OBJECT(buffer), "delete_range", - G_CALLBACK(gtk_source_undo_manager_delete_range_handler), - um); - - g_signal_connect(G_OBJECT(buffer), "begin_user_action", - G_CALLBACK(gtk_source_undo_manager_begin_user_action_handler), - um); - - g_signal_connect(G_OBJECT(buffer), "modified_changed", - G_CALLBACK(gtk_source_undo_manager_modified_changed_handler), - um); - return um; -} - -void -gtk_source_undo_manager_begin_not_undoable_action(GtkSourceUndoManager *um) { - g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um)); - g_return_if_fail(um->priv != NULL); - - ++um->priv->running_not_undoable_actions; -} - -static void -gtk_source_undo_manager_end_not_undoable_action_internal(GtkSourceUndoManager *um) { - g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um)); - g_return_if_fail(um->priv != NULL); - - g_return_if_fail(um->priv->running_not_undoable_actions > 0); - - --um->priv->running_not_undoable_actions; -} - -void -gtk_source_undo_manager_end_not_undoable_action(GtkSourceUndoManager *um) { - g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um)); - g_return_if_fail(um->priv != NULL); - - gtk_source_undo_manager_end_not_undoable_action_internal(um); - - if(um->priv->running_not_undoable_actions == 0) - { - gtk_source_undo_manager_free_action_list(um); - - um->priv->next_redo = -1; - - if(um->priv->can_undo) - { - um->priv->can_undo = FALSE; - g_signal_emit(G_OBJECT(um), - undo_manager_signals [CAN_UNDO], - 0, - FALSE); - } - - if(um->priv->can_redo) - { - um->priv->can_redo = FALSE; - g_signal_emit(G_OBJECT(um), - undo_manager_signals [CAN_REDO], - 0, - FALSE); - } - } -} - -gboolean -gtk_source_undo_manager_can_undo(const GtkSourceUndoManager *um) { - g_return_val_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um), FALSE); - g_return_val_if_fail(um->priv != NULL, FALSE); - - return um->priv->can_undo; -} - -gboolean -gtk_source_undo_manager_can_redo(const GtkSourceUndoManager *um) { - g_return_val_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um), FALSE); - g_return_val_if_fail(um->priv != NULL, FALSE); - - return um->priv->can_redo; -} - -static void -set_cursor(GtkTextBuffer *buffer, gint cursor) { - GtkTextIter iter; - - /* Place the cursor at the requested position */ - gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor); - gtk_text_buffer_place_cursor(buffer, &iter); -} - -static void -insert_text(GtkTextBuffer *buffer, gint pos, const gchar *text, gint len) { - GtkTextIter iter; - - gtk_text_buffer_get_iter_at_offset(buffer, &iter, pos); - gtk_text_buffer_insert(buffer, &iter, text, len); -} - -static void -delete_text(GtkTextBuffer *buffer, gint start, gint end) { - GtkTextIter start_iter; - GtkTextIter end_iter; - - gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start); - - if(end < 0) - gtk_text_buffer_get_end_iter(buffer, &end_iter); - else - gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end); - - gtk_text_buffer_delete(buffer, &start_iter, &end_iter); -} - -static gchar* -get_chars(GtkTextBuffer *buffer, gint start, gint end) { - GtkTextIter start_iter; - GtkTextIter end_iter; - - gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start); - - if(end < 0) - gtk_text_buffer_get_end_iter(buffer, &end_iter); - else - gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end); - - return gtk_text_buffer_get_slice(buffer, &start_iter, &end_iter, TRUE); -} - -void -gtk_source_undo_manager_undo(GtkSourceUndoManager *um) { - GtkSourceUndoAction *undo_action; - gboolean modified = FALSE; - - g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um)); - g_return_if_fail(um->priv != NULL); - g_return_if_fail(um->priv->can_undo); - - um->priv->modified_undoing_group = FALSE; - - gtk_source_undo_manager_begin_not_undoable_action(um); - - do - { - undo_action = g_list_nth_data(um->priv->actions, um->priv->next_redo + 1); - g_return_if_fail(undo_action != NULL); - - /* undo_action->modified can be TRUE only if undo_action->order_in_group <= 1 */ - g_return_if_fail((undo_action->order_in_group <= 1) || - ((undo_action->order_in_group > 1) && !undo_action->modified)); - - if(undo_action->order_in_group <= 1) - { - /* Set modified to TRUE only if the buffer did not change its state from - * "not modified" to "modified" undoing an action(with order_in_group > 1) - * in current group. */ - modified =(undo_action->modified && !um->priv->modified_undoing_group); - } - - switch(undo_action->action_type) - { - case GTK_SOURCE_UNDO_ACTION_DELETE: - insert_text( - um->priv->document, - undo_action->action.delete.start, - undo_action->action.delete.text, - strlen(undo_action->action.delete.text)); - - if(undo_action->action.delete.forward) - set_cursor( - um->priv->document, - undo_action->action.delete.start); - else - set_cursor( - um->priv->document, - undo_action->action.delete.end); - - break; - - case GTK_SOURCE_UNDO_ACTION_INSERT: - delete_text( - um->priv->document, - undo_action->action.insert.pos, - undo_action->action.insert.pos + - undo_action->action.insert.chars); - - set_cursor( - um->priv->document, - undo_action->action.insert.pos); - break; - - default: - /* Unknown action type. */ - g_return_if_reached(); - } - - ++um->priv->next_redo; - - } while(undo_action->order_in_group > 1); - - if(modified) - { - --um->priv->next_redo; - gtk_text_buffer_set_modified(um->priv->document, FALSE); - ++um->priv->next_redo; - } - - gtk_source_undo_manager_end_not_undoable_action_internal(um); - - um->priv->modified_undoing_group = FALSE; - - if(!um->priv->can_redo) - { - um->priv->can_redo = TRUE; - g_signal_emit(G_OBJECT(um), - undo_manager_signals [CAN_REDO], - 0, - TRUE); - } - - if(um->priv->next_redo >=(gint)(g_list_length(um->priv->actions) - 1)) - { - um->priv->can_undo = FALSE; - g_signal_emit(G_OBJECT(um), - undo_manager_signals [CAN_UNDO], - 0, - FALSE); - } -} - -void -gtk_source_undo_manager_redo(GtkSourceUndoManager *um) { - GtkSourceUndoAction *undo_action; - gboolean modified = FALSE; - - g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um)); - g_return_if_fail(um->priv != NULL); - g_return_if_fail(um->priv->can_redo); - - undo_action = g_list_nth_data(um->priv->actions, um->priv->next_redo); - g_return_if_fail(undo_action != NULL); - - gtk_source_undo_manager_begin_not_undoable_action(um); - - do - { - if(undo_action->modified) - { - g_return_if_fail(undo_action->order_in_group <= 1); - modified = TRUE; - } - - --um->priv->next_redo; - - switch(undo_action->action_type) - { - case GTK_SOURCE_UNDO_ACTION_DELETE: - delete_text( - um->priv->document, - undo_action->action.delete.start, - undo_action->action.delete.end); - - set_cursor( - um->priv->document, - undo_action->action.delete.start); - - break; - - case GTK_SOURCE_UNDO_ACTION_INSERT: - set_cursor( - um->priv->document, - undo_action->action.insert.pos); - - insert_text( - um->priv->document, - undo_action->action.insert.pos, - undo_action->action.insert.text, - undo_action->action.insert.length); - - break; - - default: - /* Unknown action type */ - ++um->priv->next_redo; - g_return_if_reached(); - } - - if(um->priv->next_redo < 0) - undo_action = NULL; - else - undo_action = g_list_nth_data(um->priv->actions, um->priv->next_redo); - - } while((undo_action != NULL) &&(undo_action->order_in_group > 1)); - - if(modified) - { - ++um->priv->next_redo; - gtk_text_buffer_set_modified(um->priv->document, FALSE); - --um->priv->next_redo; - } - - gtk_source_undo_manager_end_not_undoable_action_internal(um); - - if(um->priv->next_redo < 0) - { - um->priv->can_redo = FALSE; - g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_REDO], 0, FALSE); - } - - if(!um->priv->can_undo) - { - um->priv->can_undo = TRUE; - g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_UNDO], 0, TRUE); - } -} - -static void -gtk_source_undo_action_free(GtkSourceUndoAction *action) { - if(action == NULL) - return; - - if(action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT) - g_free(action->action.insert.text); - else if(action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE) - g_free(action->action.delete.text); - else - g_return_if_reached(); - - g_free(action); -} - -static void -gtk_source_undo_manager_free_action_list(GtkSourceUndoManager *um) { - GList *l; - - l = um->priv->actions; - - while(l != NULL) - { - GtkSourceUndoAction *action = l->data; - - if(action->order_in_group == 1) - --um->priv->num_of_groups; - um->priv->actions_in_current_group = action->order_in_group - 1; - - if(action->modified) - um->priv->modified_action = INVALID; - - gtk_source_undo_action_free(action); - - l = g_list_next(l); - } - - g_list_free(um->priv->actions); - um->priv->actions = NULL; -} - -static void -gtk_source_undo_manager_insert_text_handler(GtkTextBuffer *buffer, - GtkTextIter *pos, - const gchar *text, - gint length, - GtkSourceUndoManager *um) { - GtkSourceUndoAction undo_action; - - if(um->priv->running_not_undoable_actions > 0) - return; - - undo_action.action_type = GTK_SOURCE_UNDO_ACTION_INSERT; - - undo_action.action.insert.pos = gtk_text_iter_get_offset(pos); - undo_action.action.insert.text =(gchar*) text; - undo_action.action.insert.length = length; - undo_action.action.insert.chars = g_utf8_strlen(text, length); - - if((undo_action.action.insert.chars > 1) ||(g_utf8_get_char(text) == '\n')) - - undo_action.mergeable = FALSE; - else - undo_action.mergeable = TRUE; - - undo_action.modified = FALSE; - - gtk_source_undo_manager_add_action(um, &undo_action); -} - -static void -gtk_source_undo_manager_delete_range_handler(GtkTextBuffer *buffer, - GtkTextIter *start, - GtkTextIter *end, - GtkSourceUndoManager *um) { - GtkSourceUndoAction undo_action; - GtkTextIter insert_iter; - - if(um->priv->running_not_undoable_actions > 0) - return; - - undo_action.action_type = GTK_SOURCE_UNDO_ACTION_DELETE; - - gtk_text_iter_order(start, end); - - undo_action.action.delete.start = gtk_text_iter_get_offset(start); - undo_action.action.delete.end = gtk_text_iter_get_offset(end); - - undo_action.action.delete.text = get_chars( - buffer, - undo_action.action.delete.start, - undo_action.action.delete.end); - - /* figure out if the user used the Delete or the Backspace key */ - gtk_text_buffer_get_iter_at_mark(buffer, &insert_iter, - gtk_text_buffer_get_insert(buffer)); - if(gtk_text_iter_get_offset(&insert_iter) <= undo_action.action.delete.start) - undo_action.action.delete.forward = TRUE; - else - undo_action.action.delete.forward = FALSE; - - if(((undo_action.action.delete.end - undo_action.action.delete.start) > 1) || - (g_utf8_get_char(undo_action.action.delete.text ) == '\n')) - undo_action.mergeable = FALSE; - else - undo_action.mergeable = TRUE; - - undo_action.modified = FALSE; - - gtk_source_undo_manager_add_action(um, &undo_action); - - g_free(undo_action.action.delete.text); - -} - -static void -gtk_source_undo_manager_begin_user_action_handler(GtkTextBuffer *buffer, GtkSourceUndoManager *um) { - g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um)); - g_return_if_fail(um->priv != NULL); - - if(um->priv->running_not_undoable_actions > 0) - return; - - um->priv->actions_in_current_group = 0; -} - -static void -gtk_source_undo_manager_add_action(GtkSourceUndoManager *um, - const GtkSourceUndoAction *undo_action) { - GtkSourceUndoAction* action; - - if(um->priv->next_redo >= 0) - { - gtk_source_undo_manager_free_first_n_actions(um, um->priv->next_redo + 1); - } - - um->priv->next_redo = -1; - - if(!gtk_source_undo_manager_merge_action(um, undo_action)) - { - action = g_new(GtkSourceUndoAction, 1); - *action = *undo_action; - - if(action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT) - action->action.insert.text = g_strndup(undo_action->action.insert.text, undo_action->action.insert.length); - else if(action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE) - action->action.delete.text = g_strdup(undo_action->action.delete.text); - else - { - g_free(action); - g_return_if_reached(); - } - - ++um->priv->actions_in_current_group; - action->order_in_group = um->priv->actions_in_current_group; - - if(action->order_in_group == 1) - ++um->priv->num_of_groups; - - um->priv->actions = g_list_prepend(um->priv->actions, action); - } - - gtk_source_undo_manager_check_list_size(um); - - if(!um->priv->can_undo) - { - um->priv->can_undo = TRUE; - g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_UNDO], 0, TRUE); - } - - if(um->priv->can_redo) - { - um->priv->can_redo = FALSE; - g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_REDO], 0, FALSE); - } -} - -static void -gtk_source_undo_manager_free_first_n_actions(GtkSourceUndoManager *um, - gint n) { - gint i; - - if(um->priv->actions == NULL) - return; - - for(i = 0; i < n; i++) - { - GtkSourceUndoAction *action = g_list_first(um->priv->actions)->data; - - if(action->order_in_group == 1) - --um->priv->num_of_groups; - um->priv->actions_in_current_group = action->order_in_group - 1; - - if(action->modified) - um->priv->modified_action = INVALID; - - gtk_source_undo_action_free(action); - - um->priv->actions = g_list_delete_link(um->priv->actions, - um->priv->actions); - - if(um->priv->actions == NULL) - return; - } -} - -static void -gtk_source_undo_manager_check_list_size(GtkSourceUndoManager *um) { - gint undo_levels; - - g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um)); - g_return_if_fail(um->priv != NULL); - - undo_levels = gtk_source_undo_manager_get_max_undo_levels(um); - - if(undo_levels < 1) - return; - - if(um->priv->num_of_groups > undo_levels) - { - GtkSourceUndoAction *undo_action; - GList *last; - - last = g_list_last(um->priv->actions); - undo_action =(GtkSourceUndoAction*) last->data; - - do - { - GList *tmp; - - if(undo_action->order_in_group == 1) - --um->priv->num_of_groups; - um->priv->actions_in_current_group = undo_action->order_in_group - 1; - - if(undo_action->modified) - um->priv->modified_action = INVALID; - - gtk_source_undo_action_free(undo_action); - - tmp = g_list_previous(last); - um->priv->actions = g_list_delete_link(um->priv->actions, last); - last = tmp; - g_return_if_fail(last != NULL); - - undo_action =(GtkSourceUndoAction*) last->data; - - } while((undo_action->order_in_group > 1) || - (um->priv->num_of_groups > undo_levels)); - } -} - -/** - * gtk_source_undo_manager_merge_action: - * @um: a #GtkSourceUndoManager. - * @undo_action: a #GtkSourceUndoAction. - * - * This function tries to merge the undo action at the top of - * the stack with a new undo action. So when we undo for example - * typing, we can undo the whole word and not each letter by itself. - * - * Return Value: %TRUE is merge was successful, %FALSE otherwise. - **/ -static gboolean -gtk_source_undo_manager_merge_action(GtkSourceUndoManager *um, - const GtkSourceUndoAction *undo_action) { - GtkSourceUndoAction *last_action; - - g_return_val_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um), FALSE); - g_return_val_if_fail(um->priv != NULL, FALSE); - - if(um->priv->actions == NULL) - return FALSE; - - last_action =(GtkSourceUndoAction*) g_list_nth_data(um->priv->actions, 0); - - if(!last_action->mergeable) - return FALSE; - - if((!undo_action->mergeable) || - (undo_action->action_type != last_action->action_type)) - { - last_action->mergeable = FALSE; - return FALSE; - } - - if(undo_action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE) - { - if((last_action->action.delete.forward != undo_action->action.delete.forward) || - ((last_action->action.delete.start != undo_action->action.delete.start) && - (last_action->action.delete.start != undo_action->action.delete.end))) - { - last_action->mergeable = FALSE; - return FALSE; - } - - if(last_action->action.delete.start == undo_action->action.delete.start) - { - gchar *str; - -#define L (last_action->action.delete.end - last_action->action.delete.start - 1) -#define g_utf8_get_char_at(p,i) g_utf8_get_char(g_utf8_offset_to_pointer((p),(i))) - - /* Deleted with the delete key */ - if((g_utf8_get_char(undo_action->action.delete.text) != ' ') && - (g_utf8_get_char(undo_action->action.delete.text) != '\t') && - ((g_utf8_get_char_at(last_action->action.delete.text, L) == ' ') || - (g_utf8_get_char_at(last_action->action.delete.text, L) == '\t'))) - { - last_action->mergeable = FALSE; - return FALSE; - } - - str = g_strdup_printf("%s%s", last_action->action.delete.text, - undo_action->action.delete.text); - - g_free(last_action->action.delete.text); - last_action->action.delete.end +=(undo_action->action.delete.end - - undo_action->action.delete.start); - last_action->action.delete.text = str; - } - else - { - gchar *str; - - /* Deleted with the backspace key */ - if((g_utf8_get_char(undo_action->action.delete.text) != ' ') && - (g_utf8_get_char(undo_action->action.delete.text) != '\t') && - ((g_utf8_get_char(last_action->action.delete.text) == ' ') || - (g_utf8_get_char(last_action->action.delete.text) == '\t'))) - { - last_action->mergeable = FALSE; - return FALSE; - } - - str = g_strdup_printf("%s%s", undo_action->action.delete.text, - last_action->action.delete.text); - - g_free(last_action->action.delete.text); - last_action->action.delete.start = undo_action->action.delete.start; - last_action->action.delete.text = str; - } - } - else if(undo_action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT) - { - gchar* str; - -#define I (last_action->action.insert.chars - 1) - - if((undo_action->action.insert.pos != - (last_action->action.insert.pos + last_action->action.insert.chars)) || - ((g_utf8_get_char(undo_action->action.insert.text) != ' ') && - (g_utf8_get_char(undo_action->action.insert.text) != '\t') && - ((g_utf8_get_char_at(last_action->action.insert.text, I) == ' ') || - (g_utf8_get_char_at(last_action->action.insert.text, I) == '\t'))) - ) - { - last_action->mergeable = FALSE; - return FALSE; - } - - str = g_strdup_printf("%s%s", last_action->action.insert.text, - undo_action->action.insert.text); - - g_free(last_action->action.insert.text); - last_action->action.insert.length += undo_action->action.insert.length; - last_action->action.insert.text = str; - last_action->action.insert.chars += undo_action->action.insert.chars; - - } - else - /* Unknown action inside undo merge encountered */ - g_return_val_if_reached(TRUE); - - return TRUE; -} - -gint -gtk_source_undo_manager_get_max_undo_levels(GtkSourceUndoManager *um) { - g_return_val_if_fail(um != NULL, 0); - g_return_val_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um), 0); - - return um->priv->max_undo_levels; -} - -void -gtk_source_undo_manager_set_max_undo_levels(GtkSourceUndoManager *um, - gint max_undo_levels) { - gint old_levels; - - g_return_if_fail(um != NULL); - g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um)); - - old_levels = um->priv->max_undo_levels; - um->priv->max_undo_levels = max_undo_levels; - - if(max_undo_levels < 1) - return; - - if(old_levels > max_undo_levels) - { - /* strip redo actions first */ - while(um->priv->next_redo >= 0 &&(um->priv->num_of_groups > max_undo_levels)) - { - gtk_source_undo_manager_free_first_n_actions(um, 1); - um->priv->next_redo--; - } - - /* now remove undo actions if necessary */ - gtk_source_undo_manager_check_list_size(um); - - /* emit "can_undo" and/or "can_redo" if appropiate */ - if(um->priv->next_redo < 0 && um->priv->can_redo) - { - um->priv->can_redo = FALSE; - g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_REDO], 0, FALSE); - } - - if(um->priv->can_undo && - um->priv->next_redo >=(gint)(g_list_length(um->priv->actions) - 1)) - { - um->priv->can_undo = FALSE; - g_signal_emit(G_OBJECT(um), undo_manager_signals [CAN_UNDO], 0, FALSE); - } - } -} - -static void -gtk_source_undo_manager_modified_changed_handler(GtkTextBuffer *buffer, - GtkSourceUndoManager *um) { - GtkSourceUndoAction *action; - GList *list; - - g_return_if_fail(GTK_SOURCE_IS_UNDO_MANAGER(um)); - g_return_if_fail(um->priv != NULL); - - if(um->priv->actions == NULL) - return; - - list = g_list_nth(um->priv->actions, um->priv->next_redo + 1); - - if(list != NULL) - action =(GtkSourceUndoAction*) list->data; - else - action = NULL; - - if(gtk_text_buffer_get_modified(buffer) == FALSE) - { - if(action != NULL) - action->mergeable = FALSE; - - if(um->priv->modified_action != NULL) - { - if(um->priv->modified_action != INVALID) - um->priv->modified_action->modified = FALSE; - - um->priv->modified_action = NULL; - } - - return; - } - - if(action == NULL) - { - g_return_if_fail(um->priv->running_not_undoable_actions > 0); - - return; - } - - /* gtk_text_buffer_get_modified(buffer) == TRUE */ - - g_return_if_fail(um->priv->modified_action == NULL); - - if(action->order_in_group > 1) - um->priv->modified_undoing_group = TRUE; - - while(action->order_in_group > 1) - { - list = g_list_next(list); - g_return_if_fail(list != NULL); - - action =(GtkSourceUndoAction*) list->data; - g_return_if_fail(action != NULL); - } - - action->modified = TRUE; - um->priv->modified_action = action; -} - diff --git a/chromium/third_party/undoview/undo_manager.h b/chromium/third_party/undoview/undo_manager.h deleted file mode 100644 index 5a18829eb82..00000000000 --- a/chromium/third_party/undoview/undo_manager.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence - * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi - * Copyright (C) 2002, 2003 Paolo Maggi - * - * 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.1 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 UNDOVIEW_UNDO_MANAGER_H_ -#define UNDOVIEW_UNDO_MANAGER_H_ - -#include <gtk/gtk.h> - -G_BEGIN_DECLS - -#define GTK_SOURCE_TYPE_UNDO_MANAGER (gtk_source_undo_manager_get_type()) - -#define GTK_SOURCE_UNDO_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManager)) - -#define GTK_SOURCE_UNDO_MANAGER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManagerClass)) - -#define GTK_SOURCE_IS_UNDO_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_SOURCE_TYPE_UNDO_MANAGER)) - -#define GTK_SOURCE_IS_UNDO_MANAGER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_SOURCE_TYPE_UNDO_MANAGER)) - -#define GTK_SOURCE_UNDO_MANAGER_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManagerClass)) - -typedef struct _GtkSourceUndoManager GtkSourceUndoManager; -typedef struct _GtkSourceUndoManagerClass GtkSourceUndoManagerClass; - -typedef struct _GtkSourceUndoManagerPrivate GtkSourceUndoManagerPrivate; - -struct _GtkSourceUndoManager -{ - GObject base; - - GtkSourceUndoManagerPrivate *priv; -}; - -struct _GtkSourceUndoManagerClass -{ - GObjectClass parent_class; - - /* Signals */ - void (*can_undo)(GtkSourceUndoManager *um, gboolean can_undo); - void (*can_redo)(GtkSourceUndoManager *um, gboolean can_redo); -}; - -GType gtk_source_undo_manager_get_type(void) G_GNUC_CONST; - -GtkSourceUndoManager* gtk_source_undo_manager_new(GtkTextBuffer *buffer); - -gboolean gtk_source_undo_manager_can_undo(const GtkSourceUndoManager *um); -gboolean gtk_source_undo_manager_can_redo(const GtkSourceUndoManager *um); - -void gtk_source_undo_manager_undo(GtkSourceUndoManager *um); -void gtk_source_undo_manager_redo(GtkSourceUndoManager *um); - -void gtk_source_undo_manager_begin_not_undoable_action(GtkSourceUndoManager *um); -void gtk_source_undo_manager_end_not_undoable_action(GtkSourceUndoManager *um); - -gint gtk_source_undo_manager_get_max_undo_levels(GtkSourceUndoManager *um); -void gtk_source_undo_manager_set_max_undo_levels(GtkSourceUndoManager *um, - gint undo_levels); - -G_END_DECLS - -#endif // UNDOVIEW_UNDO_MANAGER_H_ - diff --git a/chromium/third_party/undoview/undo_view.c b/chromium/third_party/undoview/undo_view.c deleted file mode 100644 index 933f8adca0e..00000000000 --- a/chromium/third_party/undoview/undo_view.c +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Boilerplate code was generated by http://burtonini.com/cgi/gobject.py - -#include <gdk/gdkkeysyms.h> - -#include "undo_view.h" - -G_DEFINE_TYPE (GtkUndoView, gtk_undo_view, GTK_TYPE_TEXT_VIEW) - -static void -gtk_undo_view_dispose(GObject *object) { - GtkUndoView *uview = GTK_UNDO_VIEW(object); - - if(uview->undo_manager_) { - g_object_unref(G_OBJECT(uview->undo_manager_)); - uview->undo_manager_ = NULL; - } - G_OBJECT_CLASS(gtk_undo_view_parent_class)->dispose(object); -} - -static void -gtk_undo_view_undo(GtkUndoView *uview) { - if(gtk_source_undo_manager_can_undo(uview->undo_manager_)) - gtk_source_undo_manager_undo(uview->undo_manager_); -} - -static void -gtk_undo_view_redo(GtkUndoView *uview) { - if(gtk_source_undo_manager_can_redo(uview->undo_manager_)) - gtk_source_undo_manager_redo(uview->undo_manager_); -} - -static void -gtk_undo_view_class_init(GtkUndoViewClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS(klass); - GtkBindingSet *binding_set; - - g_signal_new("undo", - G_TYPE_FROM_CLASS(klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET(GtkUndoViewClass, undo), - NULL, - NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); - g_signal_new("redo", - G_TYPE_FROM_CLASS(klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET(GtkUndoViewClass, redo), - NULL, - NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); - - klass->undo = gtk_undo_view_undo; - klass->redo = gtk_undo_view_redo; - - binding_set = gtk_binding_set_by_class(klass); - gtk_binding_entry_add_signal(binding_set, GDK_z, GDK_CONTROL_MASK, "undo", 0); - gtk_binding_entry_add_signal(binding_set, GDK_y, GDK_CONTROL_MASK, "redo", 0); - gtk_binding_entry_add_signal(binding_set, GDK_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "redo", 0); - gtk_binding_entry_add_signal(binding_set, GDK_F14, 0, "undo", 0); - - object_class->dispose = gtk_undo_view_dispose; -} - -static void -gtk_undo_view_init(GtkUndoView *self) { -} - -GtkWidget* -gtk_undo_view_new(GtkTextBuffer *buffer) { - GtkWidget *ret = g_object_new(GTK_TYPE_UNDO_VIEW, "buffer", buffer, NULL); - GTK_UNDO_VIEW(ret)->undo_manager_ = gtk_source_undo_manager_new(GTK_TEXT_BUFFER(buffer)); - - return ret; -} - diff --git a/chromium/third_party/undoview/undo_view.h b/chromium/third_party/undoview/undo_view.h deleted file mode 100644 index 7fccf87bd99..00000000000 --- a/chromium/third_party/undoview/undo_view.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Boilerplate code was generated by http://burtonini.com/cgi/gobject.py - -#ifndef UNDOVIEW_UNDO_VIEW_H_ -#define UNDOVIEW_UNDO_VIEW_H_ - -#include <gtk/gtk.h> -#include "undo_manager.h" - -G_BEGIN_DECLS - -#define GTK_TYPE_UNDO_VIEW gtk_undo_view_get_type() - -#define GTK_UNDO_VIEW(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_UNDO_VIEW, GtkUndoView)) - -#define GTK_UNDO_VIEW_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_UNDO_VIEW, GtkUndoViewClass)) - -#define GTK_IS_UNDO_VIEW(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_UNDO_VIEW)) - -#define GTK_IS_UNDO_VIEW_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_UNDO_VIEW)) - -#define GTK_UNDO_VIEW_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_UNDO_VIEW, GtkUndoViewClass)) - -typedef struct { - GtkTextView parent; - GtkSourceUndoManager *undo_manager_; -} GtkUndoView; - -typedef struct { - GtkTextViewClass parent_class; - - void (*undo)(GtkUndoView *); - void (*redo)(GtkUndoView *); -} GtkUndoViewClass; - -GType gtk_undo_view_get_type(void); - -GtkWidget* gtk_undo_view_new(GtkTextBuffer *buffer); - -G_END_DECLS - -#endif // UNDOVIEW_UNDO_VIEW_H_ diff --git a/chromium/third_party/webrtc/modules/audio_processing/aec/aec_core.c b/chromium/third_party/webrtc/modules/audio_processing/aec/aec_core.c index 4d59956dc88..85ea8bd3510 100644 --- a/chromium/third_party/webrtc/modules/audio_processing/aec/aec_core.c +++ b/chromium/third_party/webrtc/modules/audio_processing/aec/aec_core.c @@ -98,9 +98,13 @@ ALIGN16_BEG const float ALIGN16_END WebRtcAec_overDriveCurve[65] = { 1.9354f, 1.9437f, 1.9520f, 1.9601f, 1.9682f, 1.9763f, 1.9843f, 1.9922f, 2.0000f}; -// TODO(bjornv): These parameters will be tuned. +// Delay Agnostic AEC parameters, still under development and may change. static const float kDelayQualityThresholdMax = 0.07f; +static const float kDelayQualityThresholdMin = 0.01f; static const int kInitialShiftOffset = 5; +#if !defined(WEBRTC_ANDROID) +static const int kDelayCorrectionStart = 1500; // 10 ms chunks +#endif // Target suppression levels for nlp modes. // log{0.001, 0.00001, 0.00000001} @@ -853,10 +857,28 @@ static void TimeToFrequency(float time_data[PART_LEN2], } } +static int MoveFarReadPtrWithoutSystemDelayUpdate(AecCore* self, int elements) { + WebRtc_MoveReadPtr(self->far_buf_windowed, elements); +#ifdef WEBRTC_AEC_DEBUG_DUMP + WebRtc_MoveReadPtr(self->far_time_buf, elements); +#endif + return WebRtc_MoveReadPtr(self->far_buf, elements); +} + static int SignalBasedDelayCorrection(AecCore* self) { int delay_correction = 0; int last_delay = -2; assert(self != NULL); +#if !defined(WEBRTC_ANDROID) + // On desktops, turn on correction after |kDelayCorrectionStart| frames. This + // is to let the delay estimation get a chance to converge. Also, if the + // playout audio volume is low (or even muted) the delay estimation can return + // a very large delay, which will break the AEC if it is applied. + if (self->frame_count < kDelayCorrectionStart) { + return 0; + } +#endif + // 1. Check for non-negative delay estimate. Note that the estimates we get // from the delay estimation are not compensated for lookahead. Hence, a // negative |last_delay| is an invalid one. @@ -874,15 +896,22 @@ static int SignalBasedDelayCorrection(AecCore* self) { (WebRtc_last_delay_quality(self->delay_estimator) > self->delay_quality_threshold)) { int delay = last_delay - WebRtc_lookahead(self->delay_estimator); - // Allow for a slack in the actual delay. The adaptive echo cancellation - // filter is currently |num_partitions| (of 64 samples) long. If the - // delay estimate indicates a delay of at least one quarter of the filter - // length we open up for correction. - if (delay <= 0 || delay > (self->num_partitions / 4)) { + // Allow for a slack in the actual delay, defined by a |lower_bound| and an + // |upper_bound|. The adaptive echo cancellation filter is currently + // |num_partitions| (of 64 samples) long. If the delay estimate is negative + // or at least 3/4 of the filter length we open up for correction. + const int lower_bound = 0; + const int upper_bound = self->num_partitions * 3 / 4; + const int do_correction = delay <= lower_bound || delay > upper_bound; + if (do_correction == 1) { int available_read = (int)WebRtc_available_read(self->far_buf); - // Adjust w.r.t. a |shift_offset| to account for not as reliable estimates - // in the beginning, hence we are more conservative. - delay_correction = -(delay - self->shift_offset); + // With |shift_offset| we gradually rely on the delay estimates. For + // positive delays we reduce the correction by |shift_offset| to lower the + // risk of pushing the AEC into a non causal state. For negative delays + // we rely on the values up to a rounding error, hence compensate by 1 + // element to make sure to push the delay into the causal region. + delay_correction = -delay; + delay_correction += delay > self->shift_offset ? self->shift_offset : 1; self->shift_offset--; self->shift_offset = (self->shift_offset <= 1 ? 1 : self->shift_offset); if (delay_correction > available_read - self->mult - 1) { @@ -1433,12 +1462,15 @@ int WebRtcAec_CreateAec(AecCore** aecInst) { return -1; } #ifdef WEBRTC_ANDROID + aec->reported_delay_enabled = 0; // DA-AEC enabled by default. // DA-AEC assumes the system is causal from the beginning and will self adjust // the lookahead when shifting is required. WebRtc_set_lookahead(aec->delay_estimator, 0); #else + aec->reported_delay_enabled = 1; WebRtc_set_lookahead(aec->delay_estimator, kLookaheadBlocks); #endif + aec->extended_filter_enabled = 0; // Assembly optimization WebRtcAec_FilterFar = FilterFar; @@ -1583,14 +1615,8 @@ int WebRtcAec_InitAec(AecCore* aec, int sampFreq) { aec->previous_delay = -2; // (-2): Uninitialized. aec->delay_correction_count = 0; aec->shift_offset = kInitialShiftOffset; - aec->delay_quality_threshold = 0; + aec->delay_quality_threshold = kDelayQualityThresholdMin; -#ifdef WEBRTC_ANDROID - aec->reported_delay_enabled = 0; // Disabled by default. -#else - aec->reported_delay_enabled = 1; -#endif - aec->extended_filter_enabled = 0; aec->num_partitions = kNormalNumPartitions; // Update the delay estimator with filter length. We use half the @@ -1603,6 +1629,7 @@ int WebRtcAec_InitAec(AecCore* aec, int sampFreq) { // all the time and the APIs to turn it on/off will be removed. Hence, remove // this line then. WebRtc_enable_robust_validation(aec->delay_estimator, 1); + aec->frame_count = 0; // Default target suppression mode. aec->nlp_mode = 1; @@ -1707,11 +1734,7 @@ void WebRtcAec_BufferFarendPartition(AecCore* aec, const float* farend) { } int WebRtcAec_MoveFarReadPtr(AecCore* aec, int elements) { - int elements_moved = WebRtc_MoveReadPtr(aec->far_buf_windowed, elements); - WebRtc_MoveReadPtr(aec->far_buf, elements); -#ifdef WEBRTC_AEC_DEBUG_DUMP - WebRtc_MoveReadPtr(aec->far_time_buf, elements); -#endif + int elements_moved = MoveFarReadPtrWithoutSystemDelayUpdate(aec, elements); aec->system_delay -= elements_moved * PART_LEN; return elements_moved; } @@ -1725,6 +1748,7 @@ void WebRtcAec_ProcessFrames(AecCore* aec, int i, j; int out_elements = 0; + aec->frame_count++; // For each frame the process is as follows: // 1) If the system_delay indicates on being too small for processing a // frame we stuff the buffer with enough data for 10 ms. @@ -1783,42 +1807,27 @@ void WebRtcAec_ProcessFrames(AecCore* aec, // which should be investigated. Maybe, allow for a non-symmetric // rounding, like -16. int move_elements = (aec->knownDelay - knownDelay - 32) / PART_LEN; - int moved_elements = WebRtc_MoveReadPtr(aec->far_buf, move_elements); - WebRtc_MoveReadPtr(aec->far_buf_windowed, move_elements); + int moved_elements = + MoveFarReadPtrWithoutSystemDelayUpdate(aec, move_elements); aec->knownDelay -= moved_elements * PART_LEN; - #ifdef WEBRTC_AEC_DEBUG_DUMP - WebRtc_MoveReadPtr(aec->far_time_buf, move_elements); - #endif } else { // 2 b) Apply signal based delay correction. int move_elements = SignalBasedDelayCorrection(aec); - int moved_elements = WebRtc_MoveReadPtr(aec->far_buf, move_elements); - WebRtc_MoveReadPtr(aec->far_buf_windowed, move_elements); - #ifdef WEBRTC_AEC_DEBUG_DUMP - WebRtc_MoveReadPtr(aec->far_time_buf, move_elements); - #endif + int moved_elements = + MoveFarReadPtrWithoutSystemDelayUpdate(aec, move_elements); + int far_near_buffer_diff = WebRtc_available_read(aec->far_buf) - + WebRtc_available_read(aec->nearFrBuf) / PART_LEN; WebRtc_SoftResetDelayEstimator(aec->delay_estimator, moved_elements); WebRtc_SoftResetDelayEstimatorFarend(aec->delay_estimator_farend, moved_elements); aec->signal_delay_correction += moved_elements; - // TODO(bjornv): Investigate if this is reasonable. I had to add this - // guard when the signal based delay correction replaces the system based - // one. Otherwise there was a buffer underrun in the "qa-new/01/" - // recording when adding 44 ms extra delay. This was not seen if we kept - // both delay correction algorithms running in parallel. - // A first investigation showed that we have a drift in this case that - // causes the buffer underrun. Compared to when delay correction was - // turned off, we get buffer underrun as well which was triggered in 1) - // above. In addition there was a shift in |knownDelay| later increasing - // the buffer. When running in parallel, this if statement was not - // triggered. This suggests two alternatives; (a) use both algorithms, or - // (b) allow for smaller delay corrections when we operate close to the - // buffer limit. At the time of testing we required a change of 6 blocks, - // but could change it to, e.g., 2 blocks. It requires some testing - // though. - if ((int)WebRtc_available_read(aec->far_buf) < (aec->mult + 1)) { - // We don't have enough data so we stuff the far-end buffers. - WebRtcAec_MoveFarReadPtr(aec, -(aec->mult + 1)); + // If we rely on reported system delay values only, a buffer underrun here + // can never occur since we've taken care of that in 1) above. Here, we + // apply signal based delay correction and can therefore end up with + // buffer underruns since the delay estimation can be wrong. We therefore + // stuff the buffer with enough elements if needed. + if (far_near_buffer_diff < 0) { + WebRtcAec_MoveFarReadPtr(aec, far_near_buffer_diff); } } diff --git a/chromium/third_party/webrtc/modules/audio_processing/aec/aec_core_internal.h b/chromium/third_party/webrtc/modules/audio_processing/aec/aec_core_internal.h index bdb90413a7d..2f795896f51 100644 --- a/chromium/third_party/webrtc/modules/audio_processing/aec/aec_core_internal.h +++ b/chromium/third_party/webrtc/modules/audio_processing/aec/aec_core_internal.h @@ -142,6 +142,7 @@ struct AecCore { int delay_correction_count; int shift_offset; float delay_quality_threshold; + int frame_count; // 0 = reported delay mode disabled (signal based delay correction enabled). // otherwise enabled diff --git a/chromium/third_party/webrtc/modules/audio_processing/aec/echo_cancellation.c b/chromium/third_party/webrtc/modules/audio_processing/aec/echo_cancellation.c index 54ac24dcf43..733dee0db9a 100644 --- a/chromium/third_party/webrtc/modules/audio_processing/aec/echo_cancellation.c +++ b/chromium/third_party/webrtc/modules/audio_processing/aec/echo_cancellation.c @@ -242,7 +242,10 @@ int32_t WebRtcAec_Init(void* aecInst, int32_t sampFreq, int32_t scSampFreq) { aecpc->checkBuffSize = 1; aecpc->firstVal = 0; - aecpc->startup_phase = WebRtcAec_reported_delay_enabled(aecpc->aec); + // We skip the startup_phase completely (setting to 0) if DA-AEC is enabled, + // but not extended_filter mode. + aecpc->startup_phase = WebRtcAec_delay_correction_enabled(aecpc->aec) || + WebRtcAec_reported_delay_enabled(aecpc->aec); aecpc->bufSizeStart = 0; aecpc->checkBufSizeCtr = 0; aecpc->msInSndCardBuf = 0; @@ -725,9 +728,7 @@ static int ProcessNormal(Aec* aecpc, } } else { // AEC is enabled. - if (WebRtcAec_reported_delay_enabled(aecpc->aec)) { - EstBufDelayNormal(aecpc); - } + EstBufDelayNormal(aecpc); // Call the AEC. // TODO(bjornv): Re-structure such that we don't have to pass @@ -789,12 +790,13 @@ static void ProcessExtended(Aec* self, // measurement. int startup_size_ms = reported_delay_ms < kFixedDelayMs ? kFixedDelayMs : reported_delay_ms; +#if defined(WEBRTC_ANDROID) int target_delay = startup_size_ms * self->rate_factor * 8; -#if !defined(WEBRTC_ANDROID) +#else // To avoid putting the AEC in a non-causal state we're being slightly // conservative and scale by 2. On Android we use a fixed delay and // therefore there is no need to scale the target_delay. - target_delay /= 2; + int target_delay = startup_size_ms * self->rate_factor * 8 / 2; #endif int overhead_elements = (WebRtcAec_system_delay(self->aec) - target_delay) / PART_LEN; @@ -802,9 +804,7 @@ static void ProcessExtended(Aec* self, self->startup_phase = 0; } - if (WebRtcAec_reported_delay_enabled(self->aec)) { - EstBufDelayExtended(self); - } + EstBufDelayExtended(self); { // |delay_diff_offset| gives us the option to manually rewind the delay on diff --git a/chromium/third_party/webrtc/modules/audio_processing/aec/system_delay_unittest.cc b/chromium/third_party/webrtc/modules/audio_processing/aec/system_delay_unittest.cc index da28752bcc4..259b0759c8c 100644 --- a/chromium/third_party/webrtc/modules/audio_processing/aec/system_delay_unittest.cc +++ b/chromium/third_party/webrtc/modules/audio_processing/aec/system_delay_unittest.cc @@ -40,7 +40,7 @@ class SystemDelayTest : public ::testing::Test { // Maps buffer size in ms into samples, taking the unprocessed frame into // account. - int MapBufferSizeToSamples(int size_in_ms); + int MapBufferSizeToSamples(int size_in_ms, bool extended_filter); void* handle_; Aec* self_; @@ -98,6 +98,7 @@ static const int kMaxConvergenceMs = 500; void SystemDelayTest::Init(int sample_rate_hz) { // Initialize AEC EXPECT_EQ(0, WebRtcAec_Init(handle_, sample_rate_hz, 48000)); + EXPECT_EQ(0, WebRtcAec_system_delay(self_->aec)); // One frame equals 10 ms of data. samples_per_frame_ = sample_rate_hz / 100; @@ -133,26 +134,38 @@ void SystemDelayTest::RunStableStartup() { // up the far-end buffer with the same amount as we will report in through // Process(). int buffer_size = BufferFillUp(); - // A stable device should be accepted and put in a regular process mode within - // |kStableConvergenceMs|. - int process_time_ms = 0; - for (; process_time_ms < kStableConvergenceMs; process_time_ms += 10) { + + if (WebRtcAec_reported_delay_enabled(self_->aec) == 0) { + // In extended_filter mode we set the buffer size after the first processed + // 10 ms chunk. Hence, we don't need to wait for the reported system delay + // values to become stable. RenderAndCapture(kDeviceBufMs); buffer_size += samples_per_frame_; - if (self_->startup_phase == 0) { - // We have left the startup phase. - break; + EXPECT_EQ(0, self_->startup_phase); + } else { + // A stable device should be accepted and put in a regular process mode + // within |kStableConvergenceMs|. + int process_time_ms = 0; + for (; process_time_ms < kStableConvergenceMs; process_time_ms += 10) { + RenderAndCapture(kDeviceBufMs); + buffer_size += samples_per_frame_; + if (self_->startup_phase == 0) { + // We have left the startup phase. + break; + } } + // Verify convergence time. + EXPECT_GT(kStableConvergenceMs, process_time_ms); } - // Verify convergence time. - EXPECT_GT(kStableConvergenceMs, process_time_ms); // Verify that the buffer has been flushed. EXPECT_GE(buffer_size, WebRtcAec_system_delay(self_->aec)); } -int SystemDelayTest::MapBufferSizeToSamples(int size_in_ms) { - // The extra 10 ms corresponds to the unprocessed frame. - return (size_in_ms + 10) * samples_per_frame_ / 10; + int SystemDelayTest::MapBufferSizeToSamples(int size_in_ms, + bool extended_filter) { + // If extended_filter is disabled we add an extra 10 ms for the unprocessed + // frame. That is simply how the algorithm is constructed. + return (size_in_ms + (extended_filter ? 0 : 10)) * samples_per_frame_ / 10; } // The tests should meet basic requirements and not be adjusted to what is @@ -179,14 +192,23 @@ int SystemDelayTest::MapBufferSizeToSamples(int size_in_ms) { TEST_F(SystemDelayTest, CorrectIncreaseWhenBufferFarend) { // When we add data to the AEC buffer the internal system delay should be // incremented with the same amount as the size of data. - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - - // Loop through a couple of calls to make sure the system delay increments - // correctly. - for (int j = 1; j <= 5; j++) { - EXPECT_EQ(0, WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); - EXPECT_EQ(j * samples_per_frame_, WebRtcAec_system_delay(self_->aec)); + // This process should be independent of DA-AEC and extended_filter mode. + for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { + WebRtcAec_enable_delay_correction(self_->aec, extended_filter); + EXPECT_EQ(extended_filter, WebRtcAec_delay_correction_enabled(self_->aec)); + for (int da_aec = 0; da_aec <= 1; ++da_aec) { + WebRtcAec_enable_reported_delay(self_->aec, 1 - da_aec); + EXPECT_EQ(1 - da_aec, WebRtcAec_reported_delay_enabled(self_->aec)); + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + // Loop through a couple of calls to make sure the system delay + // increments correctly. + for (int j = 1; j <= 5; j++) { + EXPECT_EQ(0, + WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); + EXPECT_EQ(j * samples_per_frame_, WebRtcAec_system_delay(self_->aec)); + } + } } } } @@ -197,21 +219,42 @@ TEST_F(SystemDelayTest, CorrectIncreaseWhenBufferFarend) { TEST_F(SystemDelayTest, CorrectDelayAfterStableStartup) { // We run the system in a stable startup. After that we verify that the system // delay meets the requirements. - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - RunStableStartup(); - - // Verify system delay with respect to requirements, i.e., the - // |system_delay| is in the interval [75%, 100%] of what's reported on the - // average. - int average_reported_delay = kDeviceBufMs * samples_per_frame_ / 10; - EXPECT_GE(average_reported_delay, WebRtcAec_system_delay(self_->aec)); - EXPECT_LE(average_reported_delay * 3 / 4, - WebRtcAec_system_delay(self_->aec)); + // This process should be independent of DA-AEC and extended_filter mode. + for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { + WebRtcAec_enable_delay_correction(self_->aec, extended_filter); + EXPECT_EQ(extended_filter, WebRtcAec_delay_correction_enabled(self_->aec)); + for (int da_aec = 0; da_aec <= 1; ++da_aec) { + WebRtcAec_enable_reported_delay(self_->aec, 1 - da_aec); + EXPECT_EQ(1 - da_aec, WebRtcAec_reported_delay_enabled(self_->aec)); + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + RunStableStartup(); + + // Verify system delay with respect to requirements, i.e., the + // |system_delay| is in the interval [75%, 100%] of what's reported on + // the average. + // In extended_filter mode we target 50% and measure after one processed + // 10 ms chunk. + int average_reported_delay = kDeviceBufMs * samples_per_frame_ / 10; + EXPECT_GE(average_reported_delay, WebRtcAec_system_delay(self_->aec)); + int lower_bound = WebRtcAec_delay_correction_enabled(self_->aec) + ? average_reported_delay / 2 - samples_per_frame_ + : average_reported_delay * 3 / 4; + EXPECT_LE(lower_bound, WebRtcAec_system_delay(self_->aec)); + } + } } } TEST_F(SystemDelayTest, CorrectDelayAfterUnstableStartup) { + // This test does not apply in extended_filter mode, since we only use the + // the first 10 ms chunk to determine a reasonable buffer size. Neither does + // it apply if DA-AEC is on because that overrides the startup procedure. + WebRtcAec_enable_delay_correction(self_->aec, 0); + EXPECT_EQ(0, WebRtcAec_delay_correction_enabled(self_->aec)); + WebRtcAec_enable_reported_delay(self_->aec, 1); + EXPECT_EQ(1, WebRtcAec_reported_delay_enabled(self_->aec)); + // In an unstable system we would start processing after |kMaxConvergenceMs|. // On the last frame the AEC buffer is adjusted to 60% of the last reported // device buffer size. @@ -252,15 +295,19 @@ TEST_F(SystemDelayTest, CorrectDelayAfterUnstableStartup) { } } -TEST_F(SystemDelayTest, - DISABLED_ON_ANDROID(CorrectDelayAfterStableBufferBuildUp)) { +TEST_F(SystemDelayTest, CorrectDelayAfterStableBufferBuildUp) { + // This test does not apply in extended_filter mode, since we only use the + // the first 10 ms chunk to determine a reasonable buffer size. Neither does + // it apply if DA-AEC is on because that overrides the startup procedure. + WebRtcAec_enable_delay_correction(self_->aec, 0); + EXPECT_EQ(0, WebRtcAec_delay_correction_enabled(self_->aec)); + WebRtcAec_enable_reported_delay(self_->aec, 1); + EXPECT_EQ(1, WebRtcAec_reported_delay_enabled(self_->aec)); + // In this test we start by establishing the device buffer size during stable // conditions, but with an empty internal far-end buffer. Once that is done we // verify that the system delay is increased correctly until we have reach an // internal buffer size of 75% of what's been reported. - - // This test assumes the reported delays are used. - WebRtcAec_enable_reported_delay(WebRtcAec_aec_core(handle_), 1); for (size_t i = 0; i < kNumSampleRates; i++) { Init(kSampleRateHz[i]); @@ -314,62 +361,73 @@ TEST_F(SystemDelayTest, CorrectDelayWhenBufferUnderrun) { // WebRtcAec_Process() we will finally run out of data, but should // automatically stuff the buffer. We verify this behavior by checking if the // system delay goes negative. - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - RunStableStartup(); - - // The AEC has now left the Startup phase. We now have at most - // |kStableConvergenceMs| in the buffer. Keep on calling Process() until - // we run out of data and verify that the system delay is non-negative. - for (int j = 0; j <= kStableConvergenceMs; j += 10) { - EXPECT_EQ(0, - WebRtcAec_Process(handle_, - &near_ptr_, - 1, - &out_ptr_, - samples_per_frame_, - kDeviceBufMs, - 0)); - EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); + // This process should be independent of DA-AEC and extended_filter mode. + for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { + WebRtcAec_enable_delay_correction(self_->aec, extended_filter); + EXPECT_EQ(extended_filter, WebRtcAec_delay_correction_enabled(self_->aec)); + for (int da_aec = 0; da_aec <= 1; ++da_aec) { + WebRtcAec_enable_reported_delay(self_->aec, 1 - da_aec); + EXPECT_EQ(1 - da_aec, WebRtcAec_reported_delay_enabled(self_->aec)); + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + RunStableStartup(); + + // The AEC has now left the Startup phase. We now have at most + // |kStableConvergenceMs| in the buffer. Keep on calling Process() until + // we run out of data and verify that the system delay is non-negative. + for (int j = 0; j <= kStableConvergenceMs; j += 10) { + EXPECT_EQ(0, WebRtcAec_Process(handle_, &near_ptr_, 1, &out_ptr_, + samples_per_frame_, kDeviceBufMs, 0)); + EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); + } + } } } } -TEST_F(SystemDelayTest, DISABLED_ON_ANDROID(CorrectDelayDuringDrift)) { +TEST_F(SystemDelayTest, CorrectDelayDuringDrift) { // This drift test should verify that the system delay is never exceeding the // device buffer. The drift is simulated by decreasing the reported device // buffer size by 1 ms every 100 ms. If the device buffer size goes below 30 // ms we jump (add) 10 ms to give a repeated pattern. - // This test assumes the reported delays are used. - WebRtcAec_enable_reported_delay(WebRtcAec_aec_core(handle_), 1); - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - RunStableStartup(); - - // We have now left the startup phase and proceed with normal processing. - int jump = 0; - for (int j = 0; j < 1000; j++) { - // Drift = -1 ms per 100 ms of data. - int device_buf_ms = kDeviceBufMs - (j / 10) + jump; - int device_buf = MapBufferSizeToSamples(device_buf_ms); - - if (device_buf_ms < 30) { - // Add 10 ms data, taking affect next frame. - jump += 10; + // This process should be independent of DA-AEC and extended_filter mode. + for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { + WebRtcAec_enable_delay_correction(self_->aec, extended_filter); + EXPECT_EQ(extended_filter, WebRtcAec_delay_correction_enabled(self_->aec)); + for (int da_aec = 0; da_aec <= 1; ++da_aec) { + WebRtcAec_enable_reported_delay(self_->aec, 1 - da_aec); + EXPECT_EQ(1 - da_aec, WebRtcAec_reported_delay_enabled(self_->aec)); + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + RunStableStartup(); + + // We have left the startup phase and proceed with normal processing. + int jump = 0; + for (int j = 0; j < 1000; j++) { + // Drift = -1 ms per 100 ms of data. + int device_buf_ms = kDeviceBufMs - (j / 10) + jump; + int device_buf = MapBufferSizeToSamples(device_buf_ms, + extended_filter == 1); + + if (device_buf_ms < 30) { + // Add 10 ms data, taking affect next frame. + jump += 10; + } + RenderAndCapture(device_buf_ms); + + // Verify that the system delay does not exceed the device buffer. + EXPECT_GE(device_buf, WebRtcAec_system_delay(self_->aec)); + + // Verify that the system delay is non-negative. + EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); + } } - RenderAndCapture(device_buf_ms); - - // Verify that the system delay does not exceed the device buffer. - EXPECT_GE(device_buf, WebRtcAec_system_delay(self_->aec)); - - // Verify that the system delay is non-negative. - EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); } } } -TEST_F(SystemDelayTest, DISABLED_ON_ANDROID(ShouldRecoverAfterGlitch)) { +TEST_F(SystemDelayTest, ShouldRecoverAfterGlitch) { // This glitch test should verify that the system delay recovers if there is // a glitch in data. The data glitch is constructed as 200 ms of buffering // after which the stable procedure continues. The glitch is never reported by @@ -377,79 +435,100 @@ TEST_F(SystemDelayTest, DISABLED_ON_ANDROID(ShouldRecoverAfterGlitch)) { // The system is said to be in a non-causal state if the difference between // the device buffer and system delay is less than a block (64 samples). - // This test assumes the reported delays are used. - WebRtcAec_enable_reported_delay(WebRtcAec_aec_core(handle_), 1); - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - RunStableStartup(); - int device_buf = MapBufferSizeToSamples(kDeviceBufMs); - // Glitch state. - for (int j = 0; j < 20; j++) { - EXPECT_EQ(0, WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); - // No need to verify system delay, since that is done in a separate test. - } - // Verify that we are in a non-causal state, i.e., - // |system_delay| > |device_buf|. - EXPECT_LT(device_buf, WebRtcAec_system_delay(self_->aec)); - - // Recover state. Should recover at least 4 ms of data per 10 ms, hence a - // glitch of 200 ms will take at most 200 * 10 / 4 = 500 ms to recover from. - bool non_causal = true; // We are currently in a non-causal state. - for (int j = 0; j < 50; j++) { - int system_delay_before = WebRtcAec_system_delay(self_->aec); - RenderAndCapture(kDeviceBufMs); - int system_delay_after = WebRtcAec_system_delay(self_->aec); - - // We have recovered if |device_buf| - |system_delay_after| >= 64 (one - // block). During recovery |system_delay_after| < |system_delay_before|, - // otherwise they are equal. - if (non_causal) { - EXPECT_LT(system_delay_after, system_delay_before); - if (device_buf - system_delay_after >= 64) { - non_causal = false; + // This process should be independent of DA-AEC and extended_filter mode. + for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { + WebRtcAec_enable_delay_correction(self_->aec, extended_filter); + EXPECT_EQ(extended_filter, WebRtcAec_delay_correction_enabled(self_->aec)); + for (int da_aec = 0; da_aec <= 1; ++da_aec) { + WebRtcAec_enable_reported_delay(self_->aec, 1 - da_aec); + EXPECT_EQ(1 - da_aec, WebRtcAec_reported_delay_enabled(self_->aec)); + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + RunStableStartup(); + int device_buf = MapBufferSizeToSamples(kDeviceBufMs, + extended_filter == 1); + // Glitch state. + for (int j = 0; j < 20; j++) { + EXPECT_EQ(0, + WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); + // No need to verify system delay, since that is done in a separate + // test. } - } else { - EXPECT_EQ(system_delay_before, system_delay_after); + // Verify that we are in a non-causal state, i.e., + // |system_delay| > |device_buf|. + EXPECT_LT(device_buf, WebRtcAec_system_delay(self_->aec)); + + // Recover state. Should recover at least 4 ms of data per 10 ms, hence + // a glitch of 200 ms will take at most 200 * 10 / 4 = 500 ms to recover + // from. + bool non_causal = true; // We are currently in a non-causal state. + for (int j = 0; j < 50; j++) { + int system_delay_before = WebRtcAec_system_delay(self_->aec); + RenderAndCapture(kDeviceBufMs); + int system_delay_after = WebRtcAec_system_delay(self_->aec); + // We have recovered if + // |device_buf| - |system_delay_after| >= PART_LEN (1 block). + // During recovery, |system_delay_after| < |system_delay_before|, + // otherwise they are equal. + if (non_causal) { + EXPECT_LT(system_delay_after, system_delay_before); + if (device_buf - system_delay_after >= PART_LEN) { + non_causal = false; + } + } else { + EXPECT_EQ(system_delay_before, system_delay_after); + } + // Verify that the system delay is non-negative. + EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); + } + // Check that we have recovered. + EXPECT_FALSE(non_causal); } - // Verify that the system delay is non-negative. - EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); } - // Check that we have recovered. - EXPECT_FALSE(non_causal); } } TEST_F(SystemDelayTest, UnaffectedWhenSpuriousDeviceBufferValues) { - // This spurious device buffer data test aims at verifying that the system - // delay is unaffected by large outliers. - // The system is said to be in a non-causal state if the difference between - // the device buffer and system delay is less than a block (64 samples). - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - RunStableStartup(); - int device_buf = MapBufferSizeToSamples(kDeviceBufMs); - - // Normal state. We are currently not in a non-causal state. - bool non_causal = false; - - // Run 1 s and replace device buffer size with 500 ms every 100 ms. - for (int j = 0; j < 100; j++) { - int system_delay_before_calls = WebRtcAec_system_delay(self_->aec); - int device_buf_ms = kDeviceBufMs; - if (j % 10 == 0) { - device_buf_ms = 500; - } - RenderAndCapture(device_buf_ms); + // This test does not apply in extended_filter mode, since we only use the + // the first 10 ms chunk to determine a reasonable buffer size. + const int extended_filter = 0; + WebRtcAec_enable_delay_correction(self_->aec, extended_filter); + EXPECT_EQ(extended_filter, WebRtcAec_delay_correction_enabled(self_->aec)); + + // Should be DA-AEC independent. + for (int da_aec = 0; da_aec <= 1; ++da_aec) { + WebRtcAec_enable_reported_delay(self_->aec, 1 - da_aec); + EXPECT_EQ(1 - da_aec, WebRtcAec_reported_delay_enabled(self_->aec)); + // This spurious device buffer data test aims at verifying that the system + // delay is unaffected by large outliers. + // The system is said to be in a non-causal state if the difference between + // the device buffer and system delay is less than a block (64 samples). + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + RunStableStartup(); + int device_buf = MapBufferSizeToSamples(kDeviceBufMs, + extended_filter == 1); + + // Normal state. We are currently not in a non-causal state. + bool non_causal = false; + + // Run 1 s and replace device buffer size with 500 ms every 100 ms. + for (int j = 0; j < 100; j++) { + int system_delay_before_calls = WebRtcAec_system_delay(self_->aec); + int device_buf_ms = j % 10 == 0 ? 500 : kDeviceBufMs; + RenderAndCapture(device_buf_ms); + + // Check for non-causality. + if (device_buf - WebRtcAec_system_delay(self_->aec) < PART_LEN) { + non_causal = true; + } + EXPECT_FALSE(non_causal); + EXPECT_EQ(system_delay_before_calls, + WebRtcAec_system_delay(self_->aec)); - // Check for non-causality. - if (device_buf - WebRtcAec_system_delay(self_->aec) < 64) { - non_causal = true; + // Verify that the system delay is non-negative. + EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); } - EXPECT_FALSE(non_causal); - EXPECT_EQ(system_delay_before_calls, WebRtcAec_system_delay(self_->aec)); - - // Verify that the system delay is non-negative. - EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); } } } @@ -460,36 +539,54 @@ TEST_F(SystemDelayTest, CorrectImpactWhenTogglingDeviceBufferValues) { // The test is constructed such that every other device buffer value is zero // and then 2 * |kDeviceBufMs|, hence the size is constant on the average. The // zero values will force us into a non-causal state and thereby lowering the - // system delay until we basically runs out of data. Once that happens the + // system delay until we basically run out of data. Once that happens the // buffer will be stuffed. // TODO(bjornv): This test will have a better impact if we verified that the - // delay estimate goes up when the system delay goes done to meet the average + // delay estimate goes up when the system delay goes down to meet the average // device buffer size. - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - RunStableStartup(); - int device_buf = MapBufferSizeToSamples(kDeviceBufMs); - - // Normal state. We are currently not in a non-causal state. - bool non_causal = false; - - // Loop through 100 frames (both render and capture), which equals 1 s of - // data. Every odd frame we set the device buffer size to 2 * |kDeviceBufMs| - // and even frames we set the device buffer size to zero. - for (int j = 0; j < 100; j++) { - int system_delay_before_calls = WebRtcAec_system_delay(self_->aec); - int device_buf_ms = 2 * (j % 2) * kDeviceBufMs; - RenderAndCapture(device_buf_ms); - // Check for non-causality, compared with the average device buffer size. - non_causal |= (device_buf - WebRtcAec_system_delay(self_->aec) < 64); - EXPECT_GE(system_delay_before_calls, WebRtcAec_system_delay(self_->aec)); - - // Verify that the system delay is non-negative. - EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); + // This test does not apply if DA-AEC is enabled and extended_filter mode + // disabled. + for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { + WebRtcAec_enable_delay_correction(self_->aec, extended_filter); + EXPECT_EQ(extended_filter, WebRtcAec_delay_correction_enabled(self_->aec)); + for (int da_aec = 0; da_aec <= 1; ++da_aec) { + WebRtcAec_enable_reported_delay(self_->aec, 1 - da_aec); + EXPECT_EQ(1 - da_aec, WebRtcAec_reported_delay_enabled(self_->aec)); + if (extended_filter == 0 && da_aec == 1) { + continue; + } + for (size_t i = 0; i < kNumSampleRates; i++) { + Init(kSampleRateHz[i]); + RunStableStartup(); + const int device_buf = MapBufferSizeToSamples(kDeviceBufMs, + extended_filter == 1); + + // Normal state. We are currently not in a non-causal state. + bool non_causal = false; + + // Loop through 100 frames (both render and capture), which equals 1 s + // of data. Every odd frame we set the device buffer size to + // 2 * |kDeviceBufMs| and even frames we set the device buffer size to + // zero. + for (int j = 0; j < 100; j++) { + int system_delay_before_calls = WebRtcAec_system_delay(self_->aec); + int device_buf_ms = 2 * (j % 2) * kDeviceBufMs; + RenderAndCapture(device_buf_ms); + + // Check for non-causality, compared with the average device buffer + // size. + non_causal |= (device_buf - WebRtcAec_system_delay(self_->aec) < 64); + EXPECT_GE(system_delay_before_calls, + WebRtcAec_system_delay(self_->aec)); + + // Verify that the system delay is non-negative. + EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); + } + // Verify we are not in a non-causal state. + EXPECT_FALSE(non_causal); + } } - // Verify we are not in a non-causal state. - EXPECT_FALSE(non_causal); } } diff --git a/chromium/third_party/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc b/chromium/third_party/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc index 9d9550b0b1b..cf46ca9c333 100644 --- a/chromium/third_party/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc +++ b/chromium/third_party/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc @@ -81,11 +81,16 @@ int32_t FecReceiverImpl::AddReceivedRedPacket( uint8_t REDHeaderLength = 1; size_t payload_data_length = packet_length - header.headerLength; + if (payload_data_length == 0) { + LOG(LS_WARNING) << "Corrupt/truncated FEC packet."; + return -1; + } + // Add to list without RED header, aka a virtual RTP packet // we remove the RED header - ForwardErrorCorrection::ReceivedPacket* received_packet = - new ForwardErrorCorrection::ReceivedPacket; + rtc::scoped_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet( + new ForwardErrorCorrection::ReceivedPacket); received_packet->pkt = new ForwardErrorCorrection::Packet; // get payload type from RED header @@ -99,16 +104,18 @@ int32_t FecReceiverImpl::AddReceivedRedPacket( if (incoming_rtp_packet[header.headerLength] & 0x80) { // f bit set in RED header REDHeaderLength = 4; + if (payload_data_length < REDHeaderLength + 1u) { + LOG(LS_WARNING) << "Corrupt/truncated FEC packet."; + return -1; + } + uint16_t timestamp_offset = (incoming_rtp_packet[header.headerLength + 1]) << 8; timestamp_offset += incoming_rtp_packet[header.headerLength + 2]; timestamp_offset = timestamp_offset >> 2; if (timestamp_offset != 0) { - // |timestampOffset| should be 0. However, it's possible this is the first - // location a corrupt payload can be caught, so don't assert. LOG(LS_WARNING) << "Corrupt payload found."; - delete received_packet; return -1; } @@ -118,21 +125,20 @@ int32_t FecReceiverImpl::AddReceivedRedPacket( // check next RED header if (incoming_rtp_packet[header.headerLength + 4] & 0x80) { - // more than 2 blocks in packet not supported - delete received_packet; - assert(false); + LOG(LS_WARNING) << "More than 2 blocks in packet not supported."; return -1; } - if (blockLength > payload_data_length - REDHeaderLength) { - // block length longer than packet - delete received_packet; - assert(false); + // Check that the packet is long enough to contain data in the following + // block. + if (blockLength > payload_data_length - (REDHeaderLength + 1)) { + LOG(LS_WARNING) << "Block length longer than packet."; return -1; } } ++packet_counter_.num_packets; - ForwardErrorCorrection::ReceivedPacket* second_received_packet = NULL; + rtc::scoped_ptr<ForwardErrorCorrection::ReceivedPacket> + second_received_packet; if (blockLength > 0) { // handle block length, split into 2 packets REDHeaderLength = 5; @@ -154,7 +160,7 @@ int32_t FecReceiverImpl::AddReceivedRedPacket( received_packet->pkt->length = blockLength; - second_received_packet = new ForwardErrorCorrection::ReceivedPacket; + second_received_packet.reset(new ForwardErrorCorrection::ReceivedPacket); second_received_packet->pkt = new ForwardErrorCorrection::Packet; second_received_packet->is_fec = true; @@ -202,14 +208,12 @@ int32_t FecReceiverImpl::AddReceivedRedPacket( } if (received_packet->pkt->length == 0) { - delete second_received_packet; - delete received_packet; return 0; } - received_packet_list_.push_back(received_packet); + received_packet_list_.push_back(received_packet.release()); if (second_received_packet) { - received_packet_list_.push_back(second_received_packet); + received_packet_list_.push_back(second_received_packet.release()); } return 0; } diff --git a/chromium/third_party/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc b/chromium/third_party/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc index 31baf4e767a..f64b537a522 100644 --- a/chromium/third_party/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc +++ b/chromium/third_party/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc @@ -16,7 +16,9 @@ #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" #include "webrtc/modules/rtp_rtcp/source/fec_test_helper.h" #include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h" @@ -81,6 +83,11 @@ class ReceiverFecTest : public ::testing::Test { delete red_packet; } + void InjectGarbagePacketLength(size_t fec_garbage_offset); + static void SurvivesMaliciousPacket(const uint8_t* data, + size_t length, + uint8_t ulpfec_payload_type); + MockRtpData rtp_data_callback_; rtc::scoped_ptr<ForwardErrorCorrection> fec_; rtc::scoped_ptr<FecReceiver> receiver_fec_; @@ -104,8 +111,7 @@ TEST_F(ReceiverFecTest, TwoMediaOneFec) { // Recovery std::list<RtpPacket*>::iterator it = media_rtp_packets.begin(); - std::list<RtpPacket*>::iterator media_it = media_rtp_packets.begin(); - BuildAndAddRedMediaPacket(*media_it); + BuildAndAddRedMediaPacket(*it); VerifyReconstructedMediaPacket(*it, 1); EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); // Drop one media packet. @@ -123,6 +129,44 @@ TEST_F(ReceiverFecTest, TwoMediaOneFec) { DeletePackets(&media_packets); } +void ReceiverFecTest::InjectGarbagePacketLength(size_t fec_garbage_offset) { + EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _)) + .WillRepeatedly(Return(true)); + + const unsigned int kNumFecPackets = 1u; + std::list<RtpPacket*> media_rtp_packets; + std::list<Packet*> media_packets; + GenerateFrame(2, 0, &media_rtp_packets, &media_packets); + std::list<Packet*> fec_packets; + GenerateFEC(&media_packets, &fec_packets, kNumFecPackets); + ByteWriter<uint16_t>::WriteBigEndian( + &fec_packets.front()->data[fec_garbage_offset], 0x4711); + + // Inject first media packet, then first FEC packet, skipping the second media + // packet to cause a recovery from the FEC packet. + BuildAndAddRedMediaPacket(media_rtp_packets.front()); + BuildAndAddRedFecPacket(fec_packets.front()); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); + + FecPacketCounter counter = receiver_fec_->GetPacketCounter(); + EXPECT_EQ(2u, counter.num_packets); + EXPECT_EQ(1u, counter.num_fec_packets); + EXPECT_EQ(0u, counter.num_recovered_packets); + + DeletePackets(&media_packets); +} + +TEST_F(ReceiverFecTest, InjectGarbageFecHeaderLengthRecovery) { + // Byte offset 8 is the 'length recovery' field of the FEC header. + InjectGarbagePacketLength(8); +} + +TEST_F(ReceiverFecTest, InjectGarbageFecLevelHeaderProtectionLength) { + // Byte offset 10 is the 'protection length' field in the first FEC level + // header. + InjectGarbagePacketLength(10); +} + TEST_F(ReceiverFecTest, TwoMediaTwoFec) { const unsigned int kNumFecPackets = 2u; std::list<RtpPacket*> media_rtp_packets; @@ -362,4 +406,132 @@ TEST_F(ReceiverFecTest, OldFecPacketDropped) { DeletePackets(&media_packets); } +void ReceiverFecTest::SurvivesMaliciousPacket(const uint8_t* data, + size_t length, + uint8_t ulpfec_payload_type) { + webrtc::RTPHeader header; + rtc::scoped_ptr<webrtc::RtpHeaderParser> parser( + webrtc::RtpHeaderParser::Create()); + ASSERT_TRUE(parser->Parse(data, length, &header)); + + webrtc::NullRtpData null_callback; + rtc::scoped_ptr<webrtc::FecReceiver> receiver_fec( + webrtc::FecReceiver::Create(&null_callback)); + + receiver_fec->AddReceivedRedPacket(header, data, length, ulpfec_payload_type); +} + +TEST_F(ReceiverFecTest, TruncatedPacketWithFBitSet) { + const uint8_t kTruncatedPacket[] = {0x80, + 0x2a, + 0x68, + 0x71, + 0x29, + 0xa1, + 0x27, + 0x3a, + 0x29, + 0x12, + 0x2a, + 0x98, + 0xe0, + 0x29}; + + SurvivesMaliciousPacket(kTruncatedPacket, sizeof(kTruncatedPacket), 100); +} + +TEST_F(ReceiverFecTest, TruncatedPacketWithFBitSetEndingAfterFirstRedHeader) { + const uint8_t kPacket[] = {0x89, + 0x27, + 0x3a, + 0x83, + 0x27, + 0x3a, + 0x3a, + 0xf3, + 0x67, + 0xbe, + 0x2a, + 0xa9, + 0x27, + 0x54, + 0x3a, + 0x3a, + 0x2a, + 0x67, + 0x3a, + 0xf3, + 0x67, + 0xbe, + 0x2a, + 0x27, + 0xe6, + 0xf6, + 0x03, + 0x3e, + 0x29, + 0x27, + 0x21, + 0x27, + 0x2a, + 0x29, + 0x21, + 0x4b, + 0x29, + 0x3a, + 0x28, + 0x29, + 0xbf, + 0x29, + 0x2a, + 0x26, + 0x29, + 0xae, + 0x27, + 0xa6, + 0xf6, + 0x00, + 0x03, + 0x3e}; + SurvivesMaliciousPacket(kPacket, sizeof(kPacket), 100); +} + +TEST_F(ReceiverFecTest, TruncatedPacketWithoutDataPastFirstBlock) { + const uint8_t kPacket[] = {0x82, + 0x38, + 0x92, + 0x38, + 0x92, + 0x38, + 0xde, + 0x2a, + 0x11, + 0xc8, + 0xa3, + 0xc4, + 0x82, + 0x38, + 0x2a, + 0x21, + 0x2a, + 0x28, + 0x92, + 0x38, + 0x92, + 0x00, + 0x00, + 0x0a, + 0x3a, + 0xc8, + 0xa3, + 0x3a, + 0x27, + 0xc4, + 0x2a, + 0x21, + 0x2a, + 0x28}; + SurvivesMaliciousPacket(kPacket, sizeof(kPacket), 100); +} + } // namespace webrtc diff --git a/chromium/third_party/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc b/chromium/third_party/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc index abef1dda302..fe62dbf25bf 100644 --- a/chromium/third_party/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc +++ b/chromium/third_party/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc @@ -634,23 +634,35 @@ void ForwardErrorCorrection::InsertPackets( DiscardOldPackets(recovered_packet_list); } -void ForwardErrorCorrection::InitRecovery(const FecPacket* fec_packet, +bool ForwardErrorCorrection::InitRecovery(const FecPacket* fec_packet, RecoveredPacket* recovered) { // This is the first packet which we try to recover with. const uint16_t ulp_header_size = fec_packet->pkt->data[0] & 0x40 ? kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; // L bit set? + if (fec_packet->pkt->length < + static_cast<size_t>(kFecHeaderSize + ulp_header_size)) { + LOG(LS_WARNING) + << "Truncated FEC packet doesn't contain room for ULP header."; + return false; + } recovered->pkt = new Packet; memset(recovered->pkt->data, 0, IP_PACKET_SIZE); recovered->returned = false; recovered->was_recovered = true; - uint8_t protection_length[2]; - // Copy the protection length from the ULP header. - memcpy(protection_length, &fec_packet->pkt->data[10], 2); + uint16_t protection_length = + ByteReader<uint16_t>::ReadBigEndian(&fec_packet->pkt->data[10]); + if (protection_length > + std::min( + sizeof(recovered->pkt->data) - kRtpHeaderSize, + sizeof(fec_packet->pkt->data) - kFecHeaderSize - ulp_header_size)) { + LOG(LS_WARNING) << "Incorrect FEC protection length, dropping."; + return false; + } // Copy FEC payload, skipping the ULP header. memcpy(&recovered->pkt->data[kRtpHeaderSize], &fec_packet->pkt->data[kFecHeaderSize + ulp_header_size], - ByteReader<uint16_t>::ReadBigEndian(protection_length)); + protection_length); // Copy the length recovery field. memcpy(recovered->length_recovery, &fec_packet->pkt->data[8], 2); // Copy the first 2 bytes of the FEC header. @@ -660,9 +672,10 @@ void ForwardErrorCorrection::InitRecovery(const FecPacket* fec_packet, // Set the SSRC field. ByteWriter<uint32_t>::WriteBigEndian(&recovered->pkt->data[8], fec_packet->ssrc); + return true; } -void ForwardErrorCorrection::FinishRecovery(RecoveredPacket* recovered) { +bool ForwardErrorCorrection::FinishRecovery(RecoveredPacket* recovered) { // Set the RTP version to 2. recovered->pkt->data[0] |= 0x80; // Set the 1st bit. recovered->pkt->data[0] &= 0xbf; // Clear the 2nd bit. @@ -674,6 +687,10 @@ void ForwardErrorCorrection::FinishRecovery(RecoveredPacket* recovered) { recovered->pkt->length = ByteReader<uint16_t>::ReadBigEndian(recovered->length_recovery) + kRtpHeaderSize; + if (recovered->pkt->length > sizeof(recovered->pkt->data) - kRtpHeaderSize) + return false; + + return true; } void ForwardErrorCorrection::XorPackets(const Packet* src_packet, @@ -700,9 +717,11 @@ void ForwardErrorCorrection::XorPackets(const Packet* src_packet, } } -void ForwardErrorCorrection::RecoverPacket( - const FecPacket* fec_packet, RecoveredPacket* rec_packet_to_insert) { - InitRecovery(fec_packet, rec_packet_to_insert); +bool ForwardErrorCorrection::RecoverPacket( + const FecPacket* fec_packet, + RecoveredPacket* rec_packet_to_insert) { + if (!InitRecovery(fec_packet, rec_packet_to_insert)) + return false; ProtectedPacketList::const_iterator protected_it = fec_packet->protected_pkt_list.begin(); while (protected_it != fec_packet->protected_pkt_list.end()) { @@ -714,7 +733,9 @@ void ForwardErrorCorrection::RecoverPacket( } ++protected_it; } - FinishRecovery(rec_packet_to_insert); + if (!FinishRecovery(rec_packet_to_insert)) + return false; + return true; } void ForwardErrorCorrection::AttemptRecover( @@ -729,7 +750,13 @@ void ForwardErrorCorrection::AttemptRecover( // Recovery possible. RecoveredPacket* packet_to_insert = new RecoveredPacket; packet_to_insert->pkt = NULL; - RecoverPacket(*fec_packet_list_it, packet_to_insert); + if (!RecoverPacket(*fec_packet_list_it, packet_to_insert)) { + // Can't recover using this packet, drop it. + DiscardFECPacket(*fec_packet_list_it); + fec_packet_list_it = fec_packet_list_.erase(fec_packet_list_it); + delete packet_to_insert; + continue; + } // Add recovered packet to the list of recovered packets and update any // FEC packets covering this packet with a pointer to the data. diff --git a/chromium/third_party/webrtc/modules/rtp_rtcp/source/forward_error_correction.h b/chromium/third_party/webrtc/modules/rtp_rtcp/source/forward_error_correction.h index a3b3fa0e493..dc831708347 100644 --- a/chromium/third_party/webrtc/modules/rtp_rtcp/source/forward_error_correction.h +++ b/chromium/third_party/webrtc/modules/rtp_rtcp/source/forward_error_correction.h @@ -279,7 +279,7 @@ class ForwardErrorCorrection { void AttemptRecover(RecoveredPacketList* recovered_packet_list); // Initializes the packet recovery using the FEC packet. - static void InitRecovery(const FecPacket* fec_packet, + static bool InitRecovery(const FecPacket* fec_packet, RecoveredPacket* recovered); // Performs XOR between |src_packet| and |dst_packet| and stores the result @@ -287,10 +287,10 @@ class ForwardErrorCorrection { static void XorPackets(const Packet* src_packet, RecoveredPacket* dst_packet); // Finish up the recovery of a packet. - static void FinishRecovery(RecoveredPacket* recovered); + static bool FinishRecovery(RecoveredPacket* recovered); // Recover a missing packet. - void RecoverPacket(const FecPacket* fec_packet, + bool RecoverPacket(const FecPacket* fec_packet, RecoveredPacket* rec_packet_to_insert); // Get the number of missing media packets which are covered by this diff --git a/chromium/third_party/webrtc/modules/rtp_rtcp/source/h264_sps_parser.cc b/chromium/third_party/webrtc/modules/rtp_rtcp/source/h264_sps_parser.cc index b022b21165d..034e761dcd4 100644 --- a/chromium/third_party/webrtc/modules/rtp_rtcp/source/h264_sps_parser.cc +++ b/chromium/third_party/webrtc/modules/rtp_rtcp/source/h264_sps_parser.cc @@ -36,8 +36,8 @@ bool H264SpsParser::Parse() { // section 7.3.1 of the H.264 standard. rtc::ByteBuffer rbsp_buffer; for (size_t i = 0; i < byte_length_;) { - if (i < byte_length_ - 3 && - sps_[i] == 0 && sps_[i + 1] == 0 && sps_[i + 2] == 3) { + if (i + 3 < byte_length_ && sps_[i] == 0 && sps_[i + 1] == 0 && + sps_[i + 2] == 3) { // Two rbsp bytes + the emulation byte. rbsp_buffer.WriteBytes(sps_bytes + i, 2); i += 3; diff --git a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc index ebd46b02e0e..c3ef75aeab1 100644 --- a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc +++ b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc @@ -39,7 +39,7 @@ enum NalDefs { kFBit = 0x80, kNriMask = 0x60, kTypeMask = 0x1F }; // Bit masks for FU (A and B) headers. enum FuDefs { kSBit = 0x80, kEBit = 0x40, kRBit = 0x20 }; -void ParseSingleNalu(RtpDepacketizer::ParsedPayload* parsed_payload, +bool ParseSingleNalu(RtpDepacketizer::ParsedPayload* parsed_payload, const uint8_t* payload_data, size_t payload_data_length) { parsed_payload->type.Video.width = 0; @@ -54,6 +54,9 @@ void ParseSingleNalu(RtpDepacketizer::ParsedPayload* parsed_payload, uint8_t nal_type = payload_data[0] & kTypeMask; if (nal_type == kStapA) { // Skip the StapA header (StapA nal type + length). + if (payload_data_length <= kStapAHeaderSize) { + return false; + } nal_type = payload_data[kStapAHeaderSize] & kTypeMask; nalu_start += kStapAHeaderSize; nalu_length -= kStapAHeaderSize; @@ -81,12 +84,16 @@ void ParseSingleNalu(RtpDepacketizer::ParsedPayload* parsed_payload, parsed_payload->frame_type = kVideoFrameDelta; break; } + return true; } -void ParseFuaNalu(RtpDepacketizer::ParsedPayload* parsed_payload, +bool ParseFuaNalu(RtpDepacketizer::ParsedPayload* parsed_payload, const uint8_t* payload_data, size_t payload_data_length, size_t* offset) { + if (payload_data_length < kFuAHeaderSize) { + return false; + } uint8_t fnri = payload_data[0] & (kFBit | kNriMask); uint8_t original_nal_type = payload_data[1] & kTypeMask; bool first_fragment = (payload_data[1] & kSBit) > 0; @@ -113,6 +120,7 @@ void ParseFuaNalu(RtpDepacketizer::ParsedPayload* parsed_payload, &parsed_payload->type.Video.codecHeader.H264; h264_header->packetization_type = kH264FuA; h264_header->nalu_type = original_nal_type; + return true; } } // namespace @@ -316,15 +324,23 @@ bool RtpDepacketizerH264::Parse(ParsedPayload* parsed_payload, const uint8_t* payload_data, size_t payload_data_length) { assert(parsed_payload != NULL); + if (payload_data_length == 0) { + return false; + } + uint8_t nal_type = payload_data[0] & kTypeMask; size_t offset = 0; if (nal_type == kFuA) { // Fragmented NAL units (FU-A). - ParseFuaNalu(parsed_payload, payload_data, payload_data_length, &offset); + if (!ParseFuaNalu( + parsed_payload, payload_data, payload_data_length, &offset)) { + return false; + } } else { // We handle STAP-A and single NALU's the same way here. The jitter buffer // will depacketize the STAP-A into NAL units later. - ParseSingleNalu(parsed_payload, payload_data, payload_data_length); + if (!ParseSingleNalu(parsed_payload, payload_data, payload_data_length)) + return false; } parsed_payload->payload = payload_data + offset; diff --git a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc index 66a19ddf569..3ad5686fe9e 100644 --- a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc +++ b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc @@ -537,4 +537,36 @@ TEST_F(RtpDepacketizerH264Test, TestFuA) { EXPECT_EQ(kH264FuA, payload.type.Video.codecHeader.H264.packetization_type); EXPECT_EQ(kIdr, payload.type.Video.codecHeader.H264.nalu_type); } + +TEST_F(RtpDepacketizerH264Test, TestEmptyPayload) { + // Using a wild pointer to crash on accesses from inside the depacketizer. + uint8_t* garbage_ptr = reinterpret_cast<uint8_t*>(0x4711); + RtpDepacketizer::ParsedPayload payload; + EXPECT_FALSE(depacketizer_->Parse(&payload, garbage_ptr, 0)); +} + +TEST_F(RtpDepacketizerH264Test, TestTruncatedFuaNalu) { + const uint8_t kPayload[] = {0x9c}; + RtpDepacketizer::ParsedPayload payload; + EXPECT_FALSE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload))); +} + +TEST_F(RtpDepacketizerH264Test, TestTruncatedSingleStapANalu) { + const uint8_t kPayload[] = {0xd8, 0x27}; + RtpDepacketizer::ParsedPayload payload; + EXPECT_FALSE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload))); +} + +TEST_F(RtpDepacketizerH264Test, TestTruncationJustAfterSingleStapANalu) { + const uint8_t kPayload[] = {0x38, 0x27, 0x27}; + RtpDepacketizer::ParsedPayload payload; + EXPECT_FALSE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload))); +} + +TEST_F(RtpDepacketizerH264Test, TestShortSpsPacket) { + const uint8_t kPayload[] = {0x27, 0x80, 0x00}; + RtpDepacketizer::ParsedPayload payload; + EXPECT_TRUE(depacketizer_->Parse(&payload, kPayload, sizeof(kPayload))); +} + } // namespace webrtc diff --git a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.cc b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.cc index 1fa288acad7..5b74aafc851 100644 --- a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.cc +++ b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.cc @@ -90,6 +90,9 @@ bool RtpDepacketizerGeneric::Parse(ParsedPayload* parsed_payload, const uint8_t* payload_data, size_t payload_data_length) { assert(parsed_payload != NULL); + if (payload_data_length == 0) { + return false; + } uint8_t generic_header = *payload_data++; --payload_data_length; diff --git a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc index 5202754caf4..1dc799968d0 100644 --- a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc +++ b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_vp8.cc @@ -668,6 +668,10 @@ bool RtpDepacketizerVp8::Parse(ParsedPayload* parsed_payload, const uint8_t* payload_data, size_t payload_data_length) { assert(parsed_payload != NULL); + if (payload_data_length == 0) { + LOG(LS_ERROR) << "Empty payload."; + return false; + } // Parse mandatory first byte of payload descriptor. bool extension = (*payload_data & 0x80) ? true : false; // X bit diff --git a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc index 804dc090388..4283a778d00 100644 --- a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc +++ b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc @@ -596,4 +596,11 @@ TEST_F(RtpDepacketizerVp8Test, TestWithPacketizer) { EXPECT_EQ(payload.type.Video.codecHeader.VP8.layerSync, input_header.layerSync); } + +TEST_F(RtpDepacketizerVp8Test, TestEmptyPayload) { + // Using a wild pointer to crash on accesses from inside the depacketizer. + uint8_t* garbage_ptr = reinterpret_cast<uint8_t*>(0x4711); + RtpDepacketizer::ParsedPayload payload; + EXPECT_FALSE(depacketizer_->Parse(&payload, garbage_ptr, 0)); +} } // namespace webrtc diff --git a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc index df3067ac6f3..8e2ff1742ef 100644 --- a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc +++ b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc @@ -237,7 +237,8 @@ bool RTPPayloadRegistry::RestoreOriginalPacket(uint8_t** restored_packet, size_t* packet_length, uint32_t original_ssrc, const RTPHeader& header) const { - if (kRtxHeaderSize + header.headerLength > *packet_length) { + if (kRtxHeaderSize + header.headerLength + header.paddingLength > + *packet_length) { return false; } const uint8_t* rtx_header = packet + header.headerLength; diff --git a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc index 8d8147cd638..ff64e49cafa 100644 --- a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc +++ b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_receiver_video.cc @@ -13,6 +13,7 @@ #include <assert.h> #include <string.h> +#include "webrtc/base/checks.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_cvo.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h" #include "webrtc/modules/rtp_rtcp/source/rtp_format.h" @@ -60,6 +61,7 @@ int32_t RTPReceiverVideo::ParseRtpPacket(WebRtcRTPHeader* rtp_header, rtp_header->header.timestamp); rtp_header->type.Video.codec = specific_payload.Video.videoCodecType; + DCHECK_GE(payload_length, rtp_header->header.paddingLength); const size_t payload_data_length = payload_length - rtp_header->header.paddingLength; diff --git a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc index 566772a42a7..23300bbff60 100644 --- a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc +++ b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc @@ -770,8 +770,10 @@ TEST_F(RtpSenderTest, SendPadding) { EXPECT_EQ(kMaxPaddingLength + rtp_header_len, transport_.last_sent_packet_len_); // Parse sent packet. - ASSERT_TRUE(rtp_parser->Parse(transport_.last_sent_packet_, kPaddingBytes, + ASSERT_TRUE(rtp_parser->Parse(transport_.last_sent_packet_, + transport_.last_sent_packet_len_, &rtp_header)); + EXPECT_EQ(kMaxPaddingLength, rtp_header.paddingLength); // Verify sequence number and timestamp. EXPECT_EQ(seq_num++, rtp_header.sequenceNumber); diff --git a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_utility.cc b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_utility.cc index 3fef05355c7..0d083bd92a5 100644 --- a/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_utility.cc +++ b/chromium/third_party/webrtc/modules/rtp_rtcp/source/rtp_utility.cc @@ -355,6 +355,8 @@ bool RtpHeaderParser::Parse(RTPHeader& header, } header.headerLength += XLen; } + if (header.headerLength + header.paddingLength > static_cast<size_t>(length)) + return false; return true; } diff --git a/chromium/third_party/webrtc/video/rampup_tests.cc b/chromium/third_party/webrtc/video/rampup_tests.cc index 6d6696004b2..934603229fb 100644 --- a/chromium/third_party/webrtc/video/rampup_tests.cc +++ b/chromium/third_party/webrtc/video/rampup_tests.cc @@ -131,7 +131,9 @@ bool StreamObserver::SendRtp(const uint8_t* packet, size_t length) { ++total_packets_sent_; if (header.paddingLength > 0) ++padding_packets_sent_; - if (rtx_media_ssrcs_.find(header.ssrc) != rtx_media_ssrcs_.end()) { + // Handle RTX retransmission, but only for non-padding-only packets. + if (rtx_media_ssrcs_.find(header.ssrc) != rtx_media_ssrcs_.end() && + header.headerLength + header.paddingLength != length) { rtx_media_sent_ += length - header.headerLength - header.paddingLength; if (header.paddingLength == 0) ++rtx_media_packets_sent_; @@ -141,9 +143,8 @@ bool StreamObserver::SendRtp(const uint8_t* packet, size_t length) { EXPECT_TRUE(payload_registry_->RestoreOriginalPacket( &restored_packet_ptr, packet, &restored_length, rtx_media_ssrcs_[header.ssrc], header)); - length = restored_length; - EXPECT_TRUE(rtp_parser_->Parse( - restored_packet, static_cast<int>(length), &header)); + EXPECT_TRUE( + rtp_parser_->Parse(restored_packet_ptr, restored_length, &header)); } else { rtp_rtcp_->SetRemoteSSRC(header.ssrc); } |