diff --git a/accessible/jsat/AccessFu.jsm b/accessible/jsat/AccessFu.jsm index f78b870c8368..cba9ee07ce95 100644 --- a/accessible/jsat/AccessFu.jsm +++ b/accessible/jsat/AccessFu.jsm @@ -33,8 +33,7 @@ this.AccessFu = { // jshint ignore:line Utils.init(aWindow); try { - Services.androidBridge.handleGeckoMessage( - { type: 'Accessibility:Ready' }); + Services.androidBridge.dispatch('Accessibility:Ready'); Services.obs.addObserver(this, 'Accessibility:Settings', false); } catch (x) { // Not on Android @@ -602,7 +601,6 @@ var Output = { } for (let androidEvent of aDetails) { - androidEvent.type = 'Accessibility:Event'; if (androidEvent.bounds) { androidEvent.bounds = AccessFu.adjustContentBounds( androidEvent.bounds, aBrowser); @@ -622,7 +620,9 @@ var Output = { androidEvent.brailleOutput); break; } - this.androidBridge.handleGeckoMessage(androidEvent); + let win = Utils.win; + let view = win && win.QueryInterface(Ci.nsIAndroidView); + view.dispatch('Accessibility:Event', androidEvent); } }, @@ -818,8 +818,9 @@ var Input = { if (Utils.MozBuildApp == 'mobile/android') { // Return focus to native Android browser chrome. - Services.androidBridge.handleGeckoMessage( - { type: 'ToggleChrome:Focus' }); + let win = Utils.win; + let view = win && win.QueryInterface(Ci.nsIAndroidView); + view.dispatch('ToggleChrome:Focus'); } break; case aEvent.DOM_VK_RETURN: diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 18d0fff5c8c6..5892886058ff 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -5731,6 +5731,8 @@ nsDocShell::Destroy() NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome, "Unexpected item type in docshell"); + AssertOriginAttributesMatchPrivateBrowsing(); + if (!mIsBeingDestroyed) { nsCOMPtr serv = services::GetObserverService(); if (serv) { @@ -14344,6 +14346,7 @@ nsDocShell::SetOriginAttributes(const DocShellOriginAttributes& aAttrs) } SetPrivateBrowsing(isPrivate); + AssertOriginAttributesMatchPrivateBrowsing(); return NS_OK; } diff --git a/dom/base/DOMIntersectionObserver.cpp b/dom/base/DOMIntersectionObserver.cpp index 4833789998a6..cac77d144e1b 100644 --- a/dom/base/DOMIntersectionObserver.cpp +++ b/dom/base/DOMIntersectionObserver.cpp @@ -47,6 +47,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMIntersectionObserver) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback) NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot) NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueuedEntries) + tmp->Disconnect(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMIntersectionObserver) @@ -185,9 +186,10 @@ DOMIntersectionObserver::Connect() if (mConnected) { return; } + + mConnected = true; nsIDocument* document = mOwner->GetExtantDoc(); document->AddIntersectionObserver(this); - mConnected = true; } void diff --git a/dom/base/DOMIntersectionObserver.h b/dom/base/DOMIntersectionObserver.h index 3eb10ad38315..8144fc5c5ceb 100644 --- a/dom/base/DOMIntersectionObserver.h +++ b/dom/base/DOMIntersectionObserver.h @@ -101,9 +101,7 @@ protected: class DOMIntersectionObserver final : public nsISupports, public nsWrapperCache { - virtual ~DOMIntersectionObserver() { - Disconnect(); - } + virtual ~DOMIntersectionObserver() { } public: DOMIntersectionObserver(already_AddRefed&& aOwner, diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 1f552954a278..d30c473d1b34 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -3869,7 +3869,7 @@ Element::ClearDataset() slots->mDataset = nullptr; } -nsTArray* +nsDataHashtable, int32_t>* Element::RegisteredIntersectionObservers() { nsDOMSlots* slots = DOMSlots(); @@ -3879,34 +3879,34 @@ Element::RegisteredIntersectionObservers() void Element::RegisterIntersectionObserver(DOMIntersectionObserver* aObserver) { - RegisteredIntersectionObservers()->AppendElement( - nsDOMSlots::IntersectionObserverRegistration { aObserver, -1 }); + nsDataHashtable, int32_t>* observers = + RegisteredIntersectionObservers(); + if (observers->Contains(aObserver)) { + return; + } + RegisteredIntersectionObservers()->Put(aObserver, -1); } void Element::UnregisterIntersectionObserver(DOMIntersectionObserver* aObserver) { - nsTArray* observers = + nsDataHashtable, int32_t>* observers = RegisteredIntersectionObservers(); - for (uint32_t i = 0; i < observers->Length(); ++i) { - nsDOMSlots::IntersectionObserverRegistration reg = observers->ElementAt(i); - if (reg.observer == aObserver) { - observers->RemoveElementAt(i); - break; - } - } + observers->Remove(aObserver); } bool Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver, int32_t aThreshold) { - nsTArray* observers = + nsDataHashtable, int32_t>* observers = RegisteredIntersectionObservers(); - for (auto& reg : *observers) { - if (reg.observer == aObserver && reg.previousThreshold != aThreshold) { - reg.previousThreshold = aThreshold; - return true; - } + if (!observers->Contains(aObserver)) { + return false; + } + int32_t previousThreshold = observers->Get(aObserver); + if (previousThreshold != aThreshold) { + observers->Put(aObserver, aThreshold); + return true; } return false; } diff --git a/dom/base/Element.h b/dom/base/Element.h index 2ebf2cf4f9be..05e9044c67ba 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -1423,7 +1423,7 @@ protected: nsDOMTokenList* GetTokenList(nsIAtom* aAtom, const DOMTokenListSupportedTokenArray aSupportedTokens = nullptr); - nsTArray* RegisteredIntersectionObservers(); + nsDataHashtable, int32_t>* RegisteredIntersectionObservers(); private: /** diff --git a/dom/base/FragmentOrElement.h b/dom/base/FragmentOrElement.h index 3cb5575fe063..65ab7bea0cdb 100644 --- a/dom/base/FragmentOrElement.h +++ b/dom/base/FragmentOrElement.h @@ -21,6 +21,7 @@ #include "nsIWeakReference.h" // base class #include "nsNodeUtils.h" // class member nsNodeUtils::CloneNodeImpl #include "nsIHTMLCollection.h" +#include "nsDataHashtable.h" class ContentUnbinder; class nsContentList; @@ -347,12 +348,7 @@ public: /** * Registered Intersection Observers on the element. */ - struct IntersectionObserverRegistration { - DOMIntersectionObserver* observer; - int32_t previousThreshold; - }; - - nsTArray mRegisteredIntersectionObservers; + nsDataHashtable, int32_t> mRegisteredIntersectionObservers; }; protected: diff --git a/dom/base/nsNodeUtils.cpp b/dom/base/nsNodeUtils.cpp index 21e39bb73081..5eda61e9b38b 100644 --- a/dom/base/nsNodeUtils.cpp +++ b/dom/base/nsNodeUtils.cpp @@ -301,8 +301,9 @@ nsNodeUtils::LastRelease(nsINode* aNode) Element* elem = aNode->AsElement(); FragmentOrElement::nsDOMSlots* domSlots = static_cast(slots); - for (auto& reg : domSlots->mRegisteredIntersectionObservers) { - reg.observer->UnlinkTarget(*elem); + for (auto iter = domSlots->mRegisteredIntersectionObservers.Iter(); !iter.Done(); iter.Next()) { + DOMIntersectionObserver* observer = iter.Key(); + observer->UnlinkTarget(*elem); } } diff --git a/dom/base/test/test_intersectionobservers.html b/dom/base/test/test_intersectionobservers.html index e7875e3af071..434811dfcb96 100644 --- a/dom/base/test/test_intersectionobservers.html +++ b/dom/base/test/test_intersectionobservers.html @@ -891,6 +891,19 @@ limitations under the License. var win = window.open("intersectionobserver_window.html"); }); + it('triggers only once if observed multiple times (and does not crash when collected)', function(done) { + var spy = sinon.spy(); + io = new IntersectionObserver(spy, {root: rootEl}); + io.observe(targetEl1); + io.observe(targetEl1); + io.observe(targetEl1); + + callDelayed(function () { + expect(spy.callCount).to.be(1); + done(); + }, ASYNC_TIMEOUT); + }); + }); describe('observe subframe', function () { diff --git a/dom/events/test/pointerevents/mochitest_support_external.js b/dom/events/test/pointerevents/mochitest_support_external.js index 01c340728714..3e43728c805d 100644 --- a/dom/events/test/pointerevents/mochitest_support_external.js +++ b/dom/events/test/pointerevents/mochitest_support_external.js @@ -35,18 +35,95 @@ function turnOnPointerEvents(callback) { }, callback); } +// Mouse Event Helper Object +var MouseEventHelper = (function() { + var utils = SpecialPowers.Ci.nsIDOMWindowUtils; + + return { + // State + // TODO: Separate this to support mouse and pen simultaneously. + BUTTONS_STATE: utils.MOUSE_BUTTONS_NO_BUTTON, + + // Button + BUTTON_NONE: -1, // Used by test framework only. (replaced before sending) + BUTTON_LEFT: utils.MOUSE_BUTTON_LEFT_BUTTON, + BUTTON_MIDDLE: utils.MOUSE_BUTTON_MIDDLE_BUTTON, + BUTTON_RIGHT: utils.MOUSE_BUTTON_RIGHT_BUTTON, + + // Buttons + BUTTONS_NONE: utils.MOUSE_BUTTONS_NO_BUTTON, + BUTTONS_LEFT: utils.MOUSE_BUTTONS_LEFT_BUTTON, + BUTTONS_MIDDLE: utils.MOUSE_BUTTONS_MIDDLE_BUTTON, + BUTTONS_RIGHT: utils.MOUSE_BUTTONS_RIGHT_BUTTON, + BUTTONS_4TH: utils.MOUSE_BUTTONS_4TH_BUTTON, + BUTTONS_5TH: utils.MOUSE_BUTTONS_5TH_BUTTON, + + // Utils + computeButtonsMaskFromButton: function(aButton) { + // Since the range of button values is 0 ~ 2 (see nsIDOMWindowUtils.idl), + // we can use an array to find out the desired mask. + var mask = [ + this.BUTTONS_NONE, // -1 (MouseEventHelper.BUTTON_NONE) + this.BUTTONS_LEFT, // 0 + this.BUTTONS_MIDDLE, // 1 + this.BUTTONS_RIGHT // 2 + ][aButton + 1]; + + ok(mask !== undefined, "Unrecognized button value caught!"); + return mask; + }, + + checkExitState: function() { + ok(!this.BUTTONS_STATE, "Mismatched mousedown/mouseup caught."); + } + }; +}) (); + // Helper function to send MouseEvent with different parameters function sendMouseEvent(int_win, elemId, mouseEventType, params) { var elem = int_win.document.getElementById(elemId); if(!!elem) { var rect = elem.getBoundingClientRect(); var eventObj = {type: mouseEventType}; - if(params && "button" in params) - eventObj.button = params.button; + if(params && "inputSource" in params) eventObj.inputSource = params.inputSource; - if(params && "buttons" in params) - eventObj.buttons = params.buttons; + + // Check or generate a |button| value. + var isButtonEvent = mouseEventType === "mouseup" || + mouseEventType === "mousedown"; + + // Set |button| to the default value first. + eventObj.button = isButtonEvent ? MouseEventHelper.BUTTON_LEFT + : MouseEventHelper.BUTTON_NONE; + + // |button| is passed, use and check it. + if (params && "button" in params) { + var hasButtonValue = (params.button !== MouseEventHelper.BUTTON_NONE); + ok(!isButtonEvent || hasButtonValue, + "Inappropriate |button| value caught."); + eventObj.button = params.button; + } + + // Generate a |buttons| value and update buttons state + var buttonsMask = MouseEventHelper.computeButtonsMaskFromButton(eventObj.button); + switch(mouseEventType) { + case "mousedown": + MouseEventHelper.BUTTONS_STATE |= buttonsMask; // Set button flag. + break; + case "mouseup": + MouseEventHelper.BUTTONS_STATE &= ~buttonsMask; // Clear button flag. + break; + } + eventObj.buttons = MouseEventHelper.BUTTONS_STATE; + + // Replace the button value for mousemove events. + // Since in widget level design, even when no button is pressed at all, the + // value of WidgetMouseEvent.button is still 0, which is the same value as + // the one for mouse left button. + if (mouseEventType === "mousemove") { + eventObj.button = MouseEventHelper.BUTTON_LEFT; + } // Default to the center of the target element but we can still send to a // position outside of the target element. @@ -61,6 +138,18 @@ function sendMouseEvent(int_win, elemId, mouseEventType, params) { } } +// Touch Event Helper Object +var TouchEventHelper = { + // State + // TODO: Support multiple point scenarios. + TOUCH_STATE: false, + + // Utils + checkExitState: function() { + ok(!this.TOUCH_STATE, "Mismatched touchstart/touchend caught."); + } +} + // Helper function to send TouchEvent with different parameters function sendTouchEvent(int_win, elemId, touchEventType, params) { var elem = int_win.document.getElementById(elemId); @@ -68,6 +157,16 @@ function sendTouchEvent(int_win, elemId, touchEventType, params) { var rect = elem.getBoundingClientRect(); var eventObj = {type: touchEventType}; + // Update touch state + switch(touchEventType) { + case "touchstart": + TouchEventHelper.TOUCH_STATE = true; // Set touch flag. + break; + case "touchend": + TouchEventHelper.TOUCH_STATE = false; // Clear touch flag. + break; + } + // Default to the center of the target element but we can still send to a // position outside of the target element. var offsetX = params && "offsetX" in params ? params.offsetX : rect.width / 2; @@ -82,42 +181,25 @@ function sendTouchEvent(int_win, elemId, touchEventType, params) { // Helper function to run Point Event test in a new tab. function runTestInNewWindow(aFile) { - var w = window.open('', "_blank"); - w.is = function(a, b, msg) { return is(a, b, aFile + " | " + msg); }; - w.ok = function(cond, name, diag) { return ok(cond, aFile + " | " + name, diag); }; - w.location = location.href.substring(0, location.href.lastIndexOf('/') + 1) + aFile; + var testURL = location.href.substring(0, location.href.lastIndexOf('/') + 1) + aFile; + var testWindow = window.open(testURL, "_blank"); - w.testContext = { - result_callback: (aTestObj) => { - if(aTestObj["status"] != aTestObj["PASS"]) { - console.log(aTestObj["status"] + " = " + aTestObj["PASS"] + ". " + aTestObj["name"]); - } - is(aTestObj["status"], aTestObj["PASS"], aTestObj["name"]); - }, - - completion_callback: () => { - if (!!w.testContext.executionPromise) { - // We need to wait tests done and execute finished then we can close the window - w.testContext.executionPromise.then(() => { - w.close(); - SimpleTest.finish(); - }); - } else { - // execute may synchronous trigger tests done. In that case executionPromise - // is not yet assigned - w.close(); - SimpleTest.finish(); - } - }, - - execute: (aWindow) => { - turnOnPointerEvents(() => { - w.testContext.executionPromise = new Promise((aResolve, aReject) => { - executeTest(aWindow); - aResolve(); + window.addEventListener("message", function(aEvent) { + switch(aEvent.data.type) { + case "START": + turnOnPointerEvents(() => { + executeTest(testWindow); }); - }); + return; + case "RESULT": + ok(aEvent.data.result, aEvent.data.message); + return; + case "FIN": + MouseEventHelper.checkExitState(); + TouchEventHelper.checkExitState(); + testWindow.close(); + SimpleTest.finish(); + return; } - }; - return w; + }); } diff --git a/dom/events/test/pointerevents/mochitest_support_internal.js b/dom/events/test/pointerevents/mochitest_support_internal.js index f8161362ce67..100ae2dea30b 100644 --- a/dom/events/test/pointerevents/mochitest_support_internal.js +++ b/dom/events/test/pointerevents/mochitest_support_internal.js @@ -2,21 +2,34 @@ // to tests on auto MochiTest system with minimum changes. // Author: Maksim Lebedev -// Function allows to prepare our tests after load document -addEventListener("load", function(event) { +const PARENT_ORIGIN = "http://mochi.test:8888/"; + +addEventListener("load", function() { + // Setup environment. console.log("OnLoad internal document"); addListeners(document.getElementById("target0")); addListeners(document.getElementById("target1")); - preExecute(); -}, false); -// Function allows to initialize prerequisites before testing -// and adds some callbacks to support mochitest system. -function preExecute() { - add_result_callback(testContext.result_callback); - add_completion_callback(testContext.completion_callback); - testContext.execute(window); -} + // Setup communication between mochitest_support_external.js. + // Function allows to initialize prerequisites before testing + // and adds some callbacks to support mochitest system. + add_result_callback((aTestObj) => { + var message = aTestObj["name"] + " ("; + message += "Get: " + JSON.stringify(aTestObj["status"]) + ", "; + message += "Expect: " + JSON.stringify(aTestObj["PASS"]) + ")"; + window.opener.postMessage({type: "RESULT", + message: message, + result: aTestObj["status"] === aTestObj["PASS"]}, + PARENT_ORIGIN); + }); + + add_completion_callback(() => { + window.opener.postMessage({type: "FIN"}, PARENT_ORIGIN); + }); + + // Start testing. + window.opener.postMessage({type: "START"}, PARENT_ORIGIN); +}); function addListeners(elem) { if(!elem) diff --git a/dom/events/test/pointerevents/test_pointerevent_attributes_mouse-manual.html b/dom/events/test/pointerevents/test_pointerevent_attributes_mouse-manual.html index 1cda30c35ecf..27d1a4a2dcd1 100644 --- a/dom/events/test/pointerevents/test_pointerevent_attributes_mouse-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_attributes_mouse-manual.html @@ -17,13 +17,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 runTestInNewWindow("pointerevent_attributes_mouse-manual.html"); } function executeTest(int_win) { - sendMouseEvent(int_win, "square1", "mousemove", {button:-1}); - sendMouseEvent(int_win, "square1", "mousedown", {button:0}); - sendMouseEvent(int_win, "square1", "mouseup", {button:0}); - sendMouseEvent(int_win, "square1", "mousemove", {button:-1}); - sendMouseEvent(int_win, "square1", "mousemove", {button:-1, - offsetX: -1, - offsetY: -1}); + sendMouseEvent(int_win, "square1", "mousemove"); + sendMouseEvent(int_win, "square1", "mousedown"); + sendMouseEvent(int_win, "square1", "mouseup"); + sendMouseEvent(int_win, "square1", "mousemove"); + sendMouseEvent(int_win, "square1", "mousemove", {offsetX: -1, offSetY: -1}); } diff --git a/dom/events/test/pointerevents/test_pointerevent_capture_mouse-manual.html b/dom/events/test/pointerevents/test_pointerevent_capture_mouse-manual.html index d2e0c52bf314..dcd999cae6d1 100644 --- a/dom/events/test/pointerevents/test_pointerevent_capture_mouse-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_capture_mouse-manual.html @@ -20,7 +20,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 sendMouseEvent(int_win, "btnCapture", "mousemove"); sendMouseEvent(int_win, "target0", "mousemove"); sendMouseEvent(int_win, "target1", "mousemove"); - sendMouseEvent(int_win, "btnCapture", "mousedown", {button:1}); + sendMouseEvent(int_win, "btnCapture", "mousedown"); sendMouseEvent(int_win, "target1", "mousemove"); sendMouseEvent(int_win, "target1", "mouseup"); sendMouseEvent(int_win, "target1", "mousemove"); diff --git a/dom/events/test/pointerevents/test_pointerevent_capture_suppressing_mouse-manual.html b/dom/events/test/pointerevents/test_pointerevent_capture_suppressing_mouse-manual.html index dc39035922ce..5eb631f6a041 100644 --- a/dom/events/test/pointerevents/test_pointerevent_capture_suppressing_mouse-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_capture_suppressing_mouse-manual.html @@ -19,7 +19,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 function executeTest(int_win) { sendMouseEvent(int_win, "target0", "mousemove"); sendMouseEvent(int_win, "target1", "mousemove"); - sendMouseEvent(int_win, "btnCapture", "mousedown", {button:1}); + sendMouseEvent(int_win, "btnCapture", "mousedown"); sendMouseEvent(int_win, "target1", "mousemove"); sendMouseEvent(int_win, "target0", "mousemove"); sendMouseEvent(int_win, "target1", "mousemove"); diff --git a/dom/events/test/pointerevents/test_pointerevent_element_haspointercapture-manual.html b/dom/events/test/pointerevents/test_pointerevent_element_haspointercapture-manual.html index 215e93b57526..899b18641741 100644 --- a/dom/events/test/pointerevents/test_pointerevent_element_haspointercapture-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_element_haspointercapture-manual.html @@ -18,12 +18,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 } function executeTest(int_win) { sendMouseEvent(int_win, "target0", "mousemove"); - sendMouseEvent(int_win, "target0", "mousedown", {button:0}); - sendMouseEvent(int_win, "target0", "mousemove", {button:0}); - sendMouseEvent(int_win, "target1", "mousemove", {button:0}); - sendMouseEvent(int_win, "target1", "mouseup", {button:0}); - sendMouseEvent(int_win, "target1", "mousedown", {button:0}); - sendMouseEvent(int_win, "target1", "mouseup", {button:0}); + sendMouseEvent(int_win, "target0", "mousedown"); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target1", "mousemove"); + sendMouseEvent(int_win, "target1", "mouseup"); + sendMouseEvent(int_win, "target1", "mousedown"); + sendMouseEvent(int_win, "target1", "mouseup"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_element_haspointercapture_release_pending_capture-manual.html b/dom/events/test/pointerevents/test_pointerevent_element_haspointercapture_release_pending_capture-manual.html index 1ec769bc731e..1e8f82717e34 100644 --- a/dom/events/test/pointerevents/test_pointerevent_element_haspointercapture_release_pending_capture-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_element_haspointercapture_release_pending_capture-manual.html @@ -18,10 +18,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 } function executeTest(int_win) { sendMouseEvent(int_win, "target0", "mousemove"); - sendMouseEvent(int_win, "target0", "mousedown", {button:0}); - sendMouseEvent(int_win, "target0", "mousemove", {button:0}); - sendMouseEvent(int_win, "target1", "mousemove", {button:0}); - sendMouseEvent(int_win, "target1", "mouseup", {button:0}); + sendMouseEvent(int_win, "target0", "mousedown"); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target1", "mousemove"); + sendMouseEvent(int_win, "target1", "mouseup"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_gotpointercapture_before_first_pointerevent-manual.html b/dom/events/test/pointerevents/test_pointerevent_gotpointercapture_before_first_pointerevent-manual.html index 875e18429e9a..ecb2fd2f0a04 100644 --- a/dom/events/test/pointerevents/test_pointerevent_gotpointercapture_before_first_pointerevent-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_gotpointercapture_before_first_pointerevent-manual.html @@ -17,8 +17,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 runTestInNewWindow("pointerevent_gotpointercapture_before_first_pointerevent-manual.html"); } function executeTest(int_win) { - sendMouseEvent(int_win, "target0", "mousedown", {button:0}); - sendMouseEvent(int_win, "target0", "mouseup", {button:0}); + sendMouseEvent(int_win, "target0", "mousedown"); + sendMouseEvent(int_win, "target0", "mouseup"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_lostpointercapture_for_disconnected_node-manual.html b/dom/events/test/pointerevents/test_pointerevent_lostpointercapture_for_disconnected_node-manual.html index b381de108116..5b292dfd91de 100644 --- a/dom/events/test/pointerevents/test_pointerevent_lostpointercapture_for_disconnected_node-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_lostpointercapture_for_disconnected_node-manual.html @@ -22,6 +22,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 sendMouseEvent(int_win, "btnCapture", "mousemove"); setTimeout(function() { sendMouseEvent(int_win, "target1", "mousemove"); + sendMouseEvent(int_win, "target1", "mouseup"); }, 500); } diff --git a/dom/events/test/pointerevents/test_pointerevent_lostpointercapture_is_first-manual.html b/dom/events/test/pointerevents/test_pointerevent_lostpointercapture_is_first-manual.html index 43071b67f245..57001a49d5e8 100644 --- a/dom/events/test/pointerevents/test_pointerevent_lostpointercapture_is_first-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_lostpointercapture_is_first-manual.html @@ -17,8 +17,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 runTestInNewWindow("pointerevent_lostpointercapture_is_first-manual.html"); } function executeTest(int_win) { - sendMouseEvent(int_win, "btnCapture", "mousedown", {button:0}); - sendMouseEvent(int_win, "btnCapture", "mouseup", {button:0}); + sendMouseEvent(int_win, "btnCapture", "mousedown"); + sendMouseEvent(int_win, "btnCapture", "mouseup"); sendMouseEvent(int_win, "btnCapture", "mousemove"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_multiple_primary_pointers_boundary_events-manual.html b/dom/events/test/pointerevents/test_pointerevent_multiple_primary_pointers_boundary_events-manual.html index d3c2f89624fe..59506d0fb6bf 100644 --- a/dom/events/test/pointerevents/test_pointerevent_multiple_primary_pointers_boundary_events-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_multiple_primary_pointers_boundary_events-manual.html @@ -21,8 +21,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 sendTouchEvent(int_win, "target1", "touchstart"); sendTouchEvent(int_win, "target1", "touchend"); sendMouseEvent(int_win, "target0", "mousemove"); - sendMouseEvent(int_win, "done", "mousedown", {button:0}); - sendMouseEvent(int_win, "done", "mouseup", {button:0}); + sendMouseEvent(int_win, "done", "mousedown"); + sendMouseEvent(int_win, "done", "mouseup"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_pointercancel_touch-manual.html b/dom/events/test/pointerevents/test_pointerevent_pointercancel_touch-manual.html index de283935048a..63fde3ef20aa 100644 --- a/dom/events/test/pointerevents/test_pointerevent_pointercancel_touch-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_pointercancel_touch-manual.html @@ -19,6 +19,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 function executeTest(int_win) { sendTouchEvent(int_win, "target0", "touchstart"); sendTouchEvent(int_win, "target0", "touchcancel"); + + // Need a touchend event to terminated the test gracefully. + sendTouchEvent(int_win, "target0", "touchend"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_pointerdown-manual.html b/dom/events/test/pointerevents/test_pointerevent_pointerdown-manual.html index f6febcfd43c6..0b76c000b492 100644 --- a/dom/events/test/pointerevents/test_pointerevent_pointerdown-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_pointerdown-manual.html @@ -18,7 +18,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 } function executeTest(int_win) { sendMouseEvent(int_win, "log", "mousemove"); - sendMouseEvent(int_win, "target0", "mousedown", {button:1}); + sendMouseEvent(int_win, "target0", "mousedown"); sendMouseEvent(int_win, "target0", "mouseup"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_pointerenter-manual.html b/dom/events/test/pointerevents/test_pointerevent_pointerenter-manual.html index 9c22471982d9..44658db4b44c 100644 --- a/dom/events/test/pointerevents/test_pointerevent_pointerenter-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_pointerenter-manual.html @@ -18,6 +18,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 } function executeTest(int_win) { sendMouseEvent(int_win, "target0", "mousedown"); + sendMouseEvent(int_win, "target0", "mouseup"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_pointerleave_after_pointercancel_touch-manual.html b/dom/events/test/pointerevents/test_pointerevent_pointerleave_after_pointercancel_touch-manual.html index 89419e29b4a8..e76b54169a62 100644 --- a/dom/events/test/pointerevents/test_pointerevent_pointerleave_after_pointercancel_touch-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_pointerleave_after_pointercancel_touch-manual.html @@ -19,6 +19,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 function executeTest(int_win) { sendTouchEvent(int_win, "target0", "touchstart"); sendTouchEvent(int_win, "target0", "touchcancel"); + + // Need a touchend event to terminated the test gracefully. + sendTouchEvent(int_win, "target0", "touchend"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_pointermove-on-chorded-mouse-button.html b/dom/events/test/pointerevents/test_pointerevent_pointermove-on-chorded-mouse-button.html index 6e90b4230121..5cbcb09a97ea 100644 --- a/dom/events/test/pointerevents/test_pointerevent_pointermove-on-chorded-mouse-button.html +++ b/dom/events/test/pointerevents/test_pointerevent_pointermove-on-chorded-mouse-button.html @@ -18,17 +18,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 } function executeTest(int_win) { - var utils = _getDOMWindowUtils(int_win); - sendMouseEvent(int_win, "target0", "mousemove", {button:0, buttons:utils.MOUSE_BUTTONS_NO_BUTTON}); - sendMouseEvent(int_win, "target0", "mousedown", {button:0, buttons:utils.MOUSE_BUTTONS_LEFT_BUTTON}); - sendMouseEvent(int_win, "target0", "mousemove", {button:0, buttons:utils.MOUSE_BUTTONS_LEFT_BUTTON}); - sendMouseEvent(int_win, "target0", "mousedown", {button:1, buttons:utils.MOUSE_BUTTONS_LEFT_BUTTON | - utils.MOUSE_BUTTONS_MIDDLE_BUTTON}); - sendMouseEvent(int_win, "target0", "mousemove", {button:1, buttons:utils.MOUSE_BUTTONS_LEFT_BUTTON | - utils.MOUSE_BUTTONS_MIDDLE_BUTTON}); - sendMouseEvent(int_win, "target0", "mouseup", {button:1, buttons:utils.MOUSE_BUTTONS_LEFT_BUTTON}); - sendMouseEvent(int_win, "target0", "mousemove", {button:1, buttons:utils.MOUSE_BUTTONS_LEFT_BUTTON}); - sendMouseEvent(int_win, "target0", "mouseup", {button:0, buttons:utils.MOUSE_BUTTONS_NO_BUTTON}); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mousedown", {button: MouseEventHelper.BUTTON_LEFT}); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mousedown", {button: MouseEventHelper.BUTTON_MIDDLE}); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mouseup", {button: MouseEventHelper.BUTTON_MIDDLE}); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mouseup", {button: MouseEventHelper.BUTTON_LEFT}); } diff --git a/dom/events/test/pointerevents/test_pointerevent_pointermove_pointertype-manual.html b/dom/events/test/pointerevents/test_pointerevent_pointermove_pointertype-manual.html index 7b2d139806a9..f80d02786f82 100644 --- a/dom/events/test/pointerevents/test_pointerevent_pointermove_pointertype-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_pointermove_pointertype-manual.html @@ -19,6 +19,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 function executeTest(int_win) { sendMouseEvent(int_win, "target0", "mousedown"); sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mouseup"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_pointerout_after_pointercancel_touch-manual.html b/dom/events/test/pointerevents/test_pointerevent_pointerout_after_pointercancel_touch-manual.html index c861caf26616..f5f5d9deb8d2 100644 --- a/dom/events/test/pointerevents/test_pointerevent_pointerout_after_pointercancel_touch-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_pointerout_after_pointercancel_touch-manual.html @@ -19,6 +19,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 function executeTest(int_win) { sendTouchEvent(int_win, "target0", "touchstart"); sendTouchEvent(int_win, "target0", "touchcancel"); + + // Need a touchend event to terminated the test gracefully. + sendTouchEvent(int_win, "target0", "touchend"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_pointertype_mouse-manual.html b/dom/events/test/pointerevents/test_pointerevent_pointertype_mouse-manual.html index aec2e8486287..500ed871486f 100644 --- a/dom/events/test/pointerevents/test_pointerevent_pointertype_mouse-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_pointertype_mouse-manual.html @@ -17,8 +17,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 runTestInNewWindow("pointerevent_pointertype_mouse-manual.html"); } function executeTest(int_win) { - sendMouseEvent(int_win, "target0", "mousedown", {button:0}); - sendMouseEvent(int_win, "target0", "mouseup", {button:0}); + sendMouseEvent(int_win, "target0", "mousedown"); + sendMouseEvent(int_win, "target0", "mouseup"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html b/dom/events/test/pointerevents/test_pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html index fc1fb6981946..8f36a988cae3 100644 --- a/dom/events/test/pointerevents/test_pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html @@ -17,8 +17,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 runTestInNewWindow("pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html"); } function executeTest(int_win) { - sendMouseEvent(int_win, "target0", "mousedown", {button:0}); - sendMouseEvent(int_win, "target0", "mouseup", {button:0}); + sendMouseEvent(int_win, "target0", "mousedown"); + sendMouseEvent(int_win, "target0", "mouseup"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_releasepointercapture_invalid_pointerid-manual.html b/dom/events/test/pointerevents/test_pointerevent_releasepointercapture_invalid_pointerid-manual.html index 55f347358317..24cbe28e0ac9 100644 --- a/dom/events/test/pointerevents/test_pointerevent_releasepointercapture_invalid_pointerid-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_releasepointercapture_invalid_pointerid-manual.html @@ -19,6 +19,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 function executeTest(int_win) { sendTouchEvent(int_win, "target0", "touchstart"); sendTouchEvent(int_win, "target0", "touchmove"); + + // Need a touchend event to terminated the test gracefully. + sendTouchEvent(int_win, "target0", "touchend"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_releasepointercapture_onpointercancel_touch-manual.html b/dom/events/test/pointerevents/test_pointerevent_releasepointercapture_onpointercancel_touch-manual.html index 6b63c307cd65..e5f7d61c1c1b 100644 --- a/dom/events/test/pointerevents/test_pointerevent_releasepointercapture_onpointercancel_touch-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_releasepointercapture_onpointercancel_touch-manual.html @@ -19,6 +19,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 function executeTest(int_win) { sendTouchEvent(int_win, "target0", "touchstart"); sendTouchEvent(int_win, "target0", "touchcancel"); + + // Need a touchend event to terminated the test gracefully. + sendTouchEvent(int_win, "target0", "touchend"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_releasepointercapture_release_right_after_capture-manual.html b/dom/events/test/pointerevents/test_pointerevent_releasepointercapture_release_right_after_capture-manual.html index 3f55c1afe131..67235f0a3016 100644 --- a/dom/events/test/pointerevents/test_pointerevent_releasepointercapture_release_right_after_capture-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_releasepointercapture_release_right_after_capture-manual.html @@ -17,11 +17,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 } function executeTest(int_win) { sendMouseEvent(int_win, "target0", "mousemove"); - sendMouseEvent(int_win, "target0", "mousedown", {button:0}); - sendMouseEvent(int_win, "target0", "mousemove", {button:0}); - sendMouseEvent(int_win, "target0", "mousemove", {button:0}); - sendMouseEvent(int_win, "target0", "mousemove", {button:0}); - sendMouseEvent(int_win, "target0", "mouseup", {button:0}); + sendMouseEvent(int_win, "target0", "mousedown"); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mouseup"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_setpointercapture_invalid_pointerid-manual.html b/dom/events/test/pointerevents/test_pointerevent_setpointercapture_invalid_pointerid-manual.html index 61171196a00f..07cfa9964259 100644 --- a/dom/events/test/pointerevents/test_pointerevent_setpointercapture_invalid_pointerid-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_setpointercapture_invalid_pointerid-manual.html @@ -18,6 +18,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 } function executeTest(int_win) { sendMouseEvent(int_win, "target0", "mousedown"); + sendMouseEvent(int_win, "target0", "mouseup"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_setpointercapture_override_pending_capture_element-manual.html b/dom/events/test/pointerevents/test_pointerevent_setpointercapture_override_pending_capture_element-manual.html index 5a3adede6e79..8c46e78ca7b7 100644 --- a/dom/events/test/pointerevents/test_pointerevent_setpointercapture_override_pending_capture_element-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_setpointercapture_override_pending_capture_element-manual.html @@ -18,11 +18,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 } function executeTest(int_win) { sendMouseEvent(int_win, "target0", "mousemove"); - sendMouseEvent(int_win, "target0", "mousedown", {button:0}); - sendMouseEvent(int_win, "target0", "mousemove", {button:0}); - sendMouseEvent(int_win, "target0", "mousemove", {button:0}); - sendMouseEvent(int_win, "target0", "mousemove", {button:0}); - sendMouseEvent(int_win, "target0", "mouseup", {button:0}); + sendMouseEvent(int_win, "target0", "mousedown"); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mouseup"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_setpointercapture_to_same_element_twice-manual.html b/dom/events/test/pointerevents/test_pointerevent_setpointercapture_to_same_element_twice-manual.html index e4af7dd56aa2..417b97810458 100644 --- a/dom/events/test/pointerevents/test_pointerevent_setpointercapture_to_same_element_twice-manual.html +++ b/dom/events/test/pointerevents/test_pointerevent_setpointercapture_to_same_element_twice-manual.html @@ -18,11 +18,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 } function executeTest(int_win) { sendMouseEvent(int_win, "target0", "mousemove"); - sendMouseEvent(int_win, "target0", "mousedown", {button:0}); - sendMouseEvent(int_win, "target0", "mousemove", {button:0}); - sendMouseEvent(int_win, "target0", "mousemove", {button:0}); - sendMouseEvent(int_win, "target0", "mousemove", {button:0}); - sendMouseEvent(int_win, "target0", "mouseup", {button:0}); + sendMouseEvent(int_win, "target0", "mousedown"); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mouseup"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_click.html b/dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_click.html index c091354493ce..dcee2e1463a9 100644 --- a/dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_click.html +++ b/dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_click.html @@ -17,12 +17,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 runTestInNewWindow("pointerevent_suppress_compat_events_on_click.html"); } function executeTest(int_win) { - sendMouseEvent(int_win, "target0", "mousedown", {button:0}); - sendMouseEvent(int_win, "target0", "mouseup", {button:0}); - sendMouseEvent(int_win, "target1", "mousedown", {button:0}); - sendMouseEvent(int_win, "target1", "mouseup", {button:0}); - sendMouseEvent(int_win, "done", "mousedown", {button:0}); - sendMouseEvent(int_win, "done", "mouseup", {button:0}); + sendMouseEvent(int_win, "target0", "mousedown"); + sendMouseEvent(int_win, "target0", "mouseup"); + sendMouseEvent(int_win, "target1", "mousedown"); + sendMouseEvent(int_win, "target1", "mouseup"); + sendMouseEvent(int_win, "done", "mousedown"); + sendMouseEvent(int_win, "done", "mouseup"); } diff --git a/dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_drag_mouse.html b/dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_drag_mouse.html index d379134cd33d..a0e658eb1088 100644 --- a/dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_drag_mouse.html +++ b/dom/events/test/pointerevents/test_pointerevent_suppress_compat_events_on_drag_mouse.html @@ -17,14 +17,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 runTestInNewWindow("pointerevent_suppress_compat_events_on_drag_mouse.html"); } function executeTest(int_win) { - sendMouseEvent(int_win, "target0", "mousedown", {button:0}); - sendMouseEvent(int_win, "target0", "mousemove", {button:0}); - sendMouseEvent(int_win, "target0", "mouseup", {button:0}); - sendMouseEvent(int_win, "target1", "mousedown", {button:0}); - sendMouseEvent(int_win, "target1", "mousemove", {button:0}); - sendMouseEvent(int_win, "target1", "mouseup", {button:0}); - sendMouseEvent(int_win, "done", "mousedown", {button:0}); - sendMouseEvent(int_win, "done", "mouseup", {button:0}); + sendMouseEvent(int_win, "target0", "mousedown"); + sendMouseEvent(int_win, "target0", "mousemove"); + sendMouseEvent(int_win, "target0", "mouseup"); + sendMouseEvent(int_win, "target1", "mousedown"); + sendMouseEvent(int_win, "target1", "mousemove"); + sendMouseEvent(int_win, "target1", "mouseup"); + sendMouseEvent(int_win, "done", "mousedown"); + sendMouseEvent(int_win, "done", "mouseup"); } diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 5c30ad4df176..10b0e855e3e2 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -1973,6 +1973,12 @@ interface nsIDOMWindowUtils : nsISupports { */ readonly attribute int32_t gpuProcessPid; + // Match WidgetMouseEventBase::buttonType. + const long MOUSE_BUTTON_LEFT_BUTTON = 0; + const long MOUSE_BUTTON_MIDDLE_BUTTON = 1; + const long MOUSE_BUTTON_RIGHT_BUTTON = 2; + + // Match WidgetMouseEventBase::buttonsFlag. const long MOUSE_BUTTONS_NO_BUTTON = 0x00; const long MOUSE_BUTTONS_LEFT_BUTTON = 0x01; const long MOUSE_BUTTONS_RIGHT_BUTTON = 0x02; diff --git a/extensions/spellcheck/hunspell/glue/moz.build b/extensions/spellcheck/hunspell/glue/moz.build index 1e1d9711a9a3..42d3d99349d7 100644 --- a/extensions/spellcheck/hunspell/glue/moz.build +++ b/extensions/spellcheck/hunspell/glue/moz.build @@ -4,7 +4,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -SOURCES += [ +UNIFIED_SOURCES += [ 'mozHunspell.cpp', 'mozHunspellDirProvider.cpp', 'RemoteSpellCheckEngineChild.cpp', diff --git a/extensions/spellcheck/hunspell/glue/mozHunspell.cpp b/extensions/spellcheck/hunspell/glue/mozHunspell.cpp index 87ffbc661f26..697d6676b6f9 100644 --- a/extensions/spellcheck/hunspell/glue/mozHunspell.cpp +++ b/extensions/spellcheck/hunspell/glue/mozHunspell.cpp @@ -200,7 +200,7 @@ NS_IMETHODIMP mozHunspell::SetDictionary(const char16_t *aDictionary) if (!mHunspell) return NS_ERROR_OUT_OF_MEMORY; - nsDependentCString label(mHunspell->get_dic_encoding()); + nsAutoCString label(mHunspell->get_dict_encoding().c_str()); nsAutoCString encoding; if (!EncodingUtils::FindEncodingForLabelNoReplacement(label, encoding)) { return NS_ERROR_UCONV_NOCONV; @@ -480,7 +480,8 @@ mozHunspell::LoadDictionariesFromDir(nsIFile* aDir) return NS_OK; } -nsresult mozHunspell::ConvertCharset(const char16_t* aStr, char ** aDst) +nsresult +mozHunspell::ConvertCharset(const char16_t* aStr, std::string* aDst) { NS_ENSURE_ARG_POINTER(aDst); NS_ENSURE_TRUE(mEncoder, NS_ERROR_NULL_POINTER); @@ -490,12 +491,13 @@ nsresult mozHunspell::ConvertCharset(const char16_t* aStr, char ** aDst) nsresult rv = mEncoder->GetMaxLength(aStr, inLength, &outLength); NS_ENSURE_SUCCESS(rv, rv); - *aDst = (char *) moz_xmalloc(sizeof(char) * (outLength+1)); - NS_ENSURE_TRUE(*aDst, NS_ERROR_OUT_OF_MEMORY); + aDst->resize(outLength); - rv = mEncoder->Convert(aStr, &inLength, *aDst, &outLength); - if (NS_SUCCEEDED(rv)) - (*aDst)[outLength] = '\0'; + char* dst = &aDst->operator[](0); + rv = mEncoder->Convert(aStr, &inLength, dst, &outLength); + if (NS_SUCCEEDED(rv)) { + aDst->resize(outLength); + } return rv; } @@ -518,12 +520,11 @@ NS_IMETHODIMP mozHunspell::Check(const char16_t *aWord, bool *aResult) NS_ENSURE_ARG_POINTER(aResult); NS_ENSURE_TRUE(mHunspell, NS_ERROR_FAILURE); - nsXPIDLCString charsetWord; - nsresult rv = ConvertCharset(aWord, getter_Copies(charsetWord)); + std::string charsetWord; + nsresult rv = ConvertCharset(aWord, &charsetWord); NS_ENSURE_SUCCESS(rv, rv); - *aResult = !!mHunspell->spell(charsetWord); - + *aResult = mHunspell->spell(charsetWord); if (!*aResult && mPersonalDictionary) rv = mPersonalDictionary->Check(aWord, mLanguage.get(), aResult); @@ -540,12 +541,12 @@ NS_IMETHODIMP mozHunspell::Suggest(const char16_t *aWord, char16_t ***aSuggestio nsresult rv; *aSuggestionCount = 0; - nsXPIDLCString charsetWord; - rv = ConvertCharset(aWord, getter_Copies(charsetWord)); + std::string charsetWord; + rv = ConvertCharset(aWord, &charsetWord); NS_ENSURE_SUCCESS(rv, rv); - char ** wlst; - *aSuggestionCount = mHunspell->suggest(&wlst, charsetWord); + std::vector suggestions = mHunspell->suggest(charsetWord); + *aSuggestionCount = static_cast(suggestions.size()); if (*aSuggestionCount) { *aSuggestions = (char16_t **)moz_xmalloc(*aSuggestionCount * sizeof(char16_t *)); @@ -553,15 +554,15 @@ NS_IMETHODIMP mozHunspell::Suggest(const char16_t *aWord, char16_t ***aSuggestio uint32_t index = 0; for (index = 0; index < *aSuggestionCount && NS_SUCCEEDED(rv); ++index) { // Convert the suggestion to utf16 - int32_t inLength = strlen(wlst[index]); + int32_t inLength = suggestions[index].size(); int32_t outLength; - rv = mDecoder->GetMaxLength(wlst[index], inLength, &outLength); + rv = mDecoder->GetMaxLength(suggestions[index].c_str(), inLength, &outLength); if (NS_SUCCEEDED(rv)) { (*aSuggestions)[index] = (char16_t *) moz_xmalloc(sizeof(char16_t) * (outLength+1)); if ((*aSuggestions)[index]) { - rv = mDecoder->Convert(wlst[index], &inLength, (*aSuggestions)[index], &outLength); + rv = mDecoder->Convert(suggestions[index].c_str(), &inLength, (*aSuggestions)[index], &outLength); if (NS_SUCCEEDED(rv)) (*aSuggestions)[index][outLength] = 0; } @@ -577,7 +578,6 @@ NS_IMETHODIMP mozHunspell::Suggest(const char16_t *aWord, char16_t ***aSuggestio rv = NS_ERROR_OUT_OF_MEMORY; } - NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(*aSuggestionCount, wlst); return rv; } diff --git a/extensions/spellcheck/hunspell/glue/mozHunspell.h b/extensions/spellcheck/hunspell/glue/mozHunspell.h index 01ef741aacfa..132ba57a58a3 100644 --- a/extensions/spellcheck/hunspell/glue/mozHunspell.h +++ b/extensions/spellcheck/hunspell/glue/mozHunspell.h @@ -99,7 +99,7 @@ public: void LoadDictionaryList(bool aNotifyChildProcesses); // helper method for converting a word to the charset of the dictionary - nsresult ConvertCharset(const char16_t* aStr, char ** aDst); + nsresult ConvertCharset(const char16_t* aStr, std::string* aDst); NS_DECL_NSIMEMORYREPORTER diff --git a/extensions/spellcheck/hunspell/src/README.mozilla b/extensions/spellcheck/hunspell/src/README.mozilla index 79a9f54d1672..b5912876c856 100644 --- a/extensions/spellcheck/hunspell/src/README.mozilla +++ b/extensions/spellcheck/hunspell/src/README.mozilla @@ -1,2 +1,2 @@ -Hunspell Version: 1.4.1 +Hunspell Version: 1.5.4 Additional Patches: See patches directory. diff --git a/extensions/spellcheck/hunspell/src/affentry.cxx b/extensions/spellcheck/hunspell/src/affentry.cxx index bd28274368b8..70b468c0a45f 100644 --- a/extensions/spellcheck/hunspell/src/affentry.cxx +++ b/extensions/spellcheck/hunspell/src/affentry.cxx @@ -79,33 +79,7 @@ #include "affentry.hxx" #include "csutil.hxx" -PfxEntry::PfxEntry(AffixMgr* pmgr, affentry* dp) - // register affix manager - : pmyMgr(pmgr), - next(NULL), - nexteq(NULL), - nextne(NULL), - flgnxt(NULL) { - // set up its initial values - aflag = dp->aflag; // flag - strip = dp->strip; // string to strip - appnd = dp->appnd; // string to append - numconds = dp->numconds; // length of the condition - opts = dp->opts; // cross product flag - // then copy over all of the conditions - if (opts & aeLONGCOND) { - memcpy(c.conds, dp->c.l.conds1, MAXCONDLEN_1); - c.l.conds2 = dp->c.l.conds2; - } else - memcpy(c.conds, dp->c.conds, MAXCONDLEN); - morphcode = dp->morphcode; - contclass = dp->contclass; - contclasslen = dp->contclasslen; -} - -PfxEntry::~PfxEntry() { - aflag = 0; - pmyMgr = NULL; +AffEntry::~AffEntry() { if (opts & aeLONGCOND) free(c.l.conds2); if (morphcode && !(opts & aeALIASM)) @@ -114,17 +88,26 @@ PfxEntry::~PfxEntry() { free(contclass); } +PfxEntry::PfxEntry(AffixMgr* pmgr) + // register affix manager + : pmyMgr(pmgr), + next(NULL), + nexteq(NULL), + nextne(NULL), + flgnxt(NULL) { +} + // add prefix to this word assuming conditions hold -char* PfxEntry::add(const char* word, size_t len) { +std::string PfxEntry::add(const char* word, size_t len) { + std::string result; if ((len > strip.size() || (len == 0 && pmyMgr->get_fullstrip())) && (len >= numconds) && test_condition(word) && (!strip.size() || (strncmp(word, strip.c_str(), strip.size()) == 0))) { /* we have a match so add prefix */ - std::string tword(appnd); - tword.append(word + strip.size()); - return mystrdup(tword.c_str()); + result.assign(appnd); + result.append(word + strip.size()); } - return NULL; + return result; } inline char* PfxEntry::nextchar(char* p) { @@ -276,8 +259,7 @@ struct hentry* PfxEntry::checkword(const char* word, // if ((opts & aeXPRODUCT) && in_compound) { if ((opts & aeXPRODUCT)) { he = pmyMgr->suffix_check(tmpword.c_str(), tmpl, aeXPRODUCT, this, - NULL, 0, NULL, FLAG_NULL, needflag, - in_compound); + FLAG_NULL, needflag, in_compound); if (he) return he; } @@ -291,8 +273,6 @@ struct hentry* PfxEntry::check_twosfx(const char* word, int len, char in_compound, const FLAG needflag) { - struct hentry* he; // hash entry of root word or NULL - // on entry prefix is 0 length or already matches the beginning of the word. // So if the remaining root word has positive length // and if there are enough chars in root word and added back strip chars @@ -324,8 +304,9 @@ struct hentry* PfxEntry::check_twosfx(const char* word, // cross checked combined with a suffix if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) { - he = pmyMgr->suffix_check_twosfx(tmpword.c_str(), tmpl, aeXPRODUCT, this, - needflag); + // hash entry of root word or NULL + struct hentry* he = pmyMgr->suffix_check_twosfx(tmpword.c_str(), tmpl, aeXPRODUCT, this, + needflag); if (he) return he; } @@ -335,15 +316,15 @@ struct hentry* PfxEntry::check_twosfx(const char* word, } // check if this prefix entry matches -char* PfxEntry::check_twosfx_morph(const char* word, - int len, - char in_compound, - const FLAG needflag) { +std::string PfxEntry::check_twosfx_morph(const char* word, + int len, + char in_compound, + const FLAG needflag) { + std::string result; // on entry prefix is 0 length or already matches the beginning of the word. // So if the remaining root word has positive length // and if there are enough chars in root word and added back strip chars // to meet the number of characters conditions, then test it - int tmpl = len - appnd.size(); // length of tmpword if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) && @@ -370,22 +351,21 @@ char* PfxEntry::check_twosfx_morph(const char* word, // ross checked combined with a suffix if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) { - return pmyMgr->suffix_check_twosfx_morph(tmpword.c_str(), tmpl, - aeXPRODUCT, - this, needflag); + result = pmyMgr->suffix_check_twosfx_morph(tmpword.c_str(), tmpl, + aeXPRODUCT, + this, needflag); } } } - return NULL; + return result; } // check if this prefix entry matches -char* PfxEntry::check_morph(const char* word, - int len, - char in_compound, - const FLAG needflag) { - struct hentry* he; // hash entry of root word or NULL - char* st; +std::string PfxEntry::check_morph(const char* word, + int len, + char in_compound, + const FLAG needflag) { + std::string result; // on entry prefix is 0 length or already matches the beginning of the word. // So if the remaining root word has positive length @@ -411,9 +391,8 @@ char* PfxEntry::check_morph(const char* word, // root word in the dictionary if (test_condition(tmpword.c_str())) { - std::string result; - tmpl += strip.size(); + struct hentry* he; // hash entry of root word or NULL if ((he = pmyMgr->lookup(tmpword.c_str())) != NULL) { do { if (TESTAFF(he->astr, aflag, he->alen) && @@ -455,23 +434,19 @@ char* PfxEntry::check_morph(const char* word, // ross checked combined with a suffix if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) { - st = pmyMgr->suffix_check_morph(tmpword.c_str(), tmpl, aeXPRODUCT, this, - FLAG_NULL, needflag); - if (st) { + std::string st = pmyMgr->suffix_check_morph(tmpword.c_str(), tmpl, aeXPRODUCT, this, + FLAG_NULL, needflag); + if (!st.empty()) { result.append(st); - free(st); } } - - if (!result.empty()) - return mystrdup(result.c_str()); } } - return NULL; + return result; } -SfxEntry::SfxEntry(AffixMgr* pmgr, affentry* dp) +SfxEntry::SfxEntry(AffixMgr* pmgr) : pmyMgr(pmgr) // register affix manager , next(NULL), @@ -481,50 +456,21 @@ SfxEntry::SfxEntry(AffixMgr* pmgr, affentry* dp) l_morph(NULL), r_morph(NULL), eq_morph(NULL) { - // set up its initial values - aflag = dp->aflag; // char flag - strip = dp->strip; // string to strip - appnd = dp->appnd; // string to append - numconds = dp->numconds; // length of the condition - opts = dp->opts; // cross product flag - - // then copy over all of the conditions - if (opts & aeLONGCOND) { - memcpy(c.l.conds1, dp->c.l.conds1, MAXCONDLEN_1); - c.l.conds2 = dp->c.l.conds2; - } else - memcpy(c.conds, dp->c.conds, MAXCONDLEN); - rappnd = appnd; - reverseword(rappnd); - morphcode = dp->morphcode; - contclass = dp->contclass; - contclasslen = dp->contclasslen; -} - -SfxEntry::~SfxEntry() { - aflag = 0; - pmyMgr = NULL; - if (opts & aeLONGCOND) - free(c.l.conds2); - if (morphcode && !(opts & aeALIASM)) - free(morphcode); - if (contclass && !(opts & aeALIASF)) - free(contclass); } // add suffix to this word assuming conditions hold -char* SfxEntry::add(const char* word, size_t len) { +std::string SfxEntry::add(const char* word, size_t len) { + std::string result; /* make sure all conditions match */ if ((len > strip.size() || (len == 0 && pmyMgr->get_fullstrip())) && (len >= numconds) && test_condition(word + len, word) && (!strip.size() || (strcmp(word + len - strip.size(), strip.c_str()) == 0))) { - std::string tword(word); + result.assign(word); /* we have a match so add suffix */ - tword.replace(len - strip.size(), std::string::npos, appnd); - return mystrdup(tword.c_str()); + result.replace(len - strip.size(), std::string::npos, appnd); } - return NULL; + return result; } inline char* SfxEntry::nextchar(char* p) { @@ -669,9 +615,6 @@ struct hentry* SfxEntry::checkword(const char* word, int len, int optflags, PfxEntry* ppfx, - char** wlst, - int maxSug, - int* ns, const FLAG cclass, const FLAG needflag, const FLAG badflag) { @@ -742,27 +685,6 @@ struct hentry* SfxEntry::checkword(const char* word, return he; he = he->next_homonym; // check homonyms } while (he); - - // obsolote stemming code (used only by the - // experimental SuffixMgr:suggest_pos_stems) - // store resulting root in wlst - } else if (wlst && (*ns < maxSug)) { - int cwrd = 1; - for (int k = 0; k < *ns; k++) - if (strcmp(tmpword, wlst[k]) == 0) { - cwrd = 0; - break; - } - if (cwrd) { - wlst[*ns] = mystrdup(tmpword); - if (wlst[*ns] == NULL) { - for (int j = 0; j < *ns; j++) - free(wlst[j]); - *ns = -1; - return NULL; - } - (*ns)++; - } } } } @@ -775,7 +697,6 @@ struct hentry* SfxEntry::check_twosfx(const char* word, int optflags, PfxEntry* ppfx, const FLAG needflag) { - struct hentry* he; // hash entry pointer PfxEntry* ep = ppfx; // if this suffix is being cross checked with a prefix @@ -813,17 +734,18 @@ struct hentry* SfxEntry::check_twosfx(const char* word, // if all conditions are met then recall suffix_check if (test_condition(end, beg)) { + struct hentry* he; // hash entry pointer if (ppfx) { // handle conditional suffix if ((contclass) && TESTAFF(contclass, ep->getFlag(), contclasslen)) - he = pmyMgr->suffix_check(tmpword.c_str(), tmpl, 0, NULL, NULL, 0, NULL, - (FLAG)aflag, needflag); + he = pmyMgr->suffix_check(tmpword.c_str(), tmpl, 0, NULL, + (FLAG)aflag, needflag, IN_CPD_NOT); else - he = pmyMgr->suffix_check(tmpword.c_str(), tmpl, optflags, ppfx, NULL, 0, - NULL, (FLAG)aflag, needflag); + he = pmyMgr->suffix_check(tmpword.c_str(), tmpl, optflags, ppfx, + (FLAG)aflag, needflag, IN_CPD_NOT); } else { - he = pmyMgr->suffix_check(tmpword.c_str(), tmpl, 0, NULL, NULL, 0, NULL, - (FLAG)aflag, needflag); + he = pmyMgr->suffix_check(tmpword.c_str(), tmpl, 0, NULL, + (FLAG)aflag, needflag, IN_CPD_NOT); } if (he) return he; @@ -833,23 +755,20 @@ struct hentry* SfxEntry::check_twosfx(const char* word, } // see if two-level suffix is present in the word -char* SfxEntry::check_twosfx_morph(const char* word, - int len, - int optflags, - PfxEntry* ppfx, - const FLAG needflag) { +std::string SfxEntry::check_twosfx_morph(const char* word, + int len, + int optflags, + PfxEntry* ppfx, + const FLAG needflag) { PfxEntry* ep = ppfx; - char* st; - char result[MAXLNLEN]; - - *result = '\0'; + std::string result; // if this suffix is being cross checked with a prefix // but it does not support cross products skip it if ((optflags & aeXPRODUCT) != 0 && (opts & aeXPRODUCT) == 0) - return NULL; + return result; // upon entry suffix is 0 length or already matches the end of the word. // So if the remaining root word has positive length @@ -883,40 +802,34 @@ char* SfxEntry::check_twosfx_morph(const char* word, if (ppfx) { // handle conditional suffix if ((contclass) && TESTAFF(contclass, ep->getFlag(), contclasslen)) { - st = pmyMgr->suffix_check_morph(tmpword.c_str(), tmpl, 0, NULL, aflag, - needflag); - if (st) { + std::string st = pmyMgr->suffix_check_morph(tmpword.c_str(), tmpl, 0, NULL, aflag, + needflag); + if (!st.empty()) { if (ppfx->getMorph()) { - mystrcat(result, ppfx->getMorph(), MAXLNLEN); - mystrcat(result, " ", MAXLNLEN); + result.append(ppfx->getMorph()); + result.append(" "); } - mystrcat(result, st, MAXLNLEN); - free(st); + result.append(st); mychomp(result); } } else { - st = pmyMgr->suffix_check_morph(tmpword.c_str(), tmpl, optflags, ppfx, aflag, - needflag); - if (st) { - mystrcat(result, st, MAXLNLEN); - free(st); + std::string st = pmyMgr->suffix_check_morph(tmpword.c_str(), tmpl, optflags, ppfx, aflag, + needflag); + if (!st.empty()) { + result.append(st); mychomp(result); } } } else { - st = - pmyMgr->suffix_check_morph(tmpword.c_str(), tmpl, 0, NULL, aflag, needflag); - if (st) { - mystrcat(result, st, MAXLNLEN); - free(st); + std::string st = pmyMgr->suffix_check_morph(tmpword.c_str(), tmpl, 0, NULL, aflag, needflag); + if (!st.empty()) { + result.append(st); mychomp(result); } } - if (*result) - return mystrdup(result); } } - return NULL; + return result; } // get next homonym with same affix @@ -948,6 +861,11 @@ struct hentry* SfxEntry::get_next_homonym(struct hentry* he, return NULL; } +void SfxEntry::initReverseWord() { + rappnd = appnd; + reverseword(rappnd); +} + #if 0 Appendix: Understanding Affix Code diff --git a/extensions/spellcheck/hunspell/src/affentry.hxx b/extensions/spellcheck/hunspell/src/affentry.hxx index 6311d83fff95..09240300d625 100644 --- a/extensions/spellcheck/hunspell/src/affentry.hxx +++ b/extensions/spellcheck/hunspell/src/affentry.hxx @@ -71,10 +71,8 @@ * SUCH DAMAGE. */ -#ifndef _AFFIX_HXX_ -#define _AFFIX_HXX_ - -#include "hunvisapi.h" +#ifndef AFFIX_HXX_ +#define AFFIX_HXX_ #include "atypes.hxx" #include "baseaffix.hxx" @@ -82,7 +80,7 @@ /* A Prefix Entry */ -class LIBHUNSPELL_DLL_EXPORTED PfxEntry : protected AffEntry { +class PfxEntry : public AffEntry { private: PfxEntry(const PfxEntry&); PfxEntry& operator=(const PfxEntry&); @@ -96,10 +94,9 @@ class LIBHUNSPELL_DLL_EXPORTED PfxEntry : protected AffEntry { PfxEntry* flgnxt; public: - PfxEntry(AffixMgr* pmgr, affentry* dp); - ~PfxEntry(); + explicit PfxEntry(AffixMgr* pmgr); - inline bool allowCross() { return ((opts & aeXPRODUCT) != 0); } + bool allowCross() const { return ((opts & aeXPRODUCT) != 0); } struct hentry* checkword(const char* word, int len, char in_compound, @@ -110,19 +107,19 @@ class LIBHUNSPELL_DLL_EXPORTED PfxEntry : protected AffEntry { char in_compound, const FLAG needflag = FLAG_NULL); - char* check_morph(const char* word, - int len, - char in_compound, - const FLAG needflag = FLAG_NULL); + std::string check_morph(const char* word, + int len, + char in_compound, + const FLAG needflag = FLAG_NULL); - char* check_twosfx_morph(const char* word, - int len, - char in_compound, - const FLAG needflag = FLAG_NULL); + std::string check_twosfx_morph(const char* word, + int len, + char in_compound, + const FLAG needflag = FLAG_NULL); - inline FLAG getFlag() { return aflag; } - inline const char* getKey() { return appnd.c_str(); } - char* add(const char* word, size_t len); + FLAG getFlag() { return aflag; } + const char* getKey() { return appnd.c_str(); } + std::string add(const char* word, size_t len); inline short getKeyLen() { return appnd.size(); } @@ -147,7 +144,7 @@ class LIBHUNSPELL_DLL_EXPORTED PfxEntry : protected AffEntry { /* A Suffix Entry */ -class LIBHUNSPELL_DLL_EXPORTED SfxEntry : protected AffEntry { +class SfxEntry : public AffEntry { private: SfxEntry(const SfxEntry&); SfxEntry& operator=(const SfxEntry&); @@ -166,20 +163,16 @@ class LIBHUNSPELL_DLL_EXPORTED SfxEntry : protected AffEntry { SfxEntry* eq_morph; public: - SfxEntry(AffixMgr* pmgr, affentry* dp); - ~SfxEntry(); + explicit SfxEntry(AffixMgr* pmgr); - inline bool allowCross() { return ((opts & aeXPRODUCT) != 0); } + bool allowCross() const { return ((opts & aeXPRODUCT) != 0); } struct hentry* checkword(const char* word, int len, int optflags, PfxEntry* ppfx, - char** wlst, - int maxSug, - int* ns, - const FLAG cclass = FLAG_NULL, - const FLAG needflag = FLAG_NULL, - const FLAG badflag = FLAG_NULL); + const FLAG cclass, + const FLAG needflag, + const FLAG badflag); struct hentry* check_twosfx(const char* word, int len, @@ -187,11 +180,11 @@ class LIBHUNSPELL_DLL_EXPORTED SfxEntry : protected AffEntry { PfxEntry* ppfx, const FLAG needflag = FLAG_NULL); - char* check_twosfx_morph(const char* word, - int len, - int optflags, - PfxEntry* ppfx, - const FLAG needflag = FLAG_NULL); + std::string check_twosfx_morph(const char* word, + int len, + int optflags, + PfxEntry* ppfx, + const FLAG needflag = FLAG_NULL); struct hentry* get_next_homonym(struct hentry* he); struct hentry* get_next_homonym(struct hentry* word, int optflags, @@ -199,9 +192,9 @@ class LIBHUNSPELL_DLL_EXPORTED SfxEntry : protected AffEntry { const FLAG cclass, const FLAG needflag); - inline FLAG getFlag() { return aflag; } - inline const char* getKey() { return rappnd.c_str(); } - char* add(const char* word, size_t len); + FLAG getFlag() { return aflag; } + const char* getKey() { return rappnd.c_str(); } + std::string add(const char* word, size_t len); inline const char* getMorph() { return morphcode; } @@ -224,6 +217,7 @@ class LIBHUNSPELL_DLL_EXPORTED SfxEntry : protected AffEntry { inline void setNextNE(SfxEntry* ptr) { nextne = ptr; } inline void setNextEQ(SfxEntry* ptr) { nexteq = ptr; } inline void setFlgNxt(SfxEntry* ptr) { flgnxt = ptr; } + void initReverseWord(); inline char* nextchar(char* p); inline int test_condition(const char* st, const char* begin); diff --git a/extensions/spellcheck/hunspell/src/affixmgr.cxx b/extensions/spellcheck/hunspell/src/affixmgr.cxx index d6bb677982ba..f288fcb8300d 100644 --- a/extensions/spellcheck/hunspell/src/affixmgr.cxx +++ b/extensions/spellcheck/hunspell/src/affixmgr.cxx @@ -88,33 +88,24 @@ #include "csutil.hxx" AffixMgr::AffixMgr(const char* affpath, - HashMgr** ptr, - int* md, - const char* key) { + const std::vector& ptr, + const char* key) + : alldic(ptr) + , pHMgr(ptr[0]) { + // register hash manager and load affix data from aff file - pHMgr = ptr[0]; - alldic = ptr; - maxdic = md; - keystring = NULL; - trystring = NULL; - encoding = NULL; csconv = NULL; utf8 = 0; complexprefixes = 0; - maptable = NULL; - nummap = 0; - breaktable = NULL; - numbreak = -1; - reptable = NULL; - numrep = 0; + parsedmaptable = false; + parsedbreaktable = false; + parsedrep = false; iconvtable = NULL; oconvtable = NULL; - checkcpdtable = NULL; // allow simplified compound forms (see 3rd field of CHECKCOMPOUNDPATTERN) simplifiedcpd = 0; - numcheckcpd = 0; - defcpdtable = NULL; - numdefcpd = 0; + parsedcheckcpd = false; + parseddefcpd = false; phone = NULL; compoundflag = FLAG_NULL; // permits word in compound forms compoundbegin = FLAG_NULL; // may be first word in compound forms @@ -135,25 +126,15 @@ AffixMgr::AffixMgr(const char* affpath, forbiddenword = FORBIDDENWORD; // forbidden word signing flag nosuggest = FLAG_NULL; // don't suggest words signed with NOSUGGEST flag nongramsuggest = FLAG_NULL; - lang = NULL; // language langnum = 0; // language code (see http://l10n.openoffice.org/languages.html) needaffix = FLAG_NULL; // forbidden root, allowed only with suffixes cpdwordmax = -1; // default: unlimited wordcount in compound words cpdmin = -1; // undefined cpdmaxsyllable = 0; // default: unlimited syllablecount in compound words - cpdvowels = NULL; // vowels (for calculating of Hungarian compounding limit, - // O(n) search! XXX) - cpdvowels_utf16 = - NULL; // vowels for UTF-8 encoding (bsearch instead of O(n) search) - cpdvowels_utf16_len = 0; // vowels pfxappnd = NULL; // previous prefix for counting syllables of the prefix BUG sfxappnd = NULL; // previous suffix for counting syllables of the suffix BUG sfxextra = 0; // modifier for syllable count of sfxappnd BUG - cpdsyllablenum = NULL; // syllable count incrementing flag checknum = 0; // checking numbers, and word with numbers - wordchars = NULL; // letters + spec. word characters - ignorechars = NULL; // letters + spec. word characters - version = NULL; // affix and dictionary file version string havecontclass = 0; // flags of possible continuing classes (double affix) // LEMMA_PRESENT: not put root into the morphological output. Lemma presents // in morhological description in dictionary file. It's often combined with @@ -225,83 +206,10 @@ AffixMgr::~AffixMgr() { sStart[j] = NULL; } - if (keystring) - free(keystring); - keystring = NULL; - if (trystring) - free(trystring); - trystring = NULL; - if (encoding) - free(encoding); - encoding = NULL; - if (maptable) { - for (int j = 0; j < nummap; j++) { - for (int k = 0; k < maptable[j].len; k++) { - if (maptable[j].set[k]) - free(maptable[j].set[k]); - } - free(maptable[j].set); - maptable[j].set = NULL; - maptable[j].len = 0; - } - free(maptable); - maptable = NULL; - } - nummap = 0; - if (breaktable) { - for (int j = 0; j < numbreak; j++) { - if (breaktable[j]) - free(breaktable[j]); - breaktable[j] = NULL; - } - free(breaktable); - breaktable = NULL; - } - numbreak = 0; - if (reptable) { - for (int j = 0; j < numrep; j++) { - free(reptable[j].pattern); - free(reptable[j].pattern2); - } - free(reptable); - reptable = NULL; - } - if (iconvtable) - delete iconvtable; - if (oconvtable) - delete oconvtable; - if (phone && phone->rules) { - for (int j = 0; j < phone->num + 1; j++) { - free(phone->rules[j * 2]); - free(phone->rules[j * 2 + 1]); - } - free(phone->rules); - free(phone); - phone = NULL; - } + delete iconvtable; + delete oconvtable; + delete phone; - if (defcpdtable) { - for (int j = 0; j < numdefcpd; j++) { - free(defcpdtable[j].def); - defcpdtable[j].def = NULL; - } - free(defcpdtable); - defcpdtable = NULL; - } - numrep = 0; - if (checkcpdtable) { - for (int j = 0; j < numcheckcpd; j++) { - free(checkcpdtable[j].pattern); - free(checkcpdtable[j].pattern2); - free(checkcpdtable[j].pattern3); - checkcpdtable[j].pattern = NULL; - checkcpdtable[j].pattern2 = NULL; - checkcpdtable[j].pattern3 = NULL; - } - free(checkcpdtable); - checkcpdtable = NULL; - } - numcheckcpd = 0; FREE_FLAG(compoundflag); FREE_FLAG(compoundbegin); FREE_FLAG(compoundmiddle); @@ -321,21 +229,7 @@ AffixMgr::~AffixMgr() { pHMgr = NULL; cpdmin = 0; cpdmaxsyllable = 0; - if (cpdvowels) - free(cpdvowels); - if (cpdvowels_utf16) - free(cpdvowels_utf16); - if (cpdsyllablenum) - free(cpdsyllablenum); free_utf_tbl(); - if (lang) - free(lang); - if (wordchars) - free(wordchars); - if (ignorechars) - free(ignorechars); - if (version) - free(version); checknum = 0; #ifdef MOZILLA_CLIENT delete[] csconv; @@ -352,8 +246,6 @@ void AffixMgr::finishFileMgr(FileMgr* afflst) { // read in aff file and build up prefix and suffix entry objects int AffixMgr::parse_file(const char* affpath, const char* key) { - char* line; // io buffers - char ft; // affix type // checking flag duplication char dupflags[CONTSIZE]; @@ -375,7 +267,8 @@ int AffixMgr::parse_file(const char* affpath, const char* key) { // read in each line ignoring any that do not // start with a known line type indicator - while ((line = afflst->getline()) != NULL) { + std::string line; + while (afflst->getline(line)) { mychomp(line); /* remove byte order mark */ @@ -383,41 +276,38 @@ int AffixMgr::parse_file(const char* affpath, const char* key) { firstline = 0; // Affix file begins with byte order mark: possible incompatibility with // old Hunspell versions - if (strncmp(line, "\xEF\xBB\xBF", 3) == 0) { - memmove(line, line + 3, strlen(line + 3) + 1); + if (line.compare(0, 3, "\xEF\xBB\xBF", 3) == 0) { + line.erase(0, 3); } } /* parse in the keyboard string */ - if (strncmp(line, "KEY", 3) == 0) { - if (parse_string(line, &keystring, afflst->getlinenum())) { + if (line.compare(0, 3, "KEY", 3) == 0) { + if (!parse_string(line, keystring, afflst->getlinenum())) { finishFileMgr(afflst); return 1; } } /* parse in the try string */ - if (strncmp(line, "TRY", 3) == 0) { - if (parse_string(line, &trystring, afflst->getlinenum())) { + if (line.compare(0, 3, "TRY", 3) == 0) { + if (!parse_string(line, trystring, afflst->getlinenum())) { finishFileMgr(afflst); return 1; } } /* parse in the name of the character set used by the .dict and .aff */ - if (strncmp(line, "SET", 3) == 0) { - if (parse_string(line, &encoding, afflst->getlinenum())) { + if (line.compare(0, 3, "SET", 3) == 0) { + if (!parse_string(line, encoding, afflst->getlinenum())) { finishFileMgr(afflst); return 1; } - if (strcmp(encoding, "UTF-8") == 0) { + if (encoding == "UTF-8") { utf8 = 1; #ifndef OPENOFFICEORG #ifndef MOZILLA_CLIENT - if (initialize_utf_tbl()) { - finishFileMgr(afflst); - return 1; - } + initialize_utf_tbl(); #endif #endif } @@ -425,26 +315,26 @@ int AffixMgr::parse_file(const char* affpath, const char* key) { /* parse COMPLEXPREFIXES for agglutinative languages with right-to-left * writing system */ - if (strncmp(line, "COMPLEXPREFIXES", 15) == 0) + if (line.compare(0, 15, "COMPLEXPREFIXES", 15) == 0) complexprefixes = 1; /* parse in the flag used by the controlled compound words */ - if (strncmp(line, "COMPOUNDFLAG", 12) == 0) { - if (parse_flag(line, &compoundflag, afflst)) { + if (line.compare(0, 12, "COMPOUNDFLAG", 12) == 0) { + if (!parse_flag(line, &compoundflag, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the flag used by compound words */ - if (strncmp(line, "COMPOUNDBEGIN", 13) == 0) { + if (line.compare(0, 13, "COMPOUNDBEGIN", 13) == 0) { if (complexprefixes) { - if (parse_flag(line, &compoundend, afflst)) { + if (!parse_flag(line, &compoundend, afflst)) { finishFileMgr(afflst); return 1; } } else { - if (parse_flag(line, &compoundbegin, afflst)) { + if (!parse_flag(line, &compoundbegin, afflst)) { finishFileMgr(afflst); return 1; } @@ -452,21 +342,22 @@ int AffixMgr::parse_file(const char* affpath, const char* key) { } /* parse in the flag used by compound words */ - if (strncmp(line, "COMPOUNDMIDDLE", 14) == 0) { - if (parse_flag(line, &compoundmiddle, afflst)) { + if (line.compare(0, 14, "COMPOUNDMIDDLE", 14) == 0) { + if (!parse_flag(line, &compoundmiddle, afflst)) { finishFileMgr(afflst); return 1; } } + /* parse in the flag used by compound words */ - if (strncmp(line, "COMPOUNDEND", 11) == 0) { + if (line.compare(0, 11, "COMPOUNDEND", 11) == 0) { if (complexprefixes) { - if (parse_flag(line, &compoundbegin, afflst)) { + if (!parse_flag(line, &compoundbegin, afflst)) { finishFileMgr(afflst); return 1; } } else { - if (parse_flag(line, &compoundend, afflst)) { + if (!parse_flag(line, &compoundend, afflst)) { finishFileMgr(afflst); return 1; } @@ -474,126 +365,126 @@ int AffixMgr::parse_file(const char* affpath, const char* key) { } /* parse in the data used by compound_check() method */ - if (strncmp(line, "COMPOUNDWORDMAX", 15) == 0) { - if (parse_num(line, &cpdwordmax, afflst)) { + if (line.compare(0, 15, "COMPOUNDWORDMAX", 15) == 0) { + if (!parse_num(line, &cpdwordmax, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the flag sign compounds in dictionary */ - if (strncmp(line, "COMPOUNDROOT", 12) == 0) { - if (parse_flag(line, &compoundroot, afflst)) { + if (line.compare(0, 12, "COMPOUNDROOT", 12) == 0) { + if (!parse_flag(line, &compoundroot, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the flag used by compound_check() method */ - if (strncmp(line, "COMPOUNDPERMITFLAG", 18) == 0) { - if (parse_flag(line, &compoundpermitflag, afflst)) { + if (line.compare(0, 18, "COMPOUNDPERMITFLAG", 18) == 0) { + if (!parse_flag(line, &compoundpermitflag, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the flag used by compound_check() method */ - if (strncmp(line, "COMPOUNDFORBIDFLAG", 18) == 0) { - if (parse_flag(line, &compoundforbidflag, afflst)) { + if (line.compare(0, 18, "COMPOUNDFORBIDFLAG", 18) == 0) { + if (!parse_flag(line, &compoundforbidflag, afflst)) { finishFileMgr(afflst); return 1; } } - if (strncmp(line, "COMPOUNDMORESUFFIXES", 20) == 0) { + if (line.compare(0, 20, "COMPOUNDMORESUFFIXES", 20) == 0) { compoundmoresuffixes = 1; } - if (strncmp(line, "CHECKCOMPOUNDDUP", 16) == 0) { + if (line.compare(0, 16, "CHECKCOMPOUNDDUP", 16) == 0) { checkcompounddup = 1; } - if (strncmp(line, "CHECKCOMPOUNDREP", 16) == 0) { + if (line.compare(0, 16, "CHECKCOMPOUNDREP", 16) == 0) { checkcompoundrep = 1; } - if (strncmp(line, "CHECKCOMPOUNDTRIPLE", 19) == 0) { + if (line.compare(0, 19, "CHECKCOMPOUNDTRIPLE", 19) == 0) { checkcompoundtriple = 1; } - if (strncmp(line, "SIMPLIFIEDTRIPLE", 16) == 0) { + if (line.compare(0, 16, "SIMPLIFIEDTRIPLE", 16) == 0) { simplifiedtriple = 1; } - if (strncmp(line, "CHECKCOMPOUNDCASE", 17) == 0) { + if (line.compare(0, 17, "CHECKCOMPOUNDCASE", 17) == 0) { checkcompoundcase = 1; } - if (strncmp(line, "NOSUGGEST", 9) == 0) { - if (parse_flag(line, &nosuggest, afflst)) { + if (line.compare(0, 9, "NOSUGGEST", 9) == 0) { + if (!parse_flag(line, &nosuggest, afflst)) { finishFileMgr(afflst); return 1; } } - if (strncmp(line, "NONGRAMSUGGEST", 14) == 0) { - if (parse_flag(line, &nongramsuggest, afflst)) { + if (line.compare(0, 14, "NONGRAMSUGGEST", 14) == 0) { + if (!parse_flag(line, &nongramsuggest, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the flag used by forbidden words */ - if (strncmp(line, "FORBIDDENWORD", 13) == 0) { - if (parse_flag(line, &forbiddenword, afflst)) { + if (line.compare(0, 13, "FORBIDDENWORD", 13) == 0) { + if (!parse_flag(line, &forbiddenword, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the flag used by forbidden words */ - if (strncmp(line, "LEMMA_PRESENT", 13) == 0) { - if (parse_flag(line, &lemma_present, afflst)) { + if (line.compare(0, 13, "LEMMA_PRESENT", 13) == 0) { + if (!parse_flag(line, &lemma_present, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the flag used by circumfixes */ - if (strncmp(line, "CIRCUMFIX", 9) == 0) { - if (parse_flag(line, &circumfix, afflst)) { + if (line.compare(0, 9, "CIRCUMFIX", 9) == 0) { + if (!parse_flag(line, &circumfix, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the flag used by fogemorphemes */ - if (strncmp(line, "ONLYINCOMPOUND", 14) == 0) { - if (parse_flag(line, &onlyincompound, afflst)) { + if (line.compare(0, 14, "ONLYINCOMPOUND", 14) == 0) { + if (!parse_flag(line, &onlyincompound, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the flag used by `needaffixs' */ - if (strncmp(line, "PSEUDOROOT", 10) == 0) { - if (parse_flag(line, &needaffix, afflst)) { + if (line.compare(0, 10, "PSEUDOROOT", 10) == 0) { + if (!parse_flag(line, &needaffix, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the flag used by `needaffixs' */ - if (strncmp(line, "NEEDAFFIX", 9) == 0) { - if (parse_flag(line, &needaffix, afflst)) { + if (line.compare(0, 9, "NEEDAFFIX", 9) == 0) { + if (!parse_flag(line, &needaffix, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the minimal length for words in compounds */ - if (strncmp(line, "COMPOUNDMIN", 11) == 0) { - if (parse_num(line, &cpdmin, afflst)) { + if (line.compare(0, 11, "COMPOUNDMIN", 11) == 0) { + if (!parse_num(line, &cpdmin, afflst)) { finishFileMgr(afflst); return 1; } @@ -602,29 +493,29 @@ int AffixMgr::parse_file(const char* affpath, const char* key) { } /* parse in the max. words and syllables in compounds */ - if (strncmp(line, "COMPOUNDSYLLABLE", 16) == 0) { - if (parse_cpdsyllable(line, afflst)) { + if (line.compare(0, 16, "COMPOUNDSYLLABLE", 16) == 0) { + if (!parse_cpdsyllable(line, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the flag used by compound_check() method */ - if (strncmp(line, "SYLLABLENUM", 11) == 0) { - if (parse_string(line, &cpdsyllablenum, afflst->getlinenum())) { + if (line.compare(0, 11, "SYLLABLENUM", 11) == 0) { + if (!parse_string(line, cpdsyllablenum, afflst->getlinenum())) { finishFileMgr(afflst); return 1; } } /* parse in the flag used by the controlled compound words */ - if (strncmp(line, "CHECKNUM", 8) == 0) { + if (line.compare(0, 8, "CHECKNUM", 8) == 0) { checknum = 1; } /* parse in the extra word characters */ - if (strncmp(line, "WORDCHARS", 9) == 0) { - if (!parse_array(line, &wordchars, wordchars_utf16, + if (line.compare(0, 9, "WORDCHARS", 9) == 0) { + if (!parse_array(line, wordchars, wordchars_utf16, utf8, afflst->getlinenum())) { finishFileMgr(afflst); return 1; @@ -633,8 +524,8 @@ int AffixMgr::parse_file(const char* affpath, const char* key) { /* parse in the ignored characters (for example, Arabic optional diacretics * charachters */ - if (strncmp(line, "IGNORE", 6) == 0) { - if (!parse_array(line, &ignorechars, ignorechars_utf16, + if (line.compare(0, 6, "IGNORE", 6) == 0) { + if (!parse_array(line, ignorechars, ignorechars_utf16, utf8, afflst->getlinenum())) { finishFileMgr(afflst); return 1; @@ -642,172 +533,174 @@ int AffixMgr::parse_file(const char* affpath, const char* key) { } /* parse in the typical fault correcting table */ - if (strncmp(line, "REP", 3) == 0) { - if (parse_reptable(line, afflst)) { + if (line.compare(0, 3, "REP", 3) == 0) { + if (!parse_reptable(line, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the input conversion table */ - if (strncmp(line, "ICONV", 5) == 0) { - if (parse_convtable(line, afflst, &iconvtable, "ICONV")) { + if (line.compare(0, 5, "ICONV", 5) == 0) { + if (!parse_convtable(line, afflst, &iconvtable, "ICONV")) { finishFileMgr(afflst); return 1; } } /* parse in the input conversion table */ - if (strncmp(line, "OCONV", 5) == 0) { - if (parse_convtable(line, afflst, &oconvtable, "OCONV")) { + if (line.compare(0, 5, "OCONV", 5) == 0) { + if (!parse_convtable(line, afflst, &oconvtable, "OCONV")) { finishFileMgr(afflst); return 1; } } /* parse in the phonetic translation table */ - if (strncmp(line, "PHONE", 5) == 0) { - if (parse_phonetable(line, afflst)) { + if (line.compare(0, 5, "PHONE", 5) == 0) { + if (!parse_phonetable(line, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the checkcompoundpattern table */ - if (strncmp(line, "CHECKCOMPOUNDPATTERN", 20) == 0) { - if (parse_checkcpdtable(line, afflst)) { + if (line.compare(0, 20, "CHECKCOMPOUNDPATTERN", 20) == 0) { + if (!parse_checkcpdtable(line, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the defcompound table */ - if (strncmp(line, "COMPOUNDRULE", 12) == 0) { - if (parse_defcpdtable(line, afflst)) { + if (line.compare(0, 12, "COMPOUNDRULE", 12) == 0) { + if (!parse_defcpdtable(line, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the related character map table */ - if (strncmp(line, "MAP", 3) == 0) { - if (parse_maptable(line, afflst)) { + if (line.compare(0, 3, "MAP", 3) == 0) { + if (!parse_maptable(line, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the word breakpoints table */ - if (strncmp(line, "BREAK", 5) == 0) { - if (parse_breaktable(line, afflst)) { + if (line.compare(0, 5, "BREAK", 5) == 0) { + if (!parse_breaktable(line, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the language for language specific codes */ - if (strncmp(line, "LANG", 4) == 0) { - if (parse_string(line, &lang, afflst->getlinenum())) { + if (line.compare(0, 4, "LANG", 4) == 0) { + if (!parse_string(line, lang, afflst->getlinenum())) { finishFileMgr(afflst); return 1; } langnum = get_lang_num(lang); } - if (strncmp(line, "VERSION", 7) == 0) { - for (line = line + 7; *line == ' ' || *line == '\t'; line++) - ; - version = mystrdup(line); + if (line.compare(0, 7, "VERSION", 7) == 0) { + size_t startpos = line.find_first_not_of(" \t", 7); + if (startpos != std::string::npos) { + version = line.substr(startpos); + } } - if (strncmp(line, "MAXNGRAMSUGS", 12) == 0) { - if (parse_num(line, &maxngramsugs, afflst)) { + if (line.compare(0, 12, "MAXNGRAMSUGS", 12) == 0) { + if (!parse_num(line, &maxngramsugs, afflst)) { finishFileMgr(afflst); return 1; } } - if (strncmp(line, "ONLYMAXDIFF", 11) == 0) + if (line.compare(0, 11, "ONLYMAXDIFF", 11) == 0) onlymaxdiff = 1; - if (strncmp(line, "MAXDIFF", 7) == 0) { - if (parse_num(line, &maxdiff, afflst)) { + if (line.compare(0, 7, "MAXDIFF", 7) == 0) { + if (!parse_num(line, &maxdiff, afflst)) { finishFileMgr(afflst); return 1; } } - if (strncmp(line, "MAXCPDSUGS", 10) == 0) { - if (parse_num(line, &maxcpdsugs, afflst)) { + if (line.compare(0, 10, "MAXCPDSUGS", 10) == 0) { + if (!parse_num(line, &maxcpdsugs, afflst)) { finishFileMgr(afflst); return 1; } } - if (strncmp(line, "NOSPLITSUGS", 11) == 0) { + if (line.compare(0, 11, "NOSPLITSUGS", 11) == 0) { nosplitsugs = 1; } - if (strncmp(line, "FULLSTRIP", 9) == 0) { + if (line.compare(0, 9, "FULLSTRIP", 9) == 0) { fullstrip = 1; } - if (strncmp(line, "SUGSWITHDOTS", 12) == 0) { + if (line.compare(0, 12, "SUGSWITHDOTS", 12) == 0) { sugswithdots = 1; } /* parse in the flag used by forbidden words */ - if (strncmp(line, "KEEPCASE", 8) == 0) { - if (parse_flag(line, &keepcase, afflst)) { + if (line.compare(0, 8, "KEEPCASE", 8) == 0) { + if (!parse_flag(line, &keepcase, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the flag used by `forceucase' */ - if (strncmp(line, "FORCEUCASE", 10) == 0) { - if (parse_flag(line, &forceucase, afflst)) { + if (line.compare(0, 10, "FORCEUCASE", 10) == 0) { + if (!parse_flag(line, &forceucase, afflst)) { finishFileMgr(afflst); return 1; } } /* parse in the flag used by `warn' */ - if (strncmp(line, "WARN", 4) == 0) { - if (parse_flag(line, &warn, afflst)) { + if (line.compare(0, 4, "WARN", 4) == 0) { + if (!parse_flag(line, &warn, afflst)) { finishFileMgr(afflst); return 1; } } - if (strncmp(line, "FORBIDWARN", 10) == 0) { + if (line.compare(0, 10, "FORBIDWARN", 10) == 0) { forbidwarn = 1; } /* parse in the flag used by the affix generator */ - if (strncmp(line, "SUBSTANDARD", 11) == 0) { - if (parse_flag(line, &substandard, afflst)) { + if (line.compare(0, 11, "SUBSTANDARD", 11) == 0) { + if (!parse_flag(line, &substandard, afflst)) { finishFileMgr(afflst); return 1; } } - if (strncmp(line, "CHECKSHARPS", 11) == 0) { + if (line.compare(0, 11, "CHECKSHARPS", 11) == 0) { checksharps = 1; } /* parse this affix: P - prefix, S - suffix */ - ft = ' '; - if (strncmp(line, "PFX", 3) == 0) + // affix type + char ft = ' '; + if (line.compare(0, 3, "PFX", 3) == 0) ft = complexprefixes ? 'S' : 'P'; - if (strncmp(line, "SFX", 3) == 0) + if (line.compare(0, 3, "SFX", 3) == 0) ft = complexprefixes ? 'P' : 'S'; if (ft != ' ') { if (dupflags_ini) { memset(dupflags, 0, sizeof(dupflags)); dupflags_ini = 0; } - if (parse_affix(line, ft, afflst, dupflags)) { + if (!parse_affix(line, ft, afflst, dupflags)) { finishFileMgr(afflst); return 1; } @@ -848,37 +741,22 @@ int AffixMgr::parse_file(const char* affpath, const char* key) { /* get encoding for CHECKCOMPOUNDCASE */ if (!utf8) { - char* enc = get_encoding(); - csconv = get_current_cs(enc); - free(enc); - enc = NULL; - - std::string expw; - if (wordchars) { - expw.assign(wordchars); - free(wordchars); - } - + csconv = get_current_cs(get_encoding()); for (int i = 0; i <= 255; i++) { if ((csconv[i].cupper != csconv[i].clower) && - (expw.find((char)i) == std::string::npos)) { - expw.push_back((char)i); + (wordchars.find((char)i) == std::string::npos)) { + wordchars.push_back((char)i); } } - wordchars = mystrdup(expw.c_str()); } // default BREAK definition - if (numbreak == -1) { - breaktable = (char**)malloc(sizeof(char*) * 3); - if (!breaktable) - return 1; - breaktable[0] = mystrdup("-"); - breaktable[1] = mystrdup("^-"); - breaktable[2] = mystrdup("-$"); - if (breaktable[0] && breaktable[1] && breaktable[2]) - numbreak = 3; + if (!parsedbreaktable) { + breaktable.push_back("-"); + breaktable.push_back("^-"); + breaktable.push_back("-$"); + parsedbreaktable = true; } return 0; } @@ -949,6 +827,9 @@ int AffixMgr::build_pfxtree(PfxEntry* pfxptr) { // both by suffix flag, and sorted by the reverse of the // suffix string itself; so we need to set up two indexes int AffixMgr::build_sfxtree(SfxEntry* sfxptr) { + + sfxptr->initReverseWord(); + SfxEntry* ptr; SfxEntry* pptr; SfxEntry* ep = sfxptr; @@ -1142,17 +1023,6 @@ int AffixMgr::process_sfx_order() { return 0; } -// add flags to the result for dictionary debugging -void AffixMgr::debugflag(char* result, unsigned short flag) { - char* st = encode_flag(flag); - mystrcat(result, " ", MAXLNLEN); - mystrcat(result, MORPH_FLAG, MAXLNLEN); - if (st) { - mystrcat(result, st, MAXLNLEN); - free(st); - } -} - // add flags to the result for dictionary debugging std::string& AffixMgr::debugflag(std::string& result, unsigned short flag) { char* st = encode_flag(flag); @@ -1181,13 +1051,18 @@ int AffixMgr::condlen(const char* st) { return l; } -int AffixMgr::encodeit(affentry& entry, const char* cs) { +int AffixMgr::encodeit(AffEntry& entry, const char* cs) { if (strcmp(cs, ".") != 0) { entry.numconds = (char)condlen(cs); - // coverity[buffer_size_warning] - deliberate use of lack of end of conds - // padded by strncpy as long condition flag - strncpy(entry.c.conds, cs, MAXCONDLEN); - if (entry.c.conds[MAXCONDLEN - 1] && cs[MAXCONDLEN]) { + const size_t cslen = strlen(cs); + const size_t short_part = std::min(MAXCONDLEN, cslen); + memcpy(entry.c.conds, cs, short_part); + if (short_part < MAXCONDLEN) { + //blank out the remaining space + memset(entry.c.conds + short_part, 0, MAXCONDLEN - short_part); + } else if (cs[MAXCONDLEN]) { + //there is more conditions than fit in fixed space, so its + //a long condition entry.opts += aeLONGCOND; entry.c.l.conds2 = mystrdup(cs + MAXCONDLEN_1); if (!entry.c.l.conds2) @@ -1316,13 +1191,12 @@ struct hentry* AffixMgr::prefix_check_twosfx(const char* word, } // check word for prefixes -char* AffixMgr::prefix_check_morph(const char* word, - int len, - char in_compound, - const FLAG needflag) { +std::string AffixMgr::prefix_check_morph(const char* word, + int len, + char in_compound, + const FLAG needflag) { - char result[MAXLNLEN]; - result[0] = '\0'; + std::string result; pfx = NULL; sfxappnd = NULL; @@ -1331,12 +1205,10 @@ char* AffixMgr::prefix_check_morph(const char* word, // first handle the special case of 0 length prefixes PfxEntry* pe = pStart[0]; while (pe) { - char* st = pe->check_morph(word, len, in_compound, needflag); - if (st) { - mystrcat(result, st, MAXLNLEN); - free(st); + std::string st = pe->check_morph(word, len, in_compound, needflag); + if (!st.empty()) { + result.append(st); } - // if (rv) return rv; pe = pe->getNext(); } @@ -1346,16 +1218,15 @@ char* AffixMgr::prefix_check_morph(const char* word, while (pptr) { if (isSubset(pptr->getKey(), word)) { - char* st = pptr->check_morph(word, len, in_compound, needflag); - if (st) { + std::string st = pptr->check_morph(word, len, in_compound, needflag); + if (!st.empty()) { // fogemorpheme if ((in_compound != IN_CPD_NOT) || !((pptr->getCont() && (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen()))))) { - mystrcat(result, st, MAXLNLEN); + result.append(st); pfx = pptr; } - free(st); } pptr = pptr->getNextEQ(); } else { @@ -1363,18 +1234,15 @@ char* AffixMgr::prefix_check_morph(const char* word, } } - if (*result) - return mystrdup(result); - return NULL; + return result; } // check word for prefixes -char* AffixMgr::prefix_check_twosfx_morph(const char* word, - int len, - char in_compound, - const FLAG needflag) { - char result[MAXLNLEN]; - result[0] = '\0'; +std::string AffixMgr::prefix_check_twosfx_morph(const char* word, + int len, + char in_compound, + const FLAG needflag) { + std::string result; pfx = NULL; sfxappnd = NULL; @@ -1383,10 +1251,9 @@ char* AffixMgr::prefix_check_twosfx_morph(const char* word, // first handle the special case of 0 length prefixes PfxEntry* pe = pStart[0]; while (pe) { - char* st = pe->check_twosfx_morph(word, len, in_compound, needflag); - if (st) { - mystrcat(result, st, MAXLNLEN); - free(st); + std::string st = pe->check_twosfx_morph(word, len, in_compound, needflag); + if (!st.empty()) { + result.append(st); } pe = pe->getNext(); } @@ -1397,10 +1264,9 @@ char* AffixMgr::prefix_check_twosfx_morph(const char* word, while (pptr) { if (isSubset(pptr->getKey(), word)) { - char* st = pptr->check_twosfx_morph(word, len, in_compound, needflag); - if (st) { - mystrcat(result, st, MAXLNLEN); - free(st); + std::string st = pptr->check_twosfx_morph(word, len, in_compound, needflag); + if (!st.empty()) { + result.append(st); pfx = pptr; } pptr = pptr->getNextEQ(); @@ -1409,29 +1275,31 @@ char* AffixMgr::prefix_check_twosfx_morph(const char* word, } } - if (*result) - return mystrdup(result); - return NULL; + return result; } // Is word a non compound with a REP substitution (see checkcompoundrep)? int AffixMgr::cpdrep_check(const char* word, int wl) { - if ((wl < 2) || !numrep) + if ((wl < 2) || reptable.empty()) return 0; - for (int i = 0; i < numrep; i++) { + for (size_t i = 0; i < reptable.size(); ++i) { const char* r = word; - int lenp = strlen(reptable[i].pattern); + const size_t lenp = reptable[i].pattern.size(); // search every occurence of the pattern in the word - while ((r = strstr(r, reptable[i].pattern)) != NULL) { + while ((r = strstr(r, reptable[i].pattern.c_str())) != NULL) { std::string candidate(word); - candidate.replace(r - word, lenp, reptable[i].pattern2); + size_t type = r == word ? 1 : 0; + if (r - word + reptable[i].pattern.size() == lenp) + type += 2; + candidate.replace(r - word, lenp, reptable[i].outstrings[type]); if (candidate_check(candidate.c_str(), candidate.size())) return 1; - r++; // search for the next letter + ++r; // search for the next letter } } + return 0; } @@ -1441,21 +1309,21 @@ int AffixMgr::cpdpat_check(const char* word, hentry* r1, hentry* r2, const char /*affixed*/) { - int len; - for (int i = 0; i < numcheckcpd; i++) { - if (isSubset(checkcpdtable[i].pattern2, word + pos) && + for (size_t i = 0; i < checkcpdtable.size(); ++i) { + size_t len; + if (isSubset(checkcpdtable[i].pattern2.c_str(), word + pos) && (!r1 || !checkcpdtable[i].cond || (r1->astr && TESTAFF(r1->astr, checkcpdtable[i].cond, r1->alen))) && (!r2 || !checkcpdtable[i].cond2 || (r2->astr && TESTAFF(r2->astr, checkcpdtable[i].cond2, r2->alen))) && // zero length pattern => only TESTAFF // zero pattern (0/flag) => unmodified stem (zero affixes allowed) - (!*(checkcpdtable[i].pattern) || - ((*(checkcpdtable[i].pattern) == '0' && r1->blen <= pos && + (checkcpdtable[i].pattern.empty() || + ((checkcpdtable[i].pattern[0] == '0' && r1->blen <= pos && strncmp(word + pos - r1->blen, r1->word, r1->blen) == 0) || - (*(checkcpdtable[i].pattern) != '0' && - ((len = strlen(checkcpdtable[i].pattern)) != 0) && - strncmp(word + pos - len, checkcpdtable[i].pattern, len) == 0)))) { + (checkcpdtable[i].pattern[0] != '0' && + ((len = checkcpdtable[i].pattern.size()) != 0) && + strncmp(word + pos - len, checkcpdtable[i].pattern.c_str(), len) == 0)))) { return 1; } } @@ -1513,7 +1381,6 @@ int AffixMgr::defcpd_check(hentry*** words, std::vector btinfo(1); short bt = 0; - int i, j; (*words)[wnum] = rv; @@ -1525,10 +1392,10 @@ int AffixMgr::defcpd_check(hentry*** words, return 0; } int ok = 0; - for (i = 0; i < numdefcpd; i++) { - for (j = 0; j < defcpdtable[i].len; j++) { - if (defcpdtable[i].def[j] != '*' && defcpdtable[i].def[j] != '?' && - TESTAFF(rv->astr, defcpdtable[i].def[j], rv->alen)) { + for (size_t i = 0; i < defcpdtable.size(); ++i) { + for (size_t j = 0; j < defcpdtable[i].size(); ++j) { + if (defcpdtable[i][j] != '*' && defcpdtable[i][j] != '?' && + TESTAFF(rv->astr, defcpdtable[i][j], rv->alen)) { ok = 1; break; } @@ -1541,25 +1408,25 @@ int AffixMgr::defcpd_check(hentry*** words, return 0; } - for (i = 0; i < numdefcpd; i++) { - signed short pp = 0; // pattern position + for (size_t i = 0; i < defcpdtable.size(); ++i) { + size_t pp = 0; // pattern position signed short wp = 0; // "words" position int ok2; ok = 1; ok2 = 1; do { - while ((pp < defcpdtable[i].len) && (wp <= wnum)) { - if (((pp + 1) < defcpdtable[i].len) && - ((defcpdtable[i].def[pp + 1] == '*') || - (defcpdtable[i].def[pp + 1] == '?'))) { - int wend = (defcpdtable[i].def[pp + 1] == '?') ? wp : wnum; + while ((pp < defcpdtable[i].size()) && (wp <= wnum)) { + if (((pp + 1) < defcpdtable[i].size()) && + ((defcpdtable[i][pp + 1] == '*') || + (defcpdtable[i][pp + 1] == '?'))) { + int wend = (defcpdtable[i][pp + 1] == '?') ? wp : wnum; ok2 = 1; pp += 2; btinfo[bt].btpp = pp; btinfo[bt].btwp = wp; while (wp <= wend) { if (!(*words)[wp]->alen || - !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp - 2], + !TESTAFF((*words)[wp]->astr, defcpdtable[i][pp - 2], (*words)[wp]->alen)) { ok2 = 0; break; @@ -1578,24 +1445,24 @@ int AffixMgr::defcpd_check(hentry*** words, } else { ok2 = 1; if (!(*words)[wp] || !(*words)[wp]->alen || - !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp], + !TESTAFF((*words)[wp]->astr, defcpdtable[i][pp], (*words)[wp]->alen)) { ok = 0; break; } pp++; wp++; - if ((defcpdtable[i].len == pp) && !(wp > wnum)) + if ((defcpdtable[i].size() == pp) && !(wp > wnum)) ok = 0; } } if (ok && ok2) { - int r = pp; - while ((defcpdtable[i].len > r) && ((r + 1) < defcpdtable[i].len) && - ((defcpdtable[i].def[r + 1] == '*') || - (defcpdtable[i].def[r + 1] == '?'))) + size_t r = pp; + while ((defcpdtable[i].size() > r) && ((r + 1) < defcpdtable[i].size()) && + ((defcpdtable[i][r + 1] == '*') || + (defcpdtable[i][r + 1] == '?'))) r += 2; - if (defcpdtable[i].len <= r) + if (defcpdtable[i].size() <= r) return 1; } // backtrack @@ -1608,16 +1475,16 @@ int AffixMgr::defcpd_check(hentry*** words, } while ((btinfo[bt - 1].btnum < 0) && --bt); } while (bt); - if (ok && ok2 && (!all || (defcpdtable[i].len <= pp))) + if (ok && ok2 && (!all || (defcpdtable[i].size() <= pp))) return 1; // check zero ending - while (ok && ok2 && (defcpdtable[i].len > pp) && - ((pp + 1) < defcpdtable[i].len) && - ((defcpdtable[i].def[pp + 1] == '*') || - (defcpdtable[i].def[pp + 1] == '?'))) + while (ok && ok2 && (defcpdtable[i].size() > pp) && + ((pp + 1) < defcpdtable[i].size()) && + ((defcpdtable[i][pp + 1] == '*') || + (defcpdtable[i][pp + 1] == '?'))) pp += 2; - if (ok && ok2 && (defcpdtable[i].len <= pp)) + if (ok && ok2 && (defcpdtable[i].size() <= pp)) return 1; } (*words)[wnum] = NULL; @@ -1651,20 +1518,23 @@ short AffixMgr::get_syllable(const std::string& word) { if (!utf8) { for (size_t i = 0; i < word.size(); ++i) { - if (strchr(cpdvowels, word[i])) - num++; + if (std::binary_search(cpdvowels.begin(), cpdvowels.end(), + word[i])) { + ++num; + } } - } else if (cpdvowels_utf16) { + } else if (!cpdvowels_utf16.empty()) { std::vector w; - int i = u8_u16(w, word); - for (; i > 0; i--) { - if (std::binary_search(cpdvowels_utf16, - cpdvowels_utf16 + cpdvowels_utf16_len, - w[i - 1])) { + u8_u16(w, word); + for (size_t i = 0; i < w.size(); ++i) { + if (std::binary_search(cpdvowels_utf16.begin(), + cpdvowels_utf16.end(), + w[i])) { ++num; } } } + return num; } @@ -1687,8 +1557,7 @@ void AffixMgr::setcminmax(int* cmin, int* cmax, const char* word, int len) { // check if compound word is correctly spelled // hu_mov_rule = spec. Hungarian rule (XXX) -struct hentry* AffixMgr::compound_check(const char* word, - int len, +struct hentry* AffixMgr::compound_check(const std::string& word, short wordnum, short numsyllable, short maxwordnum, @@ -1707,19 +1576,19 @@ struct hentry* AffixMgr::compound_check(const char* word, int cmin; int cmax; int striple = 0; - int scpd = 0; + size_t scpd = 0; int soldi = 0; int oldcmin = 0; int oldcmax = 0; int oldlen = 0; int checkedstriple = 0; - int onlycpdrule; char affixed = 0; hentry** oldwords = words; + size_t len = word.size(); int checked_prefix; - setcminmax(&cmin, &cmax, word, len); + setcminmax(&cmin, &cmax, word.c_str(), len); st.assign(word); @@ -1733,7 +1602,7 @@ struct hentry* AffixMgr::compound_check(const char* word, } words = oldwords; - onlycpdrule = (words) ? 1 : 0; + int onlycpdrule = (words) ? 1 : 0; do { // onlycpdrule loop @@ -1744,26 +1613,26 @@ struct hentry* AffixMgr::compound_check(const char* word, do { // simplified checkcompoundpattern loop if (scpd > 0) { - for (; scpd <= numcheckcpd && - (!checkcpdtable[scpd - 1].pattern3 || - strncmp(word + i, checkcpdtable[scpd - 1].pattern3, - strlen(checkcpdtable[scpd - 1].pattern3)) != 0); + for (; scpd <= checkcpdtable.size() && + (checkcpdtable[scpd - 1].pattern3.empty() || + strncmp(word.c_str() + i, checkcpdtable[scpd - 1].pattern3.c_str(), + checkcpdtable[scpd - 1].pattern3.size()) != 0); scpd++) ; - if (scpd > numcheckcpd) + if (scpd > checkcpdtable.size()) break; // break simplified checkcompoundpattern loop st.replace(i, std::string::npos, checkcpdtable[scpd - 1].pattern); soldi = i; - i += strlen(checkcpdtable[scpd - 1].pattern); + i += checkcpdtable[scpd - 1].pattern.size(); st.replace(i, std::string::npos, checkcpdtable[scpd - 1].pattern2); - st.replace(i + strlen(checkcpdtable[scpd - 1].pattern2), std::string::npos, - word + soldi + strlen(checkcpdtable[scpd - 1].pattern3)); + st.replace(i + checkcpdtable[scpd - 1].pattern2.size(), std::string::npos, + word.substr(soldi + checkcpdtable[scpd - 1].pattern3.size())); oldlen = len; - len += strlen(checkcpdtable[scpd - 1].pattern) + - strlen(checkcpdtable[scpd - 1].pattern2) - - strlen(checkcpdtable[scpd - 1].pattern3); + len += checkcpdtable[scpd - 1].pattern.size() + + checkcpdtable[scpd - 1].pattern2.size() - + checkcpdtable[scpd - 1].pattern3.size(); oldcmin = cmin; oldcmax = cmax; setcminmax(&cmin, &cmax, st.c_str(), len); @@ -1791,7 +1660,7 @@ struct hentry* AffixMgr::compound_check(const char* word, TESTAFF(rv->astr, compoundbegin, rv->alen)) || (compoundmiddle && wordnum && !words && !onlycpdrule && TESTAFF(rv->astr, compoundmiddle, rv->alen)) || - (numdefcpd && onlycpdrule && + (!defcpdtable.empty() && onlycpdrule && ((!words && !wordnum && defcpd_check(&words, wnum, rv, rwords, 0)) || (words && @@ -1812,7 +1681,7 @@ struct hentry* AffixMgr::compound_check(const char* word, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) { if (((rv = suffix_check( - st.c_str(), i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundflag, + st.c_str(), i, 0, NULL, FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || (compoundmoresuffixes && (rv = suffix_check_twosfx(st.c_str(), i, 0, NULL, compoundflag)))) && @@ -1829,7 +1698,7 @@ struct hentry* AffixMgr::compound_check(const char* word, if (rv || (((wordnum == 0) && compoundbegin && ((rv = suffix_check( - st.c_str(), i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, + st.c_str(), i, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || (compoundmoresuffixes && (rv = suffix_check_twosfx( @@ -1840,7 +1709,7 @@ struct hentry* AffixMgr::compound_check(const char* word, compoundbegin)))) || ((wordnum > 0) && compoundmiddle && ((rv = suffix_check( - st.c_str(), i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, + st.c_str(), i, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || (compoundmoresuffixes && (rv = suffix_check_twosfx( @@ -1911,8 +1780,7 @@ struct hentry* AffixMgr::compound_check(const char* word, ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) || ((oldwordnum > 0) && compoundmiddle && - TESTAFF(rv->astr, compoundmiddle, rv->alen)) // || - // (numdefcpd && ) + TESTAFF(rv->astr, compoundmiddle, rv->alen)) // LANG_hu section: spec. Hungarian rule || ((langnum == LANG_hu) && hu_mov_rule && @@ -1934,7 +1802,7 @@ struct hentry* AffixMgr::compound_check(const char* word, ((word[i - 1] == word[i + 1])) // may be word[i+1] == '\0' )) || (checkcompoundcase && scpd == 0 && !words && - cpdcase_check(word, i)))) + cpdcase_check(word.c_str(), i)))) // LANG_hu section: spec. Hungarian rule || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st.c_str(), i)) && @@ -1968,7 +1836,7 @@ struct hentry* AffixMgr::compound_check(const char* word, if (striple) { checkedstriple = 1; i--; // check "fahrt" instead of "ahrt" in "Schiffahrt" - } else if (i > 2 && *(word + i - 1) == *(word + i - 2)) + } else if (i > 2 && word[i - 1] == word[i - 2]) striple = 1; } @@ -1981,7 +1849,7 @@ struct hentry* AffixMgr::compound_check(const char* word, TESTAFF(rv->astr, compoundflag, rv->alen)) || (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) || - (numdefcpd && words && + (!defcpdtable.empty() && words && defcpd_check(&words, wnum + 1, rv, NULL, 1))) || (scpd != 0 && checkcpdtable[scpd - 1].cond2 != FLAG_NULL && !TESTAFF(rv->astr, checkcpdtable[scpd - 1].cond2, @@ -2038,8 +1906,8 @@ struct hentry* AffixMgr::compound_check(const char* word, cpdmaxsyllable))) && ( // test CHECKCOMPOUNDPATTERN - !numcheckcpd || scpd != 0 || - !cpdpat_check(word, i, rv_first, rv, 0)) && + checkcpdtable.empty() || scpd != 0 || + !cpdpat_check(word.c_str(), i, rv_first, rv, 0)) && ((!checkcompounddup || (rv != rv_first))) // test CHECKCOMPOUNDPATTERN conditions && @@ -2047,7 +1915,7 @@ struct hentry* AffixMgr::compound_check(const char* word, TESTAFF(rv->astr, checkcpdtable[scpd - 1].cond2, rv->alen))) { // forbid compound word, if it is a non compound word with typical // fault - if (checkcompoundrep && cpdrep_check(word, len)) + if (checkcompoundrep && cpdrep_check(word.c_str(), len)) return NULL; return rv_first; } @@ -2059,18 +1927,18 @@ struct hentry* AffixMgr::compound_check(const char* word, sfx = NULL; sfxflag = FLAG_NULL; rv = (compoundflag && !onlycpdrule) - ? affix_check((word + i), strlen(word + i), compoundflag, + ? affix_check((word.c_str() + i), strlen(word.c_str() + i), compoundflag, IN_CPD_END) : NULL; if (!rv && compoundend && !onlycpdrule) { sfx = NULL; pfx = NULL; - rv = affix_check((word + i), strlen(word + i), compoundend, + rv = affix_check((word.c_str() + i), strlen(word.c_str() + i), compoundend, IN_CPD_END); } - if (!rv && numdefcpd && words) { - rv = affix_check((word + i), strlen(word + i), 0, IN_CPD_END); + if (!rv && !defcpdtable.empty() && words) { + rv = affix_check((word.c_str() + i), strlen(word.c_str() + i), 0, IN_CPD_END); if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1)) return rv_first; rv = NULL; @@ -2083,8 +1951,8 @@ struct hentry* AffixMgr::compound_check(const char* word, rv = NULL; // test CHECKCOMPOUNDPATTERN conditions (forbidden compounds) - if (rv && numcheckcpd && scpd == 0 && - cpdpat_check(word, i, rv_first, rv, affixed)) + if (rv && !checkcpdtable.empty() && scpd == 0 && + cpdpat_check(word.c_str(), i, rv_first, rv, affixed)) rv = NULL; // check non_compound flag in suffix and prefix @@ -2118,7 +1986,7 @@ struct hentry* AffixMgr::compound_check(const char* word, if (langnum == LANG_hu) { // calculate syllable number of the word - numsyllable += get_syllable(word + i); + numsyllable += get_syllable(word.c_str() + i); // - affix syllable num. // XXX only second suffix (inflections, not derivations) @@ -2136,7 +2004,7 @@ struct hentry* AffixMgr::compound_check(const char* word, // increment syllable num, if last word has a SYLLABLENUM flag // and the suffix is beginning `s' - if (cpdsyllablenum) { + if (!cpdsyllablenum.empty()) { switch (sfxflag) { case 'c': { numsyllable += 2; @@ -2171,7 +2039,7 @@ struct hentry* AffixMgr::compound_check(const char* word, ((!checkcompounddup || (rv != rv_first)))) { // forbid compound word, if it is a non compound word with typical // fault - if (checkcompoundrep && cpdrep_check(word, len)) + if (checkcompoundrep && cpdrep_check(word.c_str(), len)) return NULL; return rv_first; } @@ -2181,15 +2049,15 @@ struct hentry* AffixMgr::compound_check(const char* word, // perhaps second word is a compound word (recursive call) if (wordnum < maxwordnum) { - rv = compound_check(st.c_str() + i, strlen(st.c_str() + i), wordnum + 1, + rv = compound_check(st.substr(i), wordnum + 1, numsyllable, maxwordnum, wnum + 1, words, rwords, 0, is_sug, info); - if (rv && numcheckcpd && + if (rv && !checkcpdtable.empty() && ((scpd == 0 && - cpdpat_check(word, i, rv_first, rv, affixed)) || + cpdpat_check(word.c_str(), i, rv_first, rv, affixed)) || (scpd != 0 && - !cpdpat_check(word, i, rv_first, rv, affixed)))) + !cpdpat_check(word.c_str(), i, rv_first, rv, affixed)))) rv = NULL; } else { rv = NULL; @@ -2198,13 +2066,12 @@ struct hentry* AffixMgr::compound_check(const char* word, // forbid compound word, if it is a non compound word with typical // fault if (checkcompoundrep || forbiddenword) { - struct hentry* rv2 = NULL; - if (checkcompoundrep && cpdrep_check(word, len)) + if (checkcompoundrep && cpdrep_check(word.c_str(), len)) return NULL; // check first part - if (strncmp(rv->word, word + i, rv->blen) == 0) { + if (strncmp(rv->word, word.c_str() + i, rv->blen) == 0) { char r = st[i + rv->blen]; st[i + rv->blen] = '\0'; @@ -2214,9 +2081,9 @@ struct hentry* AffixMgr::compound_check(const char* word, } if (forbiddenword) { - rv2 = lookup(word); + struct hentry* rv2 = lookup(word.c_str()); if (!rv2) - rv2 = affix_check(word, len); + rv2 = affix_check(word.c_str(), len); if (rv2 && rv2->astr && TESTAFF(rv2->astr, forbiddenword, rv2->alen) && (strncmp(rv2->word, st.c_str(), i + rv->blen) == 0)) { @@ -2248,7 +2115,7 @@ struct hentry* AffixMgr::compound_check(const char* word, scpd++; } while (!onlycpdrule && simplifiedcpd && - scpd <= numcheckcpd); // end of simplifiedcpd loop + scpd <= checkcpdtable.size()); // end of simplifiedcpd loop scpd = 0; wordnum = oldwordnum; @@ -2261,7 +2128,7 @@ struct hentry* AffixMgr::compound_check(const char* word, } else st[i] = ch; - } while (numdefcpd && oldwordnum == 0 && + } while (!defcpdtable.empty() && oldwordnum == 0 && onlycpdrule++ < 1); // end of onlycpd loop } @@ -2278,9 +2145,9 @@ int AffixMgr::compound_check_morph(const char* word, short wnum, hentry** words, hentry** rwords, - char hu_mov_rule = 0, - char** result = NULL, - char* partresult = NULL) { + char hu_mov_rule, + std::string& result, + const std::string* partresult) { int i; short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2; int ok = 0; @@ -2291,12 +2158,11 @@ int AffixMgr::compound_check_morph(const char* word, char ch; int checked_prefix; - char presult[MAXLNLEN]; + std::string presult; int cmin; int cmax; - int onlycpdrule; char affixed = 0; hentry** oldwords = words; @@ -2314,7 +2180,7 @@ int AffixMgr::compound_check_morph(const char* word, } words = oldwords; - onlycpdrule = (words) ? 1 : 0; + int onlycpdrule = (words) ? 1 : 0; do { // onlycpdrule loop @@ -2330,9 +2196,9 @@ int AffixMgr::compound_check_morph(const char* word, affixed = 1; - *presult = '\0'; + presult.clear(); if (partresult) - mystrcat(presult, partresult, MAXLNLEN); + presult.append(*partresult); rv = lookup(st.c_str()); // perhaps without prefix @@ -2345,7 +2211,7 @@ int AffixMgr::compound_check_morph(const char* word, TESTAFF(rv->astr, compoundbegin, rv->alen)) || (compoundmiddle && wordnum && !words && !onlycpdrule && TESTAFF(rv->astr, compoundmiddle, rv->alen)) || - (numdefcpd && onlycpdrule && + (!defcpdtable.empty() && onlycpdrule && ((!words && !wordnum && defcpd_check(&words, wnum, rv, rwords, 0)) || (words && @@ -2357,28 +2223,26 @@ int AffixMgr::compound_check_morph(const char* word, affixed = 0; if (rv) { - sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_PART, st.c_str()); + presult.push_back(MSEP_FLD); + presult.append(MORPH_PART); + presult.append(st.c_str()); if (!HENTRY_FIND(rv, MORPH_STEM)) { - sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_STEM, - st.c_str()); + presult.push_back(MSEP_FLD); + presult.append(MORPH_STEM); + presult.append(st.c_str()); } - // store the pointer of the hash entry - // sprintf(presult + strlen(presult), "%c%s%p", MSEP_FLD, - // MORPH_HENTRY, rv); if (HENTRY_DATA(rv)) { - sprintf(presult + strlen(presult), "%c%s", MSEP_FLD, - HENTRY_DATA2(rv)); + presult.push_back(MSEP_FLD); + presult.append(HENTRY_DATA2(rv)); } } if (!rv) { - if (onlycpdrule && strlen(*result) > MAXLNLEN / 10) - break; if (compoundflag && !(rv = prefix_check(st.c_str(), i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) { - if (((rv = suffix_check(st.c_str(), i, 0, NULL, NULL, 0, NULL, FLAG_NULL, + if (((rv = suffix_check(st.c_str(), i, 0, NULL, FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || (compoundmoresuffixes && @@ -2395,7 +2259,7 @@ int AffixMgr::compound_check_morph(const char* word, if (rv || (((wordnum == 0) && compoundbegin && - ((rv = suffix_check(st.c_str(), i, 0, NULL, NULL, 0, NULL, FLAG_NULL, + ((rv = suffix_check(st.c_str(), i, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || (compoundmoresuffixes && @@ -2406,7 +2270,7 @@ int AffixMgr::compound_check_morph(const char* word, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) || ((wordnum > 0) && compoundmiddle && - ((rv = suffix_check(st.c_str(), i, 0, NULL, NULL, 0, NULL, FLAG_NULL, + ((rv = suffix_check(st.c_str(), i, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || (compoundmoresuffixes && @@ -2416,26 +2280,23 @@ int AffixMgr::compound_check_morph(const char* word, (rv = prefix_check(st.c_str(), i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle)))))) { - // char * p = prefix_check_morph(st, i, 0, compound); - char* p = NULL; + std::string p; if (compoundflag) p = affix_check_morph(st.c_str(), i, compoundflag); - if (!p || (*p == '\0')) { - if (p) - free(p); - p = NULL; + if (p.empty()) { if ((wordnum == 0) && compoundbegin) { p = affix_check_morph(st.c_str(), i, compoundbegin); } else if ((wordnum > 0) && compoundmiddle) { p = affix_check_morph(st.c_str(), i, compoundmiddle); } } - if (p && (*p != '\0')) { - sprintf(presult + strlen(presult), "%c%s%s%s", MSEP_FLD, MORPH_PART, - st.c_str(), line_uniq_app(&p, MSEP_REC)); + if (!p.empty()) { + presult.push_back(MSEP_FLD); + presult.append(MORPH_PART); + presult.append(st.c_str()); + line_uniq_app(p, MSEP_REC); + presult.append(p); } - if (p) - free(p); checked_prefix = 1; } // else check forbiddenwords @@ -2507,7 +2368,7 @@ int AffixMgr::compound_check_morph(const char* word, )) || ( // test CHECKCOMPOUNDPATTERN - numcheckcpd && !words && + !checkcpdtable.empty() && !words && cpdpat_check(word, i, rv, NULL, affixed)) || (checkcompoundcase && !words && cpdcase_check(word, i)))) // LANG_hu section: spec. Hungarian rule @@ -2541,31 +2402,29 @@ int AffixMgr::compound_check_morph(const char* word, TESTAFF(rv->astr, compoundflag, rv->alen)) || (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) || - (numdefcpd && words && + (!defcpdtable.empty() && words && defcpd_check(&words, wnum + 1, rv, NULL, 1))))) { rv = rv->next_homonym; } if (rv && words && words[wnum + 1]) { - mystrcat(*result, presult, MAXLNLEN); - mystrcat(*result, " ", MAXLNLEN); - mystrcat(*result, MORPH_PART, MAXLNLEN); - mystrcat(*result, word + i, MAXLNLEN); + result.append(presult); + result.append(" "); + result.append(MORPH_PART); + result.append(word + i); if (complexprefixes && HENTRY_DATA(rv)) - mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); + result.append(HENTRY_DATA2(rv)); if (!HENTRY_FIND(rv, MORPH_STEM)) { - mystrcat(*result, " ", MAXLNLEN); - mystrcat(*result, MORPH_STEM, MAXLNLEN); - mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN); + result.append(" "); + result.append(MORPH_STEM); + result.append(HENTRY_WORD(rv)); } // store the pointer of the hash entry - // sprintf(*result + strlen(*result), " %s%p", - // MORPH_HENTRY, rv); if (!complexprefixes && HENTRY_DATA(rv)) { - mystrcat(*result, " ", MAXLNLEN); - mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); + result.append(" "); + result.append(HENTRY_DATA2(rv)); } - mystrcat(*result, "\n", MAXLNLEN); + result.append("\n"); return 0; } @@ -2606,28 +2465,26 @@ int AffixMgr::compound_check_morph(const char* word, cpdmaxsyllable))) && ((!checkcompounddup || (rv != rv_first)))) { // bad compound word - mystrcat(*result, presult, MAXLNLEN); - mystrcat(*result, " ", MAXLNLEN); - mystrcat(*result, MORPH_PART, MAXLNLEN); - mystrcat(*result, word + i, MAXLNLEN); + result.append(presult); + result.append(" "); + result.append(MORPH_PART); + result.append(word + i); if (HENTRY_DATA(rv)) { if (complexprefixes) - mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); + result.append(HENTRY_DATA2(rv)); if (!HENTRY_FIND(rv, MORPH_STEM)) { - mystrcat(*result, " ", MAXLNLEN); - mystrcat(*result, MORPH_STEM, MAXLNLEN); - mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN); + result.append(" "); + result.append(MORPH_STEM); + result.append(HENTRY_WORD(rv)); } // store the pointer of the hash entry - // sprintf(*result + strlen(*result), " - // %s%p", MORPH_HENTRY, rv); if (!complexprefixes) { - mystrcat(*result, " ", MAXLNLEN); - mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); + result.append(" "); + result.append(HENTRY_DATA2(rv)); } } - mystrcat(*result, "\n", MAXLNLEN); + result.append("\n"); ok = 1; } @@ -2649,27 +2506,24 @@ int AffixMgr::compound_check_morph(const char* word, rv = affix_check((word + i), strlen(word + i), compoundend); } - if (!rv && numdefcpd && words) { + if (!rv && !defcpdtable.empty() && words) { rv = affix_check((word + i), strlen(word + i), 0, IN_CPD_END); if (rv && words && defcpd_check(&words, wnum + 1, rv, NULL, 1)) { - char* m = NULL; + std::string m; if (compoundflag) m = affix_check_morph((word + i), strlen(word + i), compoundflag); - if ((!m || *m == '\0') && compoundend) { - if (m) - free(m); + if (m.empty() && compoundend) { m = affix_check_morph((word + i), strlen(word + i), compoundend); } - mystrcat(*result, presult, MAXLNLEN); - if (m || (*m != '\0')) { - char m2[MAXLNLEN]; - sprintf(m2, "%c%s%s%s", MSEP_FLD, MORPH_PART, word + i, - line_uniq_app(&m, MSEP_REC)); - mystrcat(*result, m2, MAXLNLEN); + result.append(presult); + if (!m.empty()) { + result.push_back(MSEP_FLD); + result.append(MORPH_PART); + result.append(word + i); + line_uniq_app(m, MSEP_REC); + result.append(m); } - if (m) - free(m); - mystrcat(*result, "\n", MAXLNLEN); + result.append("\n"); ok = 1; } } @@ -2713,7 +2567,7 @@ int AffixMgr::compound_check_morph(const char* word, // increment syllable num, if last word has a SYLLABLENUM flag // and the suffix is beginning `s' - if (cpdsyllablenum) { + if (!cpdsyllablenum.empty()) { switch (sfxflag) { case 'c': { numsyllable += 2; @@ -2745,25 +2599,21 @@ int AffixMgr::compound_check_morph(const char* word, (((cpdwordmax == -1) || (wordnum + 1 < cpdwordmax)) || ((cpdmaxsyllable != 0) && (numsyllable <= cpdmaxsyllable))) && ((!checkcompounddup || (rv != rv_first)))) { - char* m = NULL; + std::string m; if (compoundflag) m = affix_check_morph((word + i), strlen(word + i), compoundflag); - if ((!m || *m == '\0') && compoundend) { - if (m) - free(m); + if (m.empty() && compoundend) { m = affix_check_morph((word + i), strlen(word + i), compoundend); } - mystrcat(*result, presult, MAXLNLEN); - if (m && (*m != '\0')) { - char m2[MAXLNLEN]; - sprintf(m2, "%c%s%s%s", MSEP_FLD, MORPH_PART, word + i, - line_uniq_app(&m, MSEP_REC)); - mystrcat(*result, m2, MAXLNLEN); + result.append(presult); + if (!m.empty()) { + result.push_back(MSEP_FLD); + result.append(MORPH_PART); + result.append(word + 1); + line_uniq_app(m, MSEP_REC); + result.append(m); } - if (m) - free(m); - if (strlen(*result) + 1 < MAXLNLEN) - sprintf(*result + strlen(*result), "%c", MSEP_REC); + result.push_back(MSEP_REC); ok = 1; } @@ -2774,7 +2624,7 @@ int AffixMgr::compound_check_morph(const char* word, if ((wordnum < maxwordnum) && (ok == 0)) { compound_check_morph((word + i), strlen(word + i), wordnum + 1, numsyllable, maxwordnum, wnum + 1, words, rwords, 0, - result, presult); + result, &presult); } else { rv = NULL; } @@ -2783,26 +2633,13 @@ int AffixMgr::compound_check_morph(const char* word, wordnum = oldwordnum; numsyllable = oldnumsyllable; - } while (numdefcpd && oldwordnum == 0 && + } while (!defcpdtable.empty() && oldwordnum == 0 && onlycpdrule++ < 1); // end of onlycpd loop } return 0; } -// return 1 if s1 (reversed) is a leading subset of end of s2 -/* inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int - len) - { - while ((len > 0) && *s1 && (*s1 == *end_of_s2)) { - s1++; - end_of_s2--; - len--; - } - return (*s1 == '\0'); - } - */ - inline int AffixMgr::isRevSubset(const char* s1, const char* end_of_s2, int len) { @@ -2815,14 +2652,10 @@ inline int AffixMgr::isRevSubset(const char* s1, } // check word for suffixes - struct hentry* AffixMgr::suffix_check(const char* word, int len, int sfxopts, PfxEntry* ppfx, - char** wlst, - int maxSug, - int* ns, const FLAG cclass, const FLAG needflag, char in_compound) { @@ -2861,7 +2694,7 @@ struct hentry* AffixMgr::suffix_check(const char* word, (ppfx && !((ep->getCont()) && TESTAFF(ep->getCont(), needaffix, ep->getContLen()))))) { - rv = se->checkword(word, len, sfxopts, ppfx, wlst, maxSug, ns, + rv = se->checkword(word, len, sfxopts, ppfx, (FLAG)cclass, needflag, (in_compound ? 0 : onlyincompound)); if (rv) { @@ -2912,7 +2745,7 @@ struct hentry* AffixMgr::suffix_check(const char* word, if (in_compound != IN_CPD_END || ppfx || !(sptr->getCont() && TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))) { - rv = sptr->checkword(word, len, sfxopts, ppfx, wlst, maxSug, ns, + rv = sptr->checkword(word, len, sfxopts, ppfx, cclass, needflag, (in_compound ? 0 : onlyincompound)); if (rv) { @@ -2985,23 +2818,21 @@ struct hentry* AffixMgr::suffix_check_twosfx(const char* word, return NULL; } -char* AffixMgr::suffix_check_twosfx_morph(const char* word, - int len, - int sfxopts, - PfxEntry* ppfx, - const FLAG needflag) { +std::string AffixMgr::suffix_check_twosfx_morph(const char* word, + int len, + int sfxopts, + PfxEntry* ppfx, + const FLAG needflag) { std::string result; std::string result2; std::string result3; - char* st; - // first handle the special case of 0 length suffixes SfxEntry* se = sStart[0]; while (se) { if (contclasses[se->getFlag()]) { - st = se->check_twosfx_morph(word, len, sfxopts, ppfx, needflag); - if (st) { + std::string st = se->check_twosfx_morph(word, len, sfxopts, ppfx, needflag); + if (!st.empty()) { if (ppfx) { if (ppfx->getMorph()) { result.append(ppfx->getMorph()); @@ -3010,7 +2841,6 @@ char* AffixMgr::suffix_check_twosfx_morph(const char* word, debugflag(result, ppfx->getFlag()); } result.append(st); - free(st); if (se->getMorph()) { result.append(" "); result.append(se->getMorph()); @@ -3024,20 +2854,19 @@ char* AffixMgr::suffix_check_twosfx_morph(const char* word, // now handle the general case if (len == 0) - return NULL; // FULLSTRIP + return std::string(); // FULLSTRIP unsigned char sp = *((const unsigned char*)(word + len - 1)); SfxEntry* sptr = sStart[sp]; while (sptr) { if (isRevSubset(sptr->getKey(), word + len - 1, len)) { if (contclasses[sptr->getFlag()]) { - st = sptr->check_twosfx_morph(word, len, sfxopts, ppfx, needflag); - if (st) { + std::string st = sptr->check_twosfx_morph(word, len, sfxopts, ppfx, needflag); + if (!st.empty()) { sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless if (!sptr->getCont()) sfxappnd = sptr->getKey(); // BUG: sfxappnd not stateless result2.assign(st); - free(st); result3.clear(); @@ -3057,25 +2886,20 @@ char* AffixMgr::suffix_check_twosfx_morph(const char* word, } } - if (!result.empty()) - return mystrdup(result.c_str()); - - return NULL; + return result; } -char* AffixMgr::suffix_check_morph(const char* word, - int len, - int sfxopts, - PfxEntry* ppfx, - const FLAG cclass, - const FLAG needflag, - char in_compound) { - char result[MAXLNLEN]; +std::string AffixMgr::suffix_check_morph(const char* word, + int len, + int sfxopts, + PfxEntry* ppfx, + const FLAG cclass, + const FLAG needflag, + char in_compound) { + std::string result; struct hentry* rv = NULL; - result[0] = '\0'; - PfxEntry* ep = ppfx; // first handle the special case of 0 length suffixes @@ -3109,37 +2933,34 @@ char* AffixMgr::suffix_check_morph(const char* word, (ppfx && !((ep->getCont()) && TESTAFF(ep->getCont(), needaffix, ep->getContLen())))))) - rv = se->checkword(word, len, sfxopts, ppfx, NULL, 0, 0, cclass, - needflag); + rv = se->checkword(word, len, sfxopts, ppfx, cclass, + needflag, FLAG_NULL); while (rv) { if (ppfx) { if (ppfx->getMorph()) { - mystrcat(result, ppfx->getMorph(), MAXLNLEN); - mystrcat(result, " ", MAXLNLEN); + result.append(ppfx->getMorph()); + result.append(" "); } else debugflag(result, ppfx->getFlag()); } if (complexprefixes && HENTRY_DATA(rv)) - mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + result.append(HENTRY_DATA2(rv)); if (!HENTRY_FIND(rv, MORPH_STEM)) { - mystrcat(result, " ", MAXLNLEN); - mystrcat(result, MORPH_STEM, MAXLNLEN); - mystrcat(result, HENTRY_WORD(rv), MAXLNLEN); + result.append(" "); + result.append(MORPH_STEM); + result.append(HENTRY_WORD(rv)); } - // store the pointer of the hash entry - // sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, - // rv); if (!complexprefixes && HENTRY_DATA(rv)) { - mystrcat(result, " ", MAXLNLEN); - mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + result.append(" "); + result.append(HENTRY_DATA2(rv)); } if (se->getMorph()) { - mystrcat(result, " ", MAXLNLEN); - mystrcat(result, se->getMorph(), MAXLNLEN); + result.append(" "); + result.append(se->getMorph()); } else debugflag(result, se->getFlag()); - mystrcat(result, "\n", MAXLNLEN); + result.append("\n"); rv = se->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag); } } @@ -3148,7 +2969,7 @@ char* AffixMgr::suffix_check_morph(const char* word, // now handle the general case if (len == 0) - return NULL; // FULLSTRIP + return std::string(); // FULLSTRIP unsigned char sp = *((const unsigned char*)(word + len - 1)); SfxEntry* sptr = sStart[sp]; @@ -3179,38 +3000,35 @@ char* AffixMgr::suffix_check_morph(const char* word, (cclass || !(sptr->getCont() && TESTAFF(sptr->getCont(), needaffix, sptr->getContLen()))))) - rv = sptr->checkword(word, len, sfxopts, ppfx, NULL, 0, 0, cclass, - needflag); + rv = sptr->checkword(word, len, sfxopts, ppfx, cclass, + needflag, FLAG_NULL); while (rv) { if (ppfx) { if (ppfx->getMorph()) { - mystrcat(result, ppfx->getMorph(), MAXLNLEN); - mystrcat(result, " ", MAXLNLEN); + result.append(ppfx->getMorph()); + result.append(" "); } else debugflag(result, ppfx->getFlag()); } if (complexprefixes && HENTRY_DATA(rv)) - mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + result.append(HENTRY_DATA2(rv)); if (!HENTRY_FIND(rv, MORPH_STEM)) { - mystrcat(result, " ", MAXLNLEN); - mystrcat(result, MORPH_STEM, MAXLNLEN); - mystrcat(result, HENTRY_WORD(rv), MAXLNLEN); + result.append(" "); + result.append(MORPH_STEM); + result.append(HENTRY_WORD(rv)); } - // store the pointer of the hash entry - // sprintf(result + strlen(result), " %s%p", - // MORPH_HENTRY, rv); if (!complexprefixes && HENTRY_DATA(rv)) { - mystrcat(result, " ", MAXLNLEN); - mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + result.append(" "); + result.append(HENTRY_DATA2(rv)); } if (sptr->getMorph()) { - mystrcat(result, " ", MAXLNLEN); - mystrcat(result, sptr->getMorph(), MAXLNLEN); + result.append(" "); + result.append(sptr->getMorph()); } else debugflag(result, sptr->getFlag()); - mystrcat(result, "\n", MAXLNLEN); + result.append("\n"); rv = sptr->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag); } sptr = sptr->getNextEQ(); @@ -3219,9 +3037,7 @@ char* AffixMgr::suffix_check_morph(const char* word, } } - if (*result) - return mystrdup(result); - return NULL; + return result; } // check if word with affixes is correctly spelled @@ -3237,8 +3053,7 @@ struct hentry* AffixMgr::affix_check(const char* word, return rv; // if still not found check all suffixes - rv = suffix_check(word, len, 0, NULL, NULL, 0, NULL, FLAG_NULL, needflag, - in_compound); + rv = suffix_check(word, len, 0, NULL, FLAG_NULL, needflag, in_compound); if (havecontclass) { sfx = NULL; @@ -3259,27 +3074,22 @@ struct hentry* AffixMgr::affix_check(const char* word, } // check if word with affixes is correctly spelled -char* AffixMgr::affix_check_morph(const char* word, +std::string AffixMgr::affix_check_morph(const char* word, int len, const FLAG needflag, char in_compound) { - char result[MAXLNLEN]; - char* st = NULL; - - *result = '\0'; + std::string result; // check all prefixes (also crossed with suffixes if allowed) - st = prefix_check_morph(word, len, in_compound); - if (st) { - mystrcat(result, st, MAXLNLEN); - free(st); + std::string st = prefix_check_morph(word, len, in_compound); + if (!st.empty()) { + result.append(st); } // if still not found check all suffixes st = suffix_check_morph(word, len, 0, NULL, '\0', needflag, in_compound); - if (st) { - mystrcat(result, st, MAXLNLEN); - free(st); + if (!st.empty()) { + result.append(st); } if (havecontclass) { @@ -3287,39 +3097,120 @@ char* AffixMgr::affix_check_morph(const char* word, pfx = NULL; // if still not found check all two-level suffixes st = suffix_check_twosfx_morph(word, len, 0, NULL, needflag); - if (st) { - mystrcat(result, st, MAXLNLEN); - free(st); + if (!st.empty()) { + result.append(st); } // if still not found check all two-level suffixes st = prefix_check_twosfx_morph(word, len, IN_CPD_NOT, needflag); - if (st) { - mystrcat(result, st, MAXLNLEN); - free(st); + if (!st.empty()) { + result.append(st); } } - return mystrdup(result); + return result; } -char* AffixMgr::morphgen(const char* ts, - int wl, - const unsigned short* ap, - unsigned short al, - const char* morph, - const char* targetmorph, +// morphcmp(): compare MORPH_DERI_SFX, MORPH_INFL_SFX and MORPH_TERM_SFX fields +// in the first line of the inputs +// return 0, if inputs equal +// return 1, if inputs may equal with a secondary suffix +// otherwise return -1 +static int morphcmp(const char* s, const char* t) { + int se = 0; + int te = 0; + const char* sl; + const char* tl; + const char* olds; + const char* oldt; + if (!s || !t) + return 1; + olds = s; + sl = strchr(s, '\n'); + s = strstr(s, MORPH_DERI_SFX); + if (!s || (sl && sl < s)) + s = strstr(olds, MORPH_INFL_SFX); + if (!s || (sl && sl < s)) { + s = strstr(olds, MORPH_TERM_SFX); + olds = NULL; + } + oldt = t; + tl = strchr(t, '\n'); + t = strstr(t, MORPH_DERI_SFX); + if (!t || (tl && tl < t)) + t = strstr(oldt, MORPH_INFL_SFX); + if (!t || (tl && tl < t)) { + t = strstr(oldt, MORPH_TERM_SFX); + oldt = NULL; + } + while (s && t && (!sl || sl > s) && (!tl || tl > t)) { + s += MORPH_TAG_LEN; + t += MORPH_TAG_LEN; + se = 0; + te = 0; + while ((*s == *t) && !se && !te) { + s++; + t++; + switch (*s) { + case ' ': + case '\n': + case '\t': + case '\0': + se = 1; + } + switch (*t) { + case ' ': + case '\n': + case '\t': + case '\0': + te = 1; + } + } + if (!se || !te) { + // not terminal suffix difference + if (olds) + return -1; + return 1; + } + olds = s; + s = strstr(s, MORPH_DERI_SFX); + if (!s || (sl && sl < s)) + s = strstr(olds, MORPH_INFL_SFX); + if (!s || (sl && sl < s)) { + s = strstr(olds, MORPH_TERM_SFX); + olds = NULL; + } + oldt = t; + t = strstr(t, MORPH_DERI_SFX); + if (!t || (tl && tl < t)) + t = strstr(oldt, MORPH_INFL_SFX); + if (!t || (tl && tl < t)) { + t = strstr(oldt, MORPH_TERM_SFX); + oldt = NULL; + } + } + if (!s && !t && se && te) + return 0; + return 1; +} + +std::string AffixMgr::morphgen(const char* ts, + int wl, + const unsigned short* ap, + unsigned short al, + const char* morph, + const char* targetmorph, int level) { // handle suffixes if (!morph) - return NULL; + return std::string(); // check substandard flag if (TESTAFF(ap, substandard, al)) - return NULL; + return std::string(); if (morphcmp(morph, targetmorph) == 0) - return mystrdup(ts); + return ts; size_t stemmorphcatpos; std::string mymorph; @@ -3352,41 +3243,36 @@ char* AffixMgr::morphgen(const char* ts, int cmp = morphcmp(stemmorph, targetmorph); if (cmp == 0) { - char* newword = sptr->add(ts, wl); - if (newword) { - hentry* check = pHMgr->lookup(newword); // XXX extra dic + std::string newword = sptr->add(ts, wl); + if (!newword.empty()) { + hentry* check = pHMgr->lookup(newword.c_str()); // XXX extra dic if (!check || !check->astr || !(TESTAFF(check->astr, forbiddenword, check->alen) || TESTAFF(check->astr, ONLYUPCASEFLAG, check->alen))) { return newword; } - free(newword); } } // recursive call for secondary suffixes if ((level == 0) && (cmp == 1) && (sptr->getContLen() > 0) && - // (get_sfxcount(stemmorph) < targetcount) && !TESTAFF(sptr->getCont(), substandard, sptr->getContLen())) { - char* newword = sptr->add(ts, wl); - if (newword) { - char* newword2 = - morphgen(newword, strlen(newword), sptr->getCont(), + std::string newword = sptr->add(ts, wl); + if (!newword.empty()) { + std::string newword2 = + morphgen(newword.c_str(), newword.size(), sptr->getCont(), sptr->getContLen(), stemmorph, targetmorph, 1); - if (newword2) { - free(newword); + if (!newword2.empty()) { return newword2; } - free(newword); - newword = NULL; } } } sptr = sptr->getFlgNxt(); } } - return NULL; + return std::string(); } int AffixMgr::expand_rootword(struct guessword* wlst, @@ -3439,10 +3325,10 @@ int AffixMgr::expand_rootword(struct guessword* wlst, TESTAFF(sptr->getCont(), circumfix, sptr->getContLen())) || (onlyincompound && TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) { - char* newword = sptr->add(ts, wl); - if (newword) { + std::string newword = sptr->add(ts, wl); + if (!newword.empty()) { if (nh < maxn) { - wlst[nh].word = newword; + wlst[nh].word = mystrdup(newword.c_str()); wlst[nh].allow = sptr->allowCross(); wlst[nh].orig = NULL; nh++; @@ -3456,13 +3342,11 @@ int AffixMgr::expand_rootword(struct guessword* wlst, if (!wlst[nh].word) return nh - 1; wlst[nh].allow = (1 == 0); - wlst[nh].orig = mystrdup(newword); + wlst[nh].orig = mystrdup(newword.c_str()); if (!wlst[nh].orig) return nh - 1; nh++; } - } else { - free(newword); } } } @@ -3484,15 +3368,13 @@ int AffixMgr::expand_rootword(struct guessword* wlst, ((badl > cptr->getKeyLen()) && (strncmp(cptr->getKey(), bad, cptr->getKeyLen()) == 0)))) { int l1 = strlen(wlst[j].word); - char* newword = cptr->add(wlst[j].word, l1); - if (newword) { + std::string newword = cptr->add(wlst[j].word, l1); + if (!newword.empty()) { if (nh < maxn) { - wlst[nh].word = newword; + wlst[nh].word = mystrdup(newword.c_str()); wlst[nh].allow = cptr->allowCross(); wlst[nh].orig = NULL; nh++; - } else { - free(newword); } } } @@ -3518,15 +3400,13 @@ int AffixMgr::expand_rootword(struct guessword* wlst, TESTAFF(ptr->getCont(), circumfix, ptr->getContLen())) || (onlyincompound && TESTAFF(ptr->getCont(), onlyincompound, ptr->getContLen()))))) { - char* newword = ptr->add(ts, wl); - if (newword) { + std::string newword = ptr->add(ts, wl); + if (!newword.empty()) { if (nh < maxn) { - wlst[nh].word = newword; + wlst[nh].word = mystrdup(newword.c_str()); wlst[nh].allow = ptr->allowCross(); wlst[nh].orig = NULL; nh++; - } else { - free(newword); } } } @@ -3537,15 +3417,8 @@ int AffixMgr::expand_rootword(struct guessword* wlst, return nh; } -// return length of replacing table -int AffixMgr::get_numrep() const { - return numrep; -} - // return replacing table -struct replentry* AffixMgr::get_reptable() const { - if (!reptable) - return NULL; +const std::vector& AffixMgr::get_reptable() const { return reptable; } @@ -3570,35 +3443,21 @@ struct phonetable* AffixMgr::get_phonetable() const { return phone; } -// return length of character map table -int AffixMgr::get_nummap() const { - return nummap; -} - // return character map table -struct mapentry* AffixMgr::get_maptable() const { - if (!maptable) - return NULL; +const std::vector& AffixMgr::get_maptable() const { return maptable; } -// return length of word break table -int AffixMgr::get_numbreak() const { - return numbreak; -} - // return character map table -char** AffixMgr::get_breaktable() const { - if (!breaktable) - return NULL; +const std::vector& AffixMgr::get_breaktable() const { return breaktable; } // return text encoding of dictionary -char* AffixMgr::get_encoding() { - if (!encoding) - encoding = mystrdup(SPELL_ENCODING); - return mystrdup(encoding); +const std::string& AffixMgr::get_encoding() { + if (encoding.empty()) + encoding = SPELL_ENCODING; + return encoding; } // return text encoding of dictionary @@ -3641,10 +3500,10 @@ char* AffixMgr::encode_flag(unsigned short aflag) const { } // return the preferred ignore string for suggestions -char* AffixMgr::get_ignore() const { - if (!ignorechars) +const char* AffixMgr::get_ignore() const { + if (ignorechars.empty()) return NULL; - return ignorechars; + return ignorechars.c_str(); } // return the preferred ignore string for suggestions @@ -3654,20 +3513,20 @@ const std::vector& AffixMgr::get_ignore_utf16() const { // return the keyboard string for suggestions char* AffixMgr::get_key_string() { - if (!keystring) - keystring = mystrdup(SPELL_KEYSTRING); - return mystrdup(keystring); + if (keystring.empty()) + keystring = SPELL_KEYSTRING; + return mystrdup(keystring.c_str()); } // return the preferred try string for suggestions char* AffixMgr::get_try_string() const { - if (!trystring) + if (trystring.empty()) return NULL; - return mystrdup(trystring); + return mystrdup(trystring.c_str()); } // return the preferred try string for suggestions -const char* AffixMgr::get_wordchars() const { +const std::string& AffixMgr::get_wordchars() const { return wordchars; } @@ -3677,7 +3536,7 @@ const std::vector& AffixMgr::get_wordchars_utf16() const { // is there compounding? int AffixMgr::get_compound() const { - return compoundflag || compoundbegin || numdefcpd; + return compoundflag || compoundbegin || !defcpdtable.empty(); } // return the compound words control flag @@ -3710,49 +3569,16 @@ FLAG AffixMgr::get_onlyincompound() const { return onlyincompound; } -// return the compound word signal flag -FLAG AffixMgr::get_compoundroot() const { - return compoundroot; -} - -// return the compound begin signal flag -FLAG AffixMgr::get_compoundbegin() const { - return compoundbegin; -} - -// return the value of checknum -int AffixMgr::get_checknum() const { - return checknum; -} - -// return the value of prefix -const char* AffixMgr::get_prefix() const { - if (pfx) - return pfx->getKey(); - return NULL; -} - // return the value of suffix -const char* AffixMgr::get_suffix() const { - return sfxappnd; -} - -// return the value of suffix -const char* AffixMgr::get_version() const { +const std::string& AffixMgr::get_version() const { return version; } -// return lemma_present flag -FLAG AffixMgr::get_lemma_present() const { - return lemma_present; -} - // utility method to look up root words in hash table struct hentry* AffixMgr::lookup(const char* word) { - int i; struct hentry* he = NULL; - for (i = 0; i < *maxdic && !he; i++) { - he = (alldic[i])->lookup(word); + for (size_t i = 0; i < alldic.size() && !he; ++i) { + he = alldic[i]->lookup(word); } return he; } @@ -3794,839 +3620,751 @@ int AffixMgr::get_sugswithdots(void) const { } /* parse flag */ -int AffixMgr::parse_flag(char* line, unsigned short* out, FileMgr* af) { - char* s = NULL; +bool AffixMgr::parse_flag(const std::string& line, unsigned short* out, FileMgr* af) { if (*out != FLAG_NULL && !(*out >= DEFAULTFLAGS)) { HUNSPELL_WARNING( stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum()); - return 1; + return false; } - if (parse_string(line, &s, af->getlinenum())) - return 1; - *out = pHMgr->decode_flag(s); - free(s); - return 0; + std::string s; + if (!parse_string(line, s, af->getlinenum())) + return false; + *out = pHMgr->decode_flag(s.c_str()); + return true; } /* parse num */ -int AffixMgr::parse_num(char* line, int* out, FileMgr* af) { - char* s = NULL; +bool AffixMgr::parse_num(const std::string& line, int* out, FileMgr* af) { if (*out != -1) { HUNSPELL_WARNING( stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum()); - return 1; + return false; } - if (parse_string(line, &s, af->getlinenum())) - return 1; - *out = atoi(s); - free(s); - return 0; + std::string s; + if (!parse_string(line, s, af->getlinenum())) + return false; + *out = atoi(s.c_str()); + return true; } /* parse in the max syllablecount of compound words and */ -int AffixMgr::parse_cpdsyllable(char* line, FileMgr* af) { - char* tp = line; - char* piece; +bool AffixMgr::parse_cpdsyllable(const std::string& line, FileMgr* af) { int i = 0; int np = 0; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - np++; - break; - } - case 1: { - cpdmaxsyllable = atoi(piece); - np++; - break; - } - case 2: { - if (!utf8) { - cpdvowels = mystrdup(piece); - } else { - std::vector w; - u8_u16(w, piece); - if (!w.empty()) { - std::sort(w.begin(), w.end()); - cpdvowels_utf16 = (w_char*)malloc(w.size() * sizeof(w_char)); - if (!cpdvowels_utf16) - return 1; - memcpy(cpdvowels_utf16, &w[0], w.size()); - } - cpdvowels_utf16_len = w.size(); - } - np++; - break; - } - default: - break; + std::string::const_iterator iter = line.begin(); + std::string::const_iterator start_piece = mystrsep(line, iter); + while (start_piece != line.end()) { + switch (i) { + case 0: { + np++; + break; } - i++; + case 1: { + cpdmaxsyllable = atoi(std::string(start_piece, iter).c_str()); + np++; + break; + } + case 2: { + if (!utf8) { + cpdvowels.assign(start_piece, iter); + std::sort(cpdvowels.begin(), cpdvowels.end()); + } else { + std::string piece(start_piece, iter); + u8_u16(cpdvowels_utf16, piece); + std::sort(cpdvowels_utf16.begin(), cpdvowels_utf16.end()); + } + np++; + break; + } + default: + break; } - piece = mystrsep(&tp, 0); + ++i; + start_piece = mystrsep(line, iter); } if (np < 2) { HUNSPELL_WARNING(stderr, "error: line %d: missing compoundsyllable information\n", af->getlinenum()); - return 1; + return false; } if (np == 2) - cpdvowels = mystrdup("aeiouAEIOU"); - return 0; + cpdvowels = "AEIOUaeiou"; + return true; } /* parse in the typical fault correcting table */ -int AffixMgr::parse_reptable(char* line, FileMgr* af) { - if (numrep != 0) { +bool AffixMgr::parse_reptable(const std::string& line, FileMgr* af) { + if (parsedrep) { HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); - return 1; + return false; } - char* tp = line; - char* piece; + parsedrep = true; + int numrep = -1; int i = 0; int np = 0; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - np++; - break; - } - case 1: { - numrep = atoi(piece); - if (numrep < 1) { - HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", - af->getlinenum()); - return 1; - } - reptable = (replentry*)malloc(numrep * sizeof(struct replentry)); - if (!reptable) - return 1; - np++; - break; - } - default: - break; + std::string::const_iterator iter = line.begin(); + std::string::const_iterator start_piece = mystrsep(line, iter); + while (start_piece != line.end()) { + switch (i) { + case 0: { + np++; + break; } - i++; + case 1: { + numrep = atoi(std::string(start_piece, iter).c_str()); + if (numrep < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", + af->getlinenum()); + return false; + } + reptable.reserve(numrep); + np++; + break; + } + default: + break; } - piece = mystrsep(&tp, 0); + ++i; + start_piece = mystrsep(line, iter); } if (np != 2) { HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); - return 1; + return false; } /* now parse the numrep lines to read in the remainder of the table */ - char* nl; - for (int j = 0; j < numrep; j++) { - if ((nl = af->getline()) == NULL) - return 1; + for (int j = 0; j < numrep; ++j) { + std::string nl; + if (!af->getline(nl)) + return false; mychomp(nl); - tp = nl; + reptable.push_back(replentry()); + iter = nl.begin(); i = 0; - reptable[j].pattern = NULL; - reptable[j].pattern2 = NULL; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - if (strncmp(piece, "REP", 3) != 0) { - HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", - af->getlinenum()); - numrep = 0; - return 1; - } - break; - } - case 1: { - if (*piece == '^') - reptable[j].start = true; - else - reptable[j].start = false; - reptable[j].pattern = - mystrrep(mystrdup(piece + int(reptable[j].start)), "_", " "); - int lr = strlen(reptable[j].pattern) - 1; - if (reptable[j].pattern[lr] == '$') { - reptable[j].end = true; - reptable[j].pattern[lr] = '\0'; - } else - reptable[j].end = false; - break; - } - case 2: { - reptable[j].pattern2 = mystrrep(mystrdup(piece), "_", " "); - break; - } - default: - break; - } - i++; - } - piece = mystrsep(&tp, 0); - } - if ((!(reptable[j].pattern)) || (!(reptable[j].pattern2))) { - HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", - af->getlinenum()); - numrep = 0; - return 1; - } - } - return 0; -} - -/* parse in the typical fault correcting table */ -int AffixMgr::parse_convtable(char* line, - FileMgr* af, - RepList** rl, - const char* keyword) { - if (*rl) { - HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", - af->getlinenum()); - return 1; - } - char* tp = line; - char* piece; - int i = 0; - int np = 0; - int numrl = 0; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { + int type = 0; + start_piece = mystrsep(nl, iter); + while (start_piece != nl.end()) { switch (i) { case 0: { - np++; + if (nl.compare(start_piece - nl.begin(), 3, "REP", 3) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", + af->getlinenum()); + reptable.clear(); + return false; + } break; } case 1: { - numrl = atoi(piece); - if (numrl < 1) { - HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", - af->getlinenum()); - return 1; + if (*start_piece == '^') + type = 1; + reptable.back().pattern.assign(start_piece + type, iter); + mystrrep(reptable.back().pattern, "_", " "); + if (!reptable.back().pattern.empty() && reptable.back().pattern[reptable.back().pattern.size() - 1] == '$') { + type += 2; + reptable.back().pattern.resize(reptable.back().pattern.size() - 1); } - *rl = new RepList(numrl); - if (!*rl) - return 1; - np++; + break; + } + case 2: { + reptable.back().outstrings[type].assign(start_piece, iter); + mystrrep(reptable.back().outstrings[type], "_", " "); break; } default: break; } - i++; + ++i; + start_piece = mystrsep(nl, iter); } - piece = mystrsep(&tp, 0); + if (reptable.back().pattern.empty() || reptable.back().outstrings[type].empty()) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", + af->getlinenum()); + reptable.clear(); + return false; + } + } + return true; +} + +/* parse in the typical fault correcting table */ +bool AffixMgr::parse_convtable(const std::string& line, + FileMgr* af, + RepList** rl, + const std::string& keyword) { + if (*rl) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", + af->getlinenum()); + return false; + } + int i = 0; + int np = 0; + int numrl = 0; + std::string::const_iterator iter = line.begin(); + std::string::const_iterator start_piece = mystrsep(line, iter); + while (start_piece != line.end()) { + switch (i) { + case 0: { + np++; + break; + } + case 1: { + numrl = atoi(std::string(start_piece, iter).c_str()); + if (numrl < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", + af->getlinenum()); + return false; + } + *rl = new RepList(numrl); + if (!*rl) + return false; + np++; + break; + } + default: + break; + } + ++i; + start_piece = mystrsep(line, iter); } if (np != 2) { HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); - return 1; + return false; } /* now parse the num lines to read in the remainder of the table */ - char* nl; for (int j = 0; j < numrl; j++) { - if (!(nl = af->getline())) - return 1; + std::string nl; + if (!af->getline(nl)) + return false; mychomp(nl); - tp = nl; i = 0; - char* pattern = NULL; - char* pattern2 = NULL; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { + std::string pattern; + std::string pattern2; + iter = nl.begin(); + start_piece = mystrsep(nl, iter); + while (start_piece != nl.end()) { + { switch (i) { case 0: { - if (strncmp(piece, keyword, strlen(keyword)) != 0) { + if (nl.compare(start_piece - nl.begin(), keyword.size(), keyword, 0, keyword.size()) != 0) { HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); delete *rl; *rl = NULL; - return 1; + return false; } break; } case 1: { - pattern = mystrrep(mystrdup(piece), "_", " "); + pattern.assign(start_piece, iter); break; } case 2: { - pattern2 = mystrrep(mystrdup(piece), "_", " "); + pattern2.assign(start_piece, iter); break; } default: break; } - i++; + ++i; } - piece = mystrsep(&tp, 0); + start_piece = mystrsep(nl, iter); } - if (!pattern || !pattern2) { - if (pattern) - free(pattern); - if (pattern2) - free(pattern2); + if (pattern.empty() || pattern2.empty()) { HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); - return 1; + return false; } (*rl)->add(pattern, pattern2); } - return 0; + return true; } /* parse in the typical fault correcting table */ -int AffixMgr::parse_phonetable(char* line, FileMgr* af) { +bool AffixMgr::parse_phonetable(const std::string& line, FileMgr* af) { if (phone) { HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); - return 1; + return false; } - char* tp = line; - char* piece; + int num = -1; int i = 0; int np = 0; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - np++; - break; - } - case 1: { - phone = (phonetable*)malloc(sizeof(struct phonetable)); - if (!phone) - return 1; - phone->num = atoi(piece); - phone->rules = NULL; - phone->utf8 = (char)utf8; - if (phone->num < 1) { - HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", - af->getlinenum()); - return 1; - } - phone->rules = (char**)malloc(2 * (phone->num + 1) * sizeof(char*)); - if (!phone->rules) { - free(phone); - phone = NULL; - return 1; - } - np++; - break; - } - default: - break; + std::string::const_iterator iter = line.begin(); + std::string::const_iterator start_piece = mystrsep(line, iter); + while (start_piece != line.end()) { + switch (i) { + case 0: { + np++; + break; } - i++; + case 1: { + num = atoi(std::string(start_piece, iter).c_str()); + if (num < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", + af->getlinenum()); + return false; + } + phone = new phonetable; + phone->utf8 = (char)utf8; + np++; + break; + } + default: + break; } - piece = mystrsep(&tp, 0); + ++i; + start_piece = mystrsep(line, iter); } if (np != 2) { HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); - return 1; + return false; } /* now parse the phone->num lines to read in the remainder of the table */ - char* nl; - for (int j = 0; j < phone->num; j++) { - if (!(nl = af->getline())) - return 1; + for (int j = 0; j < num; ++j) { + std::string nl; + if (!af->getline(nl)) + return false; mychomp(nl); - tp = nl; i = 0; - phone->rules[j * 2] = NULL; - phone->rules[j * 2 + 1] = NULL; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { + const size_t old_size = phone->rules.size(); + iter = nl.begin(); + start_piece = mystrsep(nl, iter); + while (start_piece != nl.end()) { + { switch (i) { case 0: { - if (strncmp(piece, "PHONE", 5) != 0) { + if (nl.compare(start_piece - nl.begin(), 5, "PHONE", 5) != 0) { HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); - phone->num = 0; - return 1; + return false; } break; } case 1: { - phone->rules[j * 2] = mystrrep(mystrdup(piece), "_", ""); + phone->rules.push_back(std::string(start_piece, iter)); break; } case 2: { - phone->rules[j * 2 + 1] = mystrrep(mystrdup(piece), "_", ""); + phone->rules.push_back(std::string(start_piece, iter)); + mystrrep(phone->rules.back(), "_", ""); break; } default: break; } - i++; + ++i; } - piece = mystrsep(&tp, 0); + start_piece = mystrsep(nl, iter); } - if ((!(phone->rules[j * 2])) || (!(phone->rules[j * 2 + 1]))) { + if (phone->rules.size() != old_size + 2) { HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); - phone->num = 0; - return 1; + phone->rules.clear(); + return false; } } - phone->rules[phone->num * 2] = mystrdup(""); - phone->rules[phone->num * 2 + 1] = mystrdup(""); + phone->rules.push_back(""); + phone->rules.push_back(""); init_phonet_hash(*phone); - return 0; + return true; } /* parse in the checkcompoundpattern table */ -int AffixMgr::parse_checkcpdtable(char* line, FileMgr* af) { - if (numcheckcpd != 0) { +bool AffixMgr::parse_checkcpdtable(const std::string& line, FileMgr* af) { + if (parsedcheckcpd) { HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); - return 1; + return false; } - char* tp = line; - char* piece; + parsedcheckcpd = true; + int numcheckcpd = -1; int i = 0; int np = 0; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - np++; - break; - } - case 1: { - numcheckcpd = atoi(piece); - if (numcheckcpd < 1) { - HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", - af->getlinenum()); - return 1; - } - checkcpdtable = - (patentry*)malloc(numcheckcpd * sizeof(struct patentry)); - if (!checkcpdtable) - return 1; - np++; - break; - } - default: - break; + std::string::const_iterator iter = line.begin(); + std::string::const_iterator start_piece = mystrsep(line, iter); + while (start_piece != line.end()) { + switch (i) { + case 0: { + np++; + break; } - i++; + case 1: { + numcheckcpd = atoi(std::string(start_piece, iter).c_str()); + if (numcheckcpd < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", + af->getlinenum()); + return false; + } + checkcpdtable.reserve(numcheckcpd); + np++; + break; + } + default: + break; } - piece = mystrsep(&tp, 0); + ++i; + start_piece = mystrsep(line, iter); } if (np != 2) { HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); - return 1; + return false; } /* now parse the numcheckcpd lines to read in the remainder of the table */ - char* nl; - for (int j = 0; j < numcheckcpd; j++) { - if (!(nl = af->getline())) - return 1; + for (int j = 0; j < numcheckcpd; ++j) { + std::string nl; + if (!af->getline(nl)) + return false; mychomp(nl); - tp = nl; i = 0; - checkcpdtable[j].pattern = NULL; - checkcpdtable[j].pattern2 = NULL; - checkcpdtable[j].pattern3 = NULL; - checkcpdtable[j].cond = FLAG_NULL; - checkcpdtable[j].cond2 = FLAG_NULL; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - if (strncmp(piece, "CHECKCOMPOUNDPATTERN", 20) != 0) { - HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", - af->getlinenum()); - numcheckcpd = 0; - return 1; - } - break; + checkcpdtable.push_back(patentry()); + iter = nl.begin(); + start_piece = mystrsep(nl, iter); + while (start_piece != nl.end()) { + switch (i) { + case 0: { + if (nl.compare(start_piece - nl.begin(), 20, "CHECKCOMPOUNDPATTERN", 20) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", + af->getlinenum()); + return false; } - case 1: { - checkcpdtable[j].pattern = mystrdup(piece); - char* p = strchr(checkcpdtable[j].pattern, '/'); - if (p) { - *p = '\0'; - checkcpdtable[j].cond = pHMgr->decode_flag(p + 1); - } - break; - } - case 2: { - checkcpdtable[j].pattern2 = mystrdup(piece); - char* p = strchr(checkcpdtable[j].pattern2, '/'); - if (p) { - *p = '\0'; - checkcpdtable[j].cond2 = pHMgr->decode_flag(p + 1); - } - break; - } - case 3: { - checkcpdtable[j].pattern3 = mystrdup(piece); - simplifiedcpd = 1; - break; - } - default: - break; + break; } - i++; + case 1: { + checkcpdtable.back().pattern.assign(start_piece, iter); + size_t slash_pos = checkcpdtable.back().pattern.find('/'); + if (slash_pos != std::string::npos) { + std::string chunk(checkcpdtable.back().pattern, slash_pos + 1); + checkcpdtable.back().pattern.resize(slash_pos); + checkcpdtable.back().cond = pHMgr->decode_flag(chunk.c_str()); + } + break; + } + case 2: { + checkcpdtable.back().pattern2.assign(start_piece, iter); + size_t slash_pos = checkcpdtable.back().pattern2.find('/'); + if (slash_pos != std::string::npos) { + std::string chunk(checkcpdtable.back().pattern2, slash_pos + 1); + checkcpdtable.back().pattern2.resize(slash_pos); + checkcpdtable.back().cond2 = pHMgr->decode_flag(chunk.c_str()); + } + break; + } + case 3: { + checkcpdtable.back().pattern3.assign(start_piece, iter); + simplifiedcpd = 1; + break; + } + default: + break; } - piece = mystrsep(&tp, 0); - } - if ((!(checkcpdtable[j].pattern)) || (!(checkcpdtable[j].pattern2))) { - HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", - af->getlinenum()); - numcheckcpd = 0; - return 1; + i++; + start_piece = mystrsep(nl, iter); } } - return 0; + return true; } /* parse in the compound rule table */ -int AffixMgr::parse_defcpdtable(char* line, FileMgr* af) { - if (numdefcpd != 0) { +bool AffixMgr::parse_defcpdtable(const std::string& line, FileMgr* af) { + if (parseddefcpd) { HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); - return 1; + return false; } - char* tp = line; - char* piece; + parseddefcpd = true; + int numdefcpd = -1; int i = 0; int np = 0; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - np++; - break; - } - case 1: { - numdefcpd = atoi(piece); - if (numdefcpd < 1) { - HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", - af->getlinenum()); - return 1; - } - defcpdtable = (flagentry*)malloc(numdefcpd * sizeof(flagentry)); - if (!defcpdtable) - return 1; - np++; - break; - } - default: - break; + std::string::const_iterator iter = line.begin(); + std::string::const_iterator start_piece = mystrsep(line, iter); + while (start_piece != line.end()) { + switch (i) { + case 0: { + np++; + break; } - i++; + case 1: { + numdefcpd = atoi(std::string(start_piece, iter).c_str()); + if (numdefcpd < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", + af->getlinenum()); + return false; + } + defcpdtable.reserve(numdefcpd); + np++; + break; + } + default: + break; } - piece = mystrsep(&tp, 0); + ++i; + start_piece = mystrsep(line, iter); } if (np != 2) { HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); - return 1; + return false; } /* now parse the numdefcpd lines to read in the remainder of the table */ - char* nl; - for (int j = 0; j < numdefcpd; j++) { - if (!(nl = af->getline())) - return 1; + for (int j = 0; j < numdefcpd; ++j) { + std::string nl; + if (!af->getline(nl)) + return false; mychomp(nl); - tp = nl; i = 0; - defcpdtable[j].def = NULL; - defcpdtable[j].len = 0; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - if (strncmp(piece, "COMPOUNDRULE", 12) != 0) { - HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", - af->getlinenum()); - numdefcpd = 0; - return 1; - } - break; + defcpdtable.push_back(flagentry()); + iter = nl.begin(); + start_piece = mystrsep(nl, iter); + while (start_piece != nl.end()) { + switch (i) { + case 0: { + if (nl.compare(start_piece - nl.begin(), 12, "COMPOUNDRULE", 12) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", + af->getlinenum()); + numdefcpd = 0; + return false; } - case 1: { // handle parenthesized flags - if (strchr(piece, '(')) { - defcpdtable[j].def = (FLAG*)malloc(strlen(piece) * sizeof(FLAG)); - defcpdtable[j].len = 0; - int end = 0; - FLAG* conv; - while (!end) { - char* par = piece + 1; - while (*par != '(' && *par != ')' && *par != '\0') - par++; - if (*par == '\0') - end = 1; - else - *par = '\0'; - if (*piece == '(') - piece++; - if (*piece == '*' || *piece == '?') { - defcpdtable[j].def[defcpdtable[j].len++] = (FLAG)*piece; - } else if (*piece != '\0') { - int l = pHMgr->decode_flags(&conv, piece, af); - for (int k = 0; k < l; k++) - defcpdtable[j].def[defcpdtable[j].len++] = conv[k]; - free(conv); - } - piece = par + 1; - } - } else { - defcpdtable[j].len = - pHMgr->decode_flags(&(defcpdtable[j].def), piece, af); - } - break; - } - default: - break; + break; } - i++; + case 1: { // handle parenthesized flags + if (std::find(start_piece, iter, '(') != iter) { + for (std::string::const_iterator k = start_piece; k != iter; ++k) { + std::string::const_iterator chb = k; + std::string::const_iterator che = k + 1; + if (*k == '(') { + std::string::const_iterator parpos = std::find(k, iter, ')'); + if (parpos != iter) { + chb = k + 1; + che = parpos; + k = parpos; + } + } + + if (*chb == '*' || *chb == '?') { + defcpdtable.back().push_back((FLAG)*chb); + } else { + pHMgr->decode_flags(defcpdtable.back(), std::string(chb, che), af); + } + } + } else { + pHMgr->decode_flags(defcpdtable.back(), std::string(start_piece, iter), af); + } + break; + } + default: + break; } - piece = mystrsep(&tp, 0); + ++i; + start_piece = mystrsep(nl, iter); } - if (!defcpdtable[j].len) { + if (defcpdtable.back().empty()) { HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); - numdefcpd = 0; - return 1; + return false; } } - return 0; + return true; } /* parse in the character map table */ -int AffixMgr::parse_maptable(char* line, FileMgr* af) { - if (nummap != 0) { +bool AffixMgr::parse_maptable(const std::string& line, FileMgr* af) { + if (parsedmaptable) { HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); - return 1; + return false; } - char* tp = line; - char* piece; + parsedmaptable = true; + int nummap = -1; int i = 0; int np = 0; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - np++; - break; - } - case 1: { - nummap = atoi(piece); - if (nummap < 1) { - HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", - af->getlinenum()); - return 1; - } - maptable = (mapentry*)malloc(nummap * sizeof(struct mapentry)); - if (!maptable) - return 1; - np++; - break; - } - default: - break; + std::string::const_iterator iter = line.begin(); + std::string::const_iterator start_piece = mystrsep(line, iter); + while (start_piece != line.end()) { + switch (i) { + case 0: { + np++; + break; } - i++; + case 1: { + nummap = atoi(std::string(start_piece, iter).c_str()); + if (nummap < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", + af->getlinenum()); + return false; + } + maptable.reserve(nummap); + np++; + break; + } + default: + break; } - piece = mystrsep(&tp, 0); + ++i; + start_piece = mystrsep(line, iter); } if (np != 2) { HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); - return 1; + return false; } /* now parse the nummap lines to read in the remainder of the table */ - char* nl; - for (int j = 0; j < nummap; j++) { - if (!(nl = af->getline())) - return 1; + for (int j = 0; j < nummap; ++j) { + std::string nl; + if (!af->getline(nl)) + return false; mychomp(nl); - tp = nl; i = 0; - maptable[j].set = NULL; - maptable[j].len = 0; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - if (strncmp(piece, "MAP", 3) != 0) { - HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", - af->getlinenum()); - nummap = 0; - return 1; - } - break; - } - case 1: { - int setn = 0; - maptable[j].len = strlen(piece); - maptable[j].set = (char**)malloc(maptable[j].len * sizeof(char*)); - if (!maptable[j].set) - return 1; - for (int k = 0; k < maptable[j].len; k++) { - int chl = 1; - int chb = k; - if (piece[k] == '(') { - char* parpos = strchr(piece + k, ')'); - if (parpos != NULL) { - chb = k + 1; - chl = (int)(parpos - piece) - k - 1; - k = k + chl + 1; - } - } else { - if (utf8 && (piece[k] & 0xc0) == 0xc0) { - for (k++; utf8 && (piece[k] & 0xc0) == 0x80; k++) - ; - chl = k - chb; - k--; - } - } - maptable[j].set[setn] = (char*)malloc(chl + 1); - if (!maptable[j].set[setn]) - return 1; - strncpy(maptable[j].set[setn], piece + chb, chl); - maptable[j].set[setn][chl] = '\0'; - setn++; - } - maptable[j].len = setn; - break; - } - default: - break; - } - i++; - } - piece = mystrsep(&tp, 0); - } - if (!maptable[j].set || !maptable[j].len) { - HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", - af->getlinenum()); - nummap = 0; - return 1; - } - } - return 0; -} - -/* parse in the word breakpoint table */ -int AffixMgr::parse_breaktable(char* line, FileMgr* af) { - if (numbreak > -1) { - HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", - af->getlinenum()); - return 1; - } - char* tp = line; - char* piece; - int i = 0; - int np = 0; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { + maptable.push_back(mapentry()); + iter = nl.begin(); + start_piece = mystrsep(nl, iter); + while (start_piece != nl.end()) { switch (i) { case 0: { - np++; + if (nl.compare(start_piece - nl.begin(), 3, "MAP", 3) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", + af->getlinenum()); + nummap = 0; + return false; + } break; } case 1: { - numbreak = atoi(piece); - if (numbreak < 0) { - HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", - af->getlinenum()); - return 1; + for (std::string::const_iterator k = start_piece; k != iter; ++k) { + std::string::const_iterator chb = k; + std::string::const_iterator che = k + 1; + if (*k == '(') { + std::string::const_iterator parpos = std::find(k, iter, ')'); + if (parpos != iter) { + chb = k + 1; + che = parpos; + k = parpos; + } + } else { + if (utf8 && (*k & 0xc0) == 0xc0) { + ++k; + while (k != iter && (*k & 0xc0) == 0x80) + ++k; + che = k; + --k; + } + } + maptable.back().push_back(std::string(chb, che)); } - if (numbreak == 0) - return 0; - breaktable = (char**)malloc(numbreak * sizeof(char*)); - if (!breaktable) - return 1; - np++; break; } default: break; } - i++; + ++i; + start_piece = mystrsep(nl, iter); } - piece = mystrsep(&tp, 0); + if (maptable.back().empty()) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", + af->getlinenum()); + return false; + } + } + return true; +} + +/* parse in the word breakpoint table */ +bool AffixMgr::parse_breaktable(const std::string& line, FileMgr* af) { + if (parsedbreaktable) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", + af->getlinenum()); + return false; + } + parsedbreaktable = true; + int numbreak = -1; + int i = 0; + int np = 0; + std::string::const_iterator iter = line.begin(); + std::string::const_iterator start_piece = mystrsep(line, iter); + while (start_piece != line.end()) { + switch (i) { + case 0: { + np++; + break; + } + case 1: { + numbreak = atoi(std::string(start_piece, iter).c_str()); + if (numbreak < 0) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", + af->getlinenum()); + return false; + } + if (numbreak == 0) + return true; + breaktable.reserve(numbreak); + np++; + break; + } + default: + break; + } + ++i; + start_piece = mystrsep(line, iter); } if (np != 2) { HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); - return 1; + return false; } /* now parse the numbreak lines to read in the remainder of the table */ - char* nl; - for (int j = 0; j < numbreak; j++) { - if (!(nl = af->getline())) - return 1; + for (int j = 0; j < numbreak; ++j) { + std::string nl; + if (!af->getline(nl)) + return false; mychomp(nl); - tp = nl; i = 0; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - if (strncmp(piece, "BREAK", 5) != 0) { - HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", - af->getlinenum()); - numbreak = 0; - return 1; - } - break; + iter = nl.begin(); + start_piece = mystrsep(nl, iter); + while (start_piece != nl.end()) { + switch (i) { + case 0: { + if (nl.compare(start_piece - nl.begin(), 5, "BREAK", 5) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", + af->getlinenum()); + numbreak = 0; + return false; } - case 1: { - breaktable[j] = mystrdup(piece); - break; - } - default: - break; + break; } - i++; + case 1: { + breaktable.push_back(std::string(start_piece, iter)); + break; + } + default: + break; } - piece = mystrsep(&tp, 0); - } - if (!breaktable) { - HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", - af->getlinenum()); - numbreak = 0; - return 1; + ++i; + start_piece = mystrsep(nl, iter); } } - return 0; + + if (breaktable.size() != static_cast(numbreak)) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", + af->getlinenum()); + return false; + } + + return true; } void AffixMgr::reverse_condition(std::string& piece) { @@ -4665,20 +4403,68 @@ void AffixMgr::reverse_condition(std::string& piece) { } } -int AffixMgr::parse_affix(char* line, +class entries_container { + std::vector entries; + AffixMgr* m_mgr; + char m_at; +public: + entries_container(char at, AffixMgr* mgr) + : m_mgr(mgr) + , m_at(at) { + } + void release() { + entries.clear(); + } + void initialize(int numents, + char opts, unsigned short aflag) { + entries.reserve(numents); + + if (m_at == 'P') { + entries.push_back(new PfxEntry(m_mgr)); + } else { + entries.push_back(new SfxEntry(m_mgr)); + } + + entries.back()->opts = opts; + entries.back()->aflag = aflag; + } + + AffEntry* add_entry(char opts) { + if (m_at == 'P') { + entries.push_back(new PfxEntry(m_mgr)); + } else { + entries.push_back(new SfxEntry(m_mgr)); + } + AffEntry* ret = entries.back(); + ret->opts = entries[0]->opts & opts; + return ret; + } + + AffEntry* first_entry() { + return entries.empty() ? NULL : entries[0]; + } + + ~entries_container() { + for (size_t i = 0; i < entries.size(); ++i) { + delete entries[i]; + } + } + + std::vector::iterator begin() { return entries.begin(); } + std::vector::iterator end() { return entries.end(); } +}; + +bool AffixMgr::parse_affix(const std::string& line, const char at, FileMgr* af, char* dupflags) { - int numents = 0; // number of affentry structures to parse + int numents = 0; // number of AffEntry structures to parse unsigned short aflag = 0; // affix char identifier char ff = 0; - std::vector affentries; + entries_container affentries(at, this); - char* tp = line; - char* nl = line; - char* piece; int i = 0; // checking lines with bad syntax @@ -4689,71 +4475,68 @@ int AffixMgr::parse_affix(char* line, // split affix header line into pieces int np = 0; - - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - // piece 1 - is type of affix - case 0: { - np++; - break; - } - - // piece 2 - is affix char - case 1: { - np++; - aflag = pHMgr->decode_flag(piece); - if (((at == 'S') && (dupflags[aflag] & dupSFX)) || - ((at == 'P') && (dupflags[aflag] & dupPFX))) { - HUNSPELL_WARNING( - stderr, - "error: line %d: multiple definitions of an affix flag\n", - af->getlinenum()); - // return 1; XXX permissive mode for bad dictionaries - } - dupflags[aflag] += (char)((at == 'S') ? dupSFX : dupPFX); - break; - } - // piece 3 - is cross product indicator - case 2: { - np++; - if (*piece == 'Y') - ff = aeXPRODUCT; - break; - } - - // piece 4 - is number of affentries - case 3: { - np++; - numents = atoi(piece); - if ((numents <= 0) || ((std::numeric_limits::max() / - sizeof(struct affentry)) < static_cast(numents))) { - char* err = pHMgr->encode_flag(aflag); - if (err) { - HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", - af->getlinenum()); - free(err); - } - return 1; - } - affentries.resize(numents); - affentries[0].opts = ff; - if (utf8) - affentries[0].opts += aeUTF8; - if (pHMgr->is_aliasf()) - affentries[0].opts += aeALIASF; - if (pHMgr->is_aliasm()) - affentries[0].opts += aeALIASM; - affentries[0].aflag = aflag; - } - - default: - break; + std::string::const_iterator iter = line.begin(); + std::string::const_iterator start_piece = mystrsep(line, iter); + while (start_piece != line.end()) { + switch (i) { + // piece 1 - is type of affix + case 0: { + np++; + break; } - i++; + + // piece 2 - is affix char + case 1: { + np++; + aflag = pHMgr->decode_flag(std::string(start_piece, iter).c_str()); + if (((at == 'S') && (dupflags[aflag] & dupSFX)) || + ((at == 'P') && (dupflags[aflag] & dupPFX))) { + HUNSPELL_WARNING( + stderr, + "error: line %d: multiple definitions of an affix flag\n", + af->getlinenum()); + } + dupflags[aflag] += (char)((at == 'S') ? dupSFX : dupPFX); + break; + } + // piece 3 - is cross product indicator + case 2: { + np++; + if (*start_piece == 'Y') + ff = aeXPRODUCT; + break; + } + + // piece 4 - is number of affentries + case 3: { + np++; + numents = atoi(std::string(start_piece, iter).c_str()); + if ((numents <= 0) || ((std::numeric_limits::max() / + sizeof(AffEntry)) < static_cast(numents))) { + char* err = pHMgr->encode_flag(aflag); + if (err) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", + af->getlinenum()); + free(err); + } + return false; + } + + char opts = ff; + if (utf8) + opts += aeUTF8; + if (pHMgr->is_aliasf()) + opts += aeALIASF; + if (pHMgr->is_aliasm()) + opts += aeALIASM; + affentries.initialize(numents, opts, aflag); + } + + default: + break; } - piece = mystrsep(&tp, 0); + ++i; + start_piece = mystrsep(line, iter); } // check to make sure we parsed enough pieces if (np != 4) { @@ -4763,196 +4546,193 @@ int AffixMgr::parse_affix(char* line, af->getlinenum()); free(err); } - return 1; + return false; } // now parse numents affentries for this affix - std::vector::iterator start = affentries.begin(); - std::vector::iterator end = affentries.end(); - for (std::vector::iterator entry = start; entry != end; ++entry) { - if ((nl = af->getline()) == NULL) - return 1; + AffEntry* entry = affentries.first_entry(); + for (int ent = 0; ent < numents; ++ent) { + std::string nl; + if (!af->getline(nl)) + return false; mychomp(nl); - tp = nl; + + iter = nl.begin(); i = 0; np = 0; // split line into pieces - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - // piece 1 - is type - case 0: { - np++; - if (entry != start) - entry->opts = start->opts & - (char)(aeXPRODUCT + aeUTF8 + aeALIASF + aeALIASM); - break; + start_piece = mystrsep(nl, iter); + while (start_piece != nl.end()) { + switch (i) { + // piece 1 - is type + case 0: { + np++; + if (ent != 0) + entry = affentries.add_entry((char)(aeXPRODUCT + aeUTF8 + aeALIASF + aeALIASM)); + break; + } + + // piece 2 - is affix char + case 1: { + np++; + std::string chunk(start_piece, iter); + if (pHMgr->decode_flag(chunk.c_str()) != aflag) { + char* err = pHMgr->encode_flag(aflag); + if (err) { + HUNSPELL_WARNING(stderr, + "error: line %d: affix %s is corrupt\n", + af->getlinenum(), err); + free(err); + } + return false; } - // piece 2 - is affix char - case 1: { - np++; - if (pHMgr->decode_flag(piece) != aflag) { - char* err = pHMgr->encode_flag(aflag); - if (err) { - HUNSPELL_WARNING(stderr, - "error: line %d: affix %s is corrupt\n", - af->getlinenum(), err); - free(err); + if (ent != 0) { + AffEntry* start_entry = affentries.first_entry(); + entry->aflag = start_entry->aflag; + } + break; + } + + // piece 3 - is string to strip or 0 for null + case 2: { + np++; + entry->strip = std::string(start_piece, iter); + if (complexprefixes) { + if (utf8) + reverseword_utf(entry->strip); + else + reverseword(entry->strip); + } + if (entry->strip.compare("0") == 0) { + entry->strip.clear(); + } + break; + } + + // piece 4 - is affix string or 0 for null + case 3: { + entry->morphcode = NULL; + entry->contclass = NULL; + entry->contclasslen = 0; + np++; + std::string::const_iterator dash = std::find(start_piece, iter, '/'); + if (dash != iter) { + entry->appnd = std::string(start_piece, dash); + std::string dash_str(dash + 1, iter); + + if (!ignorechars.empty()) { + if (utf8) { + remove_ignored_chars_utf(entry->appnd, ignorechars_utf16); + } else { + remove_ignored_chars(entry->appnd, ignorechars); } - return 1; } - if (entry != start) - entry->aflag = start->aflag; - break; - } - - // piece 3 - is string to strip or 0 for null - case 2: { - np++; - entry->strip = piece; if (complexprefixes) { if (utf8) - reverseword_utf(entry->strip); + reverseword_utf(entry->appnd); else - reverseword(entry->strip); + reverseword(entry->appnd); } - if (entry->strip.compare("0") == 0) { - entry->strip.clear(); - } - break; - } - // piece 4 - is affix string or 0 for null - case 3: { - char* dash; - entry->morphcode = NULL; - entry->contclass = NULL; - entry->contclasslen = 0; - np++; - dash = strchr(piece, '/'); - if (dash) { - *dash = '\0'; - - entry->appnd = piece; - - if (ignorechars) { - if (utf8) { - remove_ignored_chars_utf(entry->appnd, ignorechars_utf16); - } else { - remove_ignored_chars(entry->appnd, ignorechars); - } - } - - if (complexprefixes) { - if (utf8) - reverseword_utf(entry->appnd); - else - reverseword(entry->appnd); - } - - if (pHMgr->is_aliasf()) { - int index = atoi(dash + 1); - entry->contclasslen = (unsigned short)pHMgr->get_aliasf( - index, &(entry->contclass), af); - if (!entry->contclasslen) - HUNSPELL_WARNING(stderr, - "error: bad affix flag alias: \"%s\"\n", - dash + 1); - } else { - entry->contclasslen = (unsigned short)pHMgr->decode_flags( - &(entry->contclass), dash + 1, af); - std::sort(entry->contclass, entry->contclass + entry->contclasslen); - } - *dash = '/'; - - havecontclass = 1; - for (unsigned short _i = 0; _i < entry->contclasslen; _i++) { - contclasses[(entry->contclass)[_i]] = 1; - } + if (pHMgr->is_aliasf()) { + int index = atoi(dash_str.c_str()); + entry->contclasslen = (unsigned short)pHMgr->get_aliasf( + index, &(entry->contclass), af); + if (!entry->contclasslen) + HUNSPELL_WARNING(stderr, + "error: bad affix flag alias: \"%s\"\n", + dash_str.c_str()); } else { - entry->appnd = piece; + entry->contclasslen = (unsigned short)pHMgr->decode_flags( + &(entry->contclass), dash_str.c_str(), af); + std::sort(entry->contclass, entry->contclass + entry->contclasslen); + } - if (ignorechars) { - if (utf8) { - remove_ignored_chars_utf(entry->appnd, ignorechars_utf16); - } else { - remove_ignored_chars(entry->appnd, ignorechars); - } - } + havecontclass = 1; + for (unsigned short _i = 0; _i < entry->contclasslen; _i++) { + contclasses[(entry->contclass)[_i]] = 1; + } + } else { + entry->appnd = std::string(start_piece, iter); - if (complexprefixes) { - if (utf8) - reverseword_utf(entry->appnd); - else - reverseword(entry->appnd); + if (!ignorechars.empty()) { + if (utf8) { + remove_ignored_chars_utf(entry->appnd, ignorechars_utf16); + } else { + remove_ignored_chars(entry->appnd, ignorechars); } } - if (entry->appnd.compare("0") == 0) { - entry->appnd.clear(); + if (complexprefixes) { + if (utf8) + reverseword_utf(entry->appnd); + else + reverseword(entry->appnd); } - break; } - // piece 5 - is the conditions descriptions - case 4: { - std::string chunk(piece); - np++; - if (complexprefixes) { + if (entry->appnd.compare("0") == 0) { + entry->appnd.clear(); + } + break; + } + + // piece 5 - is the conditions descriptions + case 4: { + std::string chunk(start_piece, iter); + np++; + if (complexprefixes) { + if (utf8) + reverseword_utf(chunk); + else + reverseword(chunk); + reverse_condition(chunk); + } + if (!entry->strip.empty() && chunk != "." && + redundant_condition(at, entry->strip.c_str(), entry->strip.size(), chunk.c_str(), + af->getlinenum())) + chunk = "."; + if (at == 'S') { + reverseword(chunk); + reverse_condition(chunk); + } + if (encodeit(*entry, chunk.c_str())) + return false; + break; + } + + case 5: { + std::string chunk(start_piece, iter); + np++; + if (pHMgr->is_aliasm()) { + int index = atoi(chunk.c_str()); + entry->morphcode = pHMgr->get_aliasm(index); + } else { + if (complexprefixes) { // XXX - fix me for morph. gen. if (utf8) reverseword_utf(chunk); else reverseword(chunk); - reverse_condition(chunk); } - if (!entry->strip.empty() && chunk != "." && - redundant_condition(at, entry->strip.c_str(), entry->strip.size(), chunk.c_str(), - af->getlinenum())) - chunk = "."; - if (at == 'S') { - reverseword(chunk); - reverse_condition(chunk); + // add the remaining of the line + std::string::const_iterator end = nl.end(); + if (iter != end) { + chunk.append(iter, end); } - if (encodeit(*entry, chunk.c_str())) - return 1; - break; + entry->morphcode = mystrdup(chunk.c_str()); + if (!entry->morphcode) + return false; } - - case 5: { - std::string chunk(piece); - np++; - if (pHMgr->is_aliasm()) { - int index = atoi(chunk.c_str()); - entry->morphcode = pHMgr->get_aliasm(index); - } else { - if (complexprefixes) { // XXX - fix me for morph. gen. - if (utf8) - reverseword_utf(chunk); - else - reverseword(chunk); - } - // add the remaining of the line - if (*tp) { - *(tp - 1) = ' '; - chunk.push_back(' '); - chunk.append(tp); - } - entry->morphcode = mystrdup(chunk.c_str()); - if (!entry->morphcode) - return 1; - } - break; - } - default: - break; + break; } - i++; + default: + break; } - piece = mystrsep(&tp, 0); + i++; + start_piece = mystrsep(nl, iter); } // check to make sure we parsed enough pieces if (np < 4) { @@ -4962,7 +4742,7 @@ int AffixMgr::parse_affix(char* line, af->getlinenum(), err); free(err); } - return 1; + return false; } #ifdef DEBUG @@ -4982,16 +4762,20 @@ int AffixMgr::parse_affix(char* line, // now create SfxEntry or PfxEntry objects and use links to // build an ordered (sorted by affix string) list - for (std::vector::iterator entry = start; entry != end; ++entry) { + std::vector::iterator start = affentries.begin(); + std::vector::iterator end = affentries.end(); + for (std::vector::iterator affentry = start; affentry != end; ++affentry) { if (at == 'P') { - PfxEntry* pfxptr = new PfxEntry(this, &(*entry)); - build_pfxtree(pfxptr); + build_pfxtree(static_cast(*affentry)); } else { - SfxEntry* sfxptr = new SfxEntry(this, &(*entry)); - build_sfxtree(sfxptr); + build_sfxtree(static_cast(*affentry)); } } - return 0; + + //contents belong to AffixMgr now + affentries.release(); + + return true; } int AffixMgr::redundant_condition(char ft, @@ -5088,11 +4872,10 @@ int AffixMgr::redundant_condition(char ft, return 0; } -int AffixMgr::get_suffix_words(short unsigned* suff, +std::vector AffixMgr::get_suffix_words(short unsigned* suff, int len, - const char* root_word, - char** slst) { - int suff_words_cnt = 0; + const char* root_word) { + std::vector slst; short unsigned* start_ptr = suff; for (int j = 0; j < SETSIZE; j++) { SfxEntry* ptr = sStart[j]; @@ -5102,10 +4885,9 @@ int AffixMgr::get_suffix_words(short unsigned* suff, if ((*suff) == ptr->getFlag()) { std::string nw(root_word); nw.append(ptr->getAffix()); - hentry* ht = ptr->checkword(nw.c_str(), nw.size(), 0, NULL, NULL, 0, - NULL, 0, 0, 0); + hentry* ht = ptr->checkword(nw.c_str(), nw.size(), 0, NULL, 0, 0, 0); if (ht) { - slst[suff_words_cnt++] = mystrdup(nw.c_str()); + slst.push_back(nw); } } suff++; @@ -5113,5 +4895,5 @@ int AffixMgr::get_suffix_words(short unsigned* suff, ptr = ptr->getNext(); } } - return suff_words_cnt; + return slst; } diff --git a/extensions/spellcheck/hunspell/src/affixmgr.hxx b/extensions/spellcheck/hunspell/src/affixmgr.hxx index d70e853388ad..83a4b42c3332 100644 --- a/extensions/spellcheck/hunspell/src/affixmgr.hxx +++ b/extensions/spellcheck/hunspell/src/affixmgr.hxx @@ -71,14 +71,13 @@ * SUCH DAMAGE. */ -#ifndef _AFFIXMGR_HXX_ -#define _AFFIXMGR_HXX_ - -#include "hunvisapi.h" +#ifndef AFFIXMGR_HXX_ +#define AFFIXMGR_HXX_ #include #include +#include #include "atypes.hxx" #include "baseaffix.hxx" @@ -93,17 +92,16 @@ class PfxEntry; class SfxEntry; -class LIBHUNSPELL_DLL_EXPORTED AffixMgr { +class AffixMgr { PfxEntry* pStart[SETSIZE]; SfxEntry* sStart[SETSIZE]; PfxEntry* pFlag[SETSIZE]; SfxEntry* sFlag[SETSIZE]; - HashMgr* pHMgr; - HashMgr** alldic; - int* maxdic; - char* keystring; - char* trystring; - char* encoding; + const std::vector& alldic; + const HashMgr* pHMgr; + std::string keystring; + std::string trystring; + std::string encoding; struct cs_info* csconv; int utf8; int complexprefixes; @@ -125,19 +123,19 @@ class LIBHUNSPELL_DLL_EXPORTED AffixMgr { FLAG nongramsuggest; FLAG needaffix; int cpdmin; - int numrep; - replentry* reptable; + bool parsedrep; + std::vector reptable; RepList* iconvtable; RepList* oconvtable; - int nummap; - mapentry* maptable; - int numbreak; - char** breaktable; - int numcheckcpd; - patentry* checkcpdtable; + bool parsedmaptable; + std::vector maptable; + bool parsedbreaktable; + std::vector breaktable; + bool parsedcheckcpd; + std::vector checkcpdtable; int simplifiedcpd; - int numdefcpd; - flagentry* defcpdtable; + bool parseddefcpd; + std::vector defcpdtable; phonetable* phone; int maxngramsugs; int maxcpdsugs; @@ -147,10 +145,9 @@ class LIBHUNSPELL_DLL_EXPORTED AffixMgr { int sugswithdots; int cpdwordmax; int cpdmaxsyllable; - char* cpdvowels; - w_char* cpdvowels_utf16; - int cpdvowels_utf16_len; - char* cpdsyllablenum; + std::string cpdvowels; // vowels (for calculating of Hungarian compounding limit, + std::vector cpdvowels_utf16; //vowels for UTF-8 encoding + std::string cpdsyllablenum; // syllable count incrementing flag const char* pfxappnd; // BUG: not stateless const char* sfxappnd; // BUG: not stateless int sfxextra; // BUG: not stateless @@ -159,12 +156,12 @@ class LIBHUNSPELL_DLL_EXPORTED AffixMgr { SfxEntry* sfx; // BUG: not stateless PfxEntry* pfx; // BUG: not stateless int checknum; - char* wordchars; + std::string wordchars; // letters + spec. word characters std::vector wordchars_utf16; - char* ignorechars; + std::string ignorechars; // letters + spec. word characters std::vector ignorechars_utf16; - char* version; - char* lang; + std::string version; // affix and dictionary file version string + std::string lang; // language int langnum; FLAG lemma_present; FLAG circumfix; @@ -182,7 +179,7 @@ class LIBHUNSPELL_DLL_EXPORTED AffixMgr { // affix) public: - AffixMgr(const char* affpath, HashMgr** ptr, int* md, const char* key = NULL); + AffixMgr(const char* affpath, const std::vector& ptr, const char* key = NULL); ~AffixMgr(); struct hentry* affix_check(const char* word, int len, @@ -202,9 +199,6 @@ class LIBHUNSPELL_DLL_EXPORTED AffixMgr { int len, int sfxopts, PfxEntry* ppfx, - char** wlst, - int maxSug, - int* ns, const FLAG cclass = FLAG_NULL, const FLAG needflag = FLAG_NULL, char in_compound = IN_CPD_NOT); @@ -214,39 +208,39 @@ class LIBHUNSPELL_DLL_EXPORTED AffixMgr { PfxEntry* ppfx, const FLAG needflag = FLAG_NULL); - char* affix_check_morph(const char* word, - int len, - const FLAG needflag = FLAG_NULL, - char in_compound = IN_CPD_NOT); - char* prefix_check_morph(const char* word, - int len, - char in_compound, - const FLAG needflag = FLAG_NULL); - char* suffix_check_morph(const char* word, - int len, - int sfxopts, - PfxEntry* ppfx, - const FLAG cclass = FLAG_NULL, - const FLAG needflag = FLAG_NULL, - char in_compound = IN_CPD_NOT); + std::string affix_check_morph(const char* word, + int len, + const FLAG needflag = FLAG_NULL, + char in_compound = IN_CPD_NOT); + std::string prefix_check_morph(const char* word, + int len, + char in_compound, + const FLAG needflag = FLAG_NULL); + std::string suffix_check_morph(const char* word, + int len, + int sfxopts, + PfxEntry* ppfx, + const FLAG cclass = FLAG_NULL, + const FLAG needflag = FLAG_NULL, + char in_compound = IN_CPD_NOT); - char* prefix_check_twosfx_morph(const char* word, - int len, - char in_compound, - const FLAG needflag = FLAG_NULL); - char* suffix_check_twosfx_morph(const char* word, - int len, - int sfxopts, - PfxEntry* ppfx, - const FLAG needflag = FLAG_NULL); + std::string prefix_check_twosfx_morph(const char* word, + int len, + char in_compound, + const FLAG needflag = FLAG_NULL); + std::string suffix_check_twosfx_morph(const char* word, + int len, + int sfxopts, + PfxEntry* ppfx, + const FLAG needflag = FLAG_NULL); - char* morphgen(const char* ts, - int wl, - const unsigned short* ap, - unsigned short al, - const char* morph, - const char* targetmorph, - int level); + std::string morphgen(const char* ts, + int wl, + const unsigned short* ap, + unsigned short al, + const char* morph, + const char* targetmorph, + int level); int expand_rootword(struct guessword* wlst, int maxn, @@ -273,8 +267,7 @@ class LIBHUNSPELL_DLL_EXPORTED AffixMgr { int cpdcase_check(const char* word, int len); inline int candidate_check(const char* word, int len); void setcminmax(int* cmin, int* cmax, const char* word, int len); - struct hentry* compound_check(const char* word, - int len, + struct hentry* compound_check(const std::string& word, short wordnum, short numsyllable, short maxwordnum, @@ -294,47 +287,37 @@ class LIBHUNSPELL_DLL_EXPORTED AffixMgr { hentry** words, hentry** rwords, char hu_mov_rule, - char** result, - char* partresult); + std::string& result, + const std::string* partresult); - int get_suffix_words(short unsigned* suff, + std::vector get_suffix_words(short unsigned* suff, int len, - const char* root_word, - char** slst); + const char* root_word); struct hentry* lookup(const char* word); - int get_numrep() const; - struct replentry* get_reptable() const; + const std::vector& get_reptable() const; RepList* get_iconvtable() const; RepList* get_oconvtable() const; struct phonetable* get_phonetable() const; - int get_nummap() const; - struct mapentry* get_maptable() const; - int get_numbreak() const; - char** get_breaktable() const; - char* get_encoding(); + const std::vector& get_maptable() const; + const std::vector& get_breaktable() const; + const std::string& get_encoding(); int get_langnum() const; char* get_key_string(); char* get_try_string() const; - const char* get_wordchars() const; + const std::string& get_wordchars() const; const std::vector& get_wordchars_utf16() const; - char* get_ignore() const; + const char* get_ignore() const; const std::vector& get_ignore_utf16() const; int get_compound() const; FLAG get_compoundflag() const; - FLAG get_compoundbegin() const; FLAG get_forbiddenword() const; FLAG get_nosuggest() const; FLAG get_nongramsuggest() const; FLAG get_needaffix() const; FLAG get_onlyincompound() const; - FLAG get_compoundroot() const; - FLAG get_lemma_present() const; - int get_checknum() const; - const char* get_prefix() const; - const char* get_suffix() const; const char* get_derived() const; - const char* get_version() const; + const std::string& get_version() const; int have_contclass() const; int get_utf8() const; int get_complexprefixes() const; @@ -355,26 +338,25 @@ class LIBHUNSPELL_DLL_EXPORTED AffixMgr { private: int parse_file(const char* affpath, const char* key); - int parse_flag(char* line, unsigned short* out, FileMgr* af); - int parse_num(char* line, int* out, FileMgr* af); - int parse_cpdsyllable(char* line, FileMgr* af); - int parse_reptable(char* line, FileMgr* af); - int parse_convtable(char* line, + bool parse_flag(const std::string& line, unsigned short* out, FileMgr* af); + bool parse_num(const std::string& line, int* out, FileMgr* af); + bool parse_cpdsyllable(const std::string& line, FileMgr* af); + bool parse_reptable(const std::string& line, FileMgr* af); + bool parse_convtable(const std::string& line, FileMgr* af, RepList** rl, - const char* keyword); - int parse_phonetable(char* line, FileMgr* af); - int parse_maptable(char* line, FileMgr* af); - int parse_breaktable(char* line, FileMgr* af); - int parse_checkcpdtable(char* line, FileMgr* af); - int parse_defcpdtable(char* line, FileMgr* af); - int parse_affix(char* line, const char at, FileMgr* af, char* dupflags); + const std::string& keyword); + bool parse_phonetable(const std::string& line, FileMgr* af); + bool parse_maptable(const std::string& line, FileMgr* af); + bool parse_breaktable(const std::string& line, FileMgr* af); + bool parse_checkcpdtable(const std::string& line, FileMgr* af); + bool parse_defcpdtable(const std::string& line, FileMgr* af); + bool parse_affix(const std::string& line, const char at, FileMgr* af, char* dupflags); void reverse_condition(std::string&); - void debugflag(char* result, unsigned short flag); std::string& debugflag(std::string& result, unsigned short flag); int condlen(const char*); - int encodeit(affentry& entry, const char* cs); + int encodeit(AffEntry& entry, const char* cs); int build_pfxtree(PfxEntry* pfxptr); int build_sfxtree(SfxEntry* sfxptr); int process_pfx_order(); diff --git a/extensions/spellcheck/hunspell/src/atypes.hxx b/extensions/spellcheck/hunspell/src/atypes.hxx index 60826af20e67..fe495f78ed14 100644 --- a/extensions/spellcheck/hunspell/src/atypes.hxx +++ b/extensions/spellcheck/hunspell/src/atypes.hxx @@ -38,8 +38,8 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef _ATYPES_HXX_ -#define _ATYPES_HXX_ +#ifndef ATYPES_HXX_ +#define ATYPES_HXX_ #ifndef HUNSPELL_WARNING #include @@ -55,15 +55,15 @@ static inline void HUNSPELL_WARNING(FILE*, const char*, ...) {} // HUNSTEM def. #define HUNSTEM -#include "hashmgr.hxx" #include "w_char.hxx" #include #include +#include #define SETSIZE 256 #define CONTSIZE 65536 -// affentry options +// AffEntry options #define aeXPRODUCT (1 << 0) #define aeUTF8 (1 << 1) #define aeALIASF (1 << 2) @@ -85,8 +85,6 @@ static inline void HUNSPELL_WARNING(FILE*, const char*, ...) {} #define SPELL_ORIGCAP (1 << 5) #define SPELL_WARN (1 << 6) -#define MAXLNLEN 8192 - #define MINCPDLEN 3 #define MAXCOMPOUND 10 #define MAXCONDLEN 20 @@ -100,46 +98,25 @@ static inline void HUNSPELL_WARNING(FILE*, const char*, ...) {} #define TESTAFF(a, b, c) (std::binary_search(a, a + c, b)) -struct affentry { - std::string strip; - std::string appnd; - char numconds; - char opts; - unsigned short aflag; - unsigned short* contclass; - short contclasslen; - union { - char conds[MAXCONDLEN]; - struct { - char conds1[MAXCONDLEN_1]; - char* conds2; - } l; - } c; - char* morphcode; -}; - struct guessword { char* word; bool allow; char* orig; }; -struct mapentry { - char** set; - int len; -}; - -struct flagentry { - FLAG* def; - int len; -}; +typedef std::vector mapentry; +typedef std::vector flagentry; struct patentry { - char* pattern; - char* pattern2; - char* pattern3; + std::string pattern; + std::string pattern2; + std::string pattern3; FLAG cond; FLAG cond2; + patentry() + : cond(FLAG_NULL) + , cond2(FLAG_NULL) { + } }; #endif diff --git a/extensions/spellcheck/hunspell/src/baseaffix.hxx b/extensions/spellcheck/hunspell/src/baseaffix.hxx index 59256e92f35b..579c030bcfe5 100644 --- a/extensions/spellcheck/hunspell/src/baseaffix.hxx +++ b/extensions/spellcheck/hunspell/src/baseaffix.hxx @@ -38,18 +38,17 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef _BASEAFF_HXX_ -#define _BASEAFF_HXX_ +#ifndef BASEAFF_HXX_ +#define BASEAFF_HXX_ -#include "hunvisapi.h" #include -class LIBHUNSPELL_DLL_EXPORTED AffEntry { +class AffEntry { private: AffEntry(const AffEntry&); AffEntry& operator=(const AffEntry&); - protected: + public: AffEntry() : numconds(0), opts(0), @@ -57,6 +56,7 @@ class LIBHUNSPELL_DLL_EXPORTED AffEntry { morphcode(0), contclass(NULL), contclasslen(0) {} + virtual ~AffEntry(); std::string appnd; std::string strip; unsigned char numconds; diff --git a/extensions/spellcheck/hunspell/src/csutil.cxx b/extensions/spellcheck/hunspell/src/csutil.cxx index 1948e4a3b381..ac5cd986f2fe 100644 --- a/extensions/spellcheck/hunspell/src/csutil.cxx +++ b/extensions/spellcheck/hunspell/src/csutil.cxx @@ -76,6 +76,7 @@ #include #include #include +#include #include "csutil.hxx" #include "atypes.hxx" @@ -122,26 +123,24 @@ static struct unicode_info2* utf_tbl = NULL; static int utf_tbl_count = 0; // utf_tbl can be used by multiple Hunspell instances -FILE* myfopen(const char* path, const char* mode) { -#ifdef _WIN32 +void myopen(std::ifstream& stream, const char* path, std::ios_base::openmode mode) +{ +#if defined(_WIN32) && defined(_MSC_VER) #define WIN32_LONG_PATH_PREFIX "\\\\?\\" if (strncmp(path, WIN32_LONG_PATH_PREFIX, 4) == 0) { int len = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0); - wchar_t* buff = (wchar_t*)malloc(len * sizeof(wchar_t)); - wchar_t* buff2 = (wchar_t*)malloc(len * sizeof(wchar_t)); - FILE* f = NULL; - if (buff && buff2) { - MultiByteToWideChar(CP_UTF8, 0, path, -1, buff, len); - if (_wfullpath(buff2, buff, len) != NULL) { - f = _wfopen(buff2, (strcmp(mode, "r") == 0) ? L"r" : L"rb"); - } - free(buff); - free(buff2); + wchar_t* buff = new wchar_t[len]; + wchar_t* buff2 = new wchar_t[len]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, buff, len); + if (_wfullpath(buff2, buff, len) != NULL) { + stream.open(buff2, mode); } - return f; + delete [] buff; + delete [] buff2; } + else #endif - return fopen(path, mode); + stream.open(path, mode); } std::string& u16_u8(std::string& dest, const std::vector& src) { @@ -218,7 +217,7 @@ int u8_u16(std::vector& dest, const std::string& src) { case 0xd0: { // 2-byte UTF-8 codes if ((*(u8 + 1) & 0xc0) == 0x80) { u2.h = (*u8 & 0x1f) >> 2; - u2.l = (*u8 << 6) + (*(u8 + 1) & 0x3f); + u2.l = (static_cast(*u8) << 6) + (*(u8 + 1) & 0x3f); ++u8; } else { HUNSPELL_WARNING(stderr, @@ -275,34 +274,35 @@ int u8_u16(std::vector& dest, const std::string& src) { return dest.size(); } -// strip strings into token based on single char delimiter -// acts like strsep() but only uses a delim char and not -// a delim string -// default delimiter: white space characters +namespace { +class is_any_of { + public: + explicit is_any_of(const std::string& in) : chars(in) {} -char* mystrsep(char** stringp, const char delim) { - char* mp = *stringp; - if (*mp != '\0') { - char* dp; - if (delim) { - dp = strchr(mp, delim); - } else { - // don't use isspace() here, the string can be in some random charset - // that's way different than the locale's - for (dp = mp; (*dp && *dp != ' ' && *dp != '\t'); dp++) - ; - if (!*dp) - dp = NULL; - } - if (dp) { - *stringp = dp + 1; - *dp = '\0'; - } else { - *stringp = mp + strlen(mp); - } - return mp; - } - return NULL; + bool operator()(char c) { return chars.find(c) != std::string::npos; } + + private: + std::string chars; +}; +} + +std::string::const_iterator mystrsep(const std::string &str, + std::string::const_iterator& start) { + std::string::const_iterator end = str.end(); + + is_any_of op(" \t"); + // don't use isspace() here, the string can be in some random charset + // that's way different than the locale's + std::string::const_iterator sp = start; + while (sp != end && op(*sp)) + ++sp; + + std::string::const_iterator dp = sp; + while (dp != end && !op(*dp)) + ++dp; + + start = dp; + return sp; } // replaces strdup with ansi version @@ -320,142 +320,98 @@ char* mystrdup(const char* s) { return d; } -// strcat for limited length destination string -char* mystrcat(char* dest, const char* st, int max) { - int len; - int len2; - if (dest == NULL || st == NULL) - return dest; - len = strlen(dest); - len2 = strlen(st); - if (len + len2 + 1 > max) - return dest; - strcpy(dest + len, st); - return dest; -} - // remove cross-platform text line end characters -void mychomp(char* s) { - size_t k = strlen(s); - if ((k > 0) && ((*(s + k - 1) == '\r') || (*(s + k - 1) == '\n'))) - *(s + k - 1) = '\0'; - if ((k > 1) && (*(s + k - 2) == '\r')) - *(s + k - 2) = '\0'; +void mychomp(std::string& s) { + size_t k = s.size(); + size_t newsize = k; + if ((k > 0) && ((s[k - 1] == '\r') || (s[k - 1] == '\n'))) + --newsize; + if ((k > 1) && (s[k - 2] == '\r')) + --newsize; + s.resize(newsize); } // break text to lines -// return number of lines -int line_tok(const char* text, char*** lines, char breakchar) { - int linenum = 0; - if (!text) { - return linenum; - } - char* dup = mystrdup(text); - char* p = strchr(dup, breakchar); - while (p) { - linenum++; - *p = '\0'; - p++; - p = strchr(p, breakchar); - } - linenum++; - *lines = (char**)malloc(linenum * sizeof(char*)); - if (!(*lines)) { - free(dup); - return 0; +std::vector line_tok(const std::string& text, char breakchar) { + std::vector ret; + if (text.empty()) { + return ret; } - p = dup; - int l = 0; - for (int i = 0; i < linenum; i++) { - if (*p != '\0') { - (*lines)[l] = mystrdup(p); - if (!(*lines)[l]) { - for (i = 0; i < l; i++) - free((*lines)[i]); - free(dup); - return 0; - } - l++; + std::stringstream ss(text); + std::string tok; + while(std::getline(ss, tok, breakchar)) { + if (!tok.empty()) { + ret.push_back(tok); } - p += strlen(p) + 1; } - free(dup); - if (!l) { - free(*lines); - *lines = NULL; - } - return l; + + return ret; } // uniq line in place -char* line_uniq(char* text, char breakchar) { - char** lines; - int linenum = line_tok(text, &lines, breakchar); - int i; - strcpy(text, lines[0]); - for (i = 1; i < linenum; i++) { - int dup = 0; - for (int j = 0; j < i; j++) { - if (strcmp(lines[i], lines[j]) == 0) { - dup = 1; +void line_uniq(std::string& text, char breakchar) +{ + std::vector lines = line_tok(text, breakchar); + text.clear(); + if (lines.empty()) { + return; + } + text = lines[0]; + for (size_t i = 1; i < lines.size(); ++i) { + bool dup = false; + for (size_t j = 0; j < i; ++j) { + if (lines[i] == lines[j]) { + dup = true; break; } } if (!dup) { - if ((i > 1) || (*(lines[0]) != '\0')) { - sprintf(text + strlen(text), "%c", breakchar); - } - strcat(text, lines[i]); + if (!text.empty()) + text.push_back(breakchar); + text.append(lines[i]); } } - for (i = 0; i < linenum; i++) { - free(lines[i]); - } - free(lines); - return text; } // uniq and boundary for compound analysis: "1\n\2\n\1" -> " ( \1 | \2 ) " -char* line_uniq_app(char** text, char breakchar) { - if (!strchr(*text, breakchar)) { - return *text; +void line_uniq_app(std::string& text, char breakchar) { + if (text.find(breakchar) == std::string::npos) { + return; } - char** lines; - int i; - int linenum = line_tok(*text, &lines, breakchar); - int dup = 0; - for (i = 0; i < linenum; i++) { - for (int j = 0; j < (i - 1); j++) { - if (strcmp(lines[i], lines[j]) == 0) { - *(lines[i]) = '\0'; - dup++; + std::vector lines = line_tok(text, breakchar); + text.clear(); + if (lines.empty()) { + return; + } + text = lines[0]; + for (size_t i = 1; i < lines.size(); ++i) { + bool dup = false; + for (size_t j = 0; j < i; ++j) { + if (lines[i] == lines[j]) { + dup = true; break; } } - } - if ((linenum - dup) == 1) { - strcpy(*text, lines[0]); - freelist(&lines, linenum); - return *text; - } - char* newtext = (char*)malloc(strlen(*text) + 2 * linenum + 3 + 1); - if (newtext) { - free(*text); - *text = newtext; - } else { - freelist(&lines, linenum); - return *text; - } - strcpy(*text, " ( "); - for (i = 0; i < linenum; i++) - if (*(lines[i])) { - sprintf(*text + strlen(*text), "%s%s", lines[i], " | "); + if (!dup) { + if (!text.empty()) + text.push_back(breakchar); + text.append(lines[i]); } - (*text)[strlen(*text) - 2] = ')'; // " ) " - freelist(&lines, linenum); - return *text; + } + + if (lines.size() == 1) { + text = lines[0]; + return; + } + + text.assign(" ( "); + for (size_t i = 0; i < lines.size(); ++i) { + text.append(lines[i]); + text.append(" | "); + } + text[text.size() - 2] = ')'; // " ) " } // append s to ends of every lines in text @@ -469,111 +425,6 @@ std::string& strlinecat(std::string& str, const std::string& apd) { return str; } -// morphcmp(): compare MORPH_DERI_SFX, MORPH_INFL_SFX and MORPH_TERM_SFX fields -// in the first line of the inputs -// return 0, if inputs equal -// return 1, if inputs may equal with a secondary suffix -// otherwise return -1 -int morphcmp(const char* s, const char* t) { - int se = 0; - int te = 0; - const char* sl; - const char* tl; - const char* olds; - const char* oldt; - if (!s || !t) - return 1; - olds = s; - sl = strchr(s, '\n'); - s = strstr(s, MORPH_DERI_SFX); - if (!s || (sl && sl < s)) - s = strstr(olds, MORPH_INFL_SFX); - if (!s || (sl && sl < s)) { - s = strstr(olds, MORPH_TERM_SFX); - olds = NULL; - } - oldt = t; - tl = strchr(t, '\n'); - t = strstr(t, MORPH_DERI_SFX); - if (!t || (tl && tl < t)) - t = strstr(oldt, MORPH_INFL_SFX); - if (!t || (tl && tl < t)) { - t = strstr(oldt, MORPH_TERM_SFX); - oldt = NULL; - } - while (s && t && (!sl || sl > s) && (!tl || tl > t)) { - s += MORPH_TAG_LEN; - t += MORPH_TAG_LEN; - se = 0; - te = 0; - while ((*s == *t) && !se && !te) { - s++; - t++; - switch (*s) { - case ' ': - case '\n': - case '\t': - case '\0': - se = 1; - } - switch (*t) { - case ' ': - case '\n': - case '\t': - case '\0': - te = 1; - } - } - if (!se || !te) { - // not terminal suffix difference - if (olds) - return -1; - return 1; - } - olds = s; - s = strstr(s, MORPH_DERI_SFX); - if (!s || (sl && sl < s)) - s = strstr(olds, MORPH_INFL_SFX); - if (!s || (sl && sl < s)) { - s = strstr(olds, MORPH_TERM_SFX); - olds = NULL; - } - oldt = t; - t = strstr(t, MORPH_DERI_SFX); - if (!t || (tl && tl < t)) - t = strstr(oldt, MORPH_INFL_SFX); - if (!t || (tl && tl < t)) { - t = strstr(oldt, MORPH_TERM_SFX); - oldt = NULL; - } - } - if (!s && !t && se && te) - return 0; - return 1; -} - -int get_sfxcount(const char* morph) { - if (!morph || !*morph) - return 0; - int n = 0; - const char* old = morph; - morph = strstr(morph, MORPH_DERI_SFX); - if (!morph) - morph = strstr(old, MORPH_INFL_SFX); - if (!morph) - morph = strstr(old, MORPH_TERM_SFX); - while (morph) { - n++; - old = morph; - morph = strstr(morph + 1, MORPH_DERI_SFX); - if (!morph) - morph = strstr(old + 1, MORPH_INFL_SFX); - if (!morph) - morph = strstr(old + 1, MORPH_TERM_SFX); - } - return n; -} - int fieldlen(const char* r) { int n = 0; while (r && *r != ' ' && *r != '\t' && *r != '\0' && *r != '\n') { @@ -615,33 +466,6 @@ std::string& mystrrep(std::string& str, return str; } -char* mystrrep(char* word, const char* pat, const char* rep) { - char* pos = strstr(word, pat); - if (pos) { - int replen = strlen(rep); - int patlen = strlen(pat); - while (pos) { - if (replen < patlen) { - char* end = word + strlen(word); - char* next = pos + replen; - char* prev = pos + strlen(pat); - for (; prev < end;* next = *prev, prev++, next++) - ; - *next = '\0'; - } else if (replen > patlen) { - char* end = pos + patlen; - char* next = word + strlen(word) + replen - patlen; - char* prev = next - replen + patlen; - for (; prev >= end;* next = *prev, prev--, next--) - ; - } - strncpy(pos, rep, replen); - pos = strstr(word, pat); - } - } - return word; -} - // reverse word size_t reverseword(std::string& word) { std::reverse(word.begin(), word.end()); @@ -657,35 +481,19 @@ size_t reverseword_utf(std::string& word) { return w.size(); } -int uniqlist(char** list, int n) { - int i; - if (n < 2) - return n; - for (i = 0; i < n; i++) { - for (int j = 0; j < i; j++) { - if (list[j] && list[i] && (strcmp(list[j], list[i]) == 0)) { - free(list[i]); - list[i] = NULL; - break; - } - } - } - int m = 1; - for (i = 1; i < n; i++) - if (list[i]) { - list[m] = list[i]; - m++; - } - return m; -} +void uniqlist(std::vector& list) { + if (list.size() < 2) + return; -void freelist(char*** list, int n) { - if (list && *list) { - for (int i = 0; i < n; i++) - free((*list)[i]); - free(*list); - *list = NULL; + std::vector ret; + ret.push_back(list[0]); + + for (size_t i = 1; i < list.size(); ++i) { + if (std::find(ret.begin(), ret.end(), list[i]) == ret.end()) + ret.push_back(list[i]); } + + list.swap(ret); } namespace { @@ -2457,9 +2265,9 @@ static void toAsciiLowerAndRemoveNonAlphanumeric(const char* pName, *pBuf = '\0'; } -struct cs_info* get_current_cs(const char* es) { - char* normalized_encoding = new char[strlen(es) + 1]; - toAsciiLowerAndRemoveNonAlphanumeric(es, normalized_encoding); +struct cs_info* get_current_cs(const std::string& es) { + char* normalized_encoding = new char[es.size() + 1]; + toAsciiLowerAndRemoveNonAlphanumeric(es.c_str(), normalized_encoding); struct cs_info* ccs = NULL; int n = sizeof(encds) / sizeof(encds[0]); @@ -2474,7 +2282,7 @@ struct cs_info* get_current_cs(const char* es) { if (!ccs) { HUNSPELL_WARNING(stderr, - "error: unknown encoding %s: using %s as fallback\n", es, + "error: unknown encoding %s: using %s as fallback\n", es.c_str(), encds[0].enc_name); ccs = encds[0].cs_table; } @@ -2485,7 +2293,7 @@ struct cs_info* get_current_cs(const char* es) { // XXX This function was rewritten for mozilla. Instead of storing the // conversion tables static in this file, create them when needed // with help the mozilla backend. -struct cs_info* get_current_cs(const char* es) { +struct cs_info* get_current_cs(const std::string& es) { struct cs_info* ccs = new cs_info[256]; // Initialze the array with dummy data so that we wouldn't need // to return null in case of failures. @@ -2500,7 +2308,7 @@ struct cs_info* get_current_cs(const char* es) { nsresult rv; - nsAutoCString label(es); + nsAutoCString label(es.c_str()); nsAutoCString encoding; if (!EncodingUtils::FindEncodingForLabelNoReplacement(label, encoding)) { return ccs; @@ -2565,21 +2373,18 @@ struct cs_info* get_current_cs(const char* es) { #endif // primitive isalpha() replacement for tokenization -char* get_casechars(const char* enc) { +std::string get_casechars(const char* enc) { struct cs_info* csconv = get_current_cs(enc); - char expw[MAXLNLEN]; - char* p = expw; - for (int i = 0; i <= 255; i++) { + std::string expw; + for (int i = 0; i <= 255; ++i) { if (cupper(csconv, i) != clower(csconv, i)) { - *p = static_cast(i); - p++; + expw.push_back(static_cast(i)); } } - *p = '\0'; #ifdef MOZILLA_CLIENT delete[] csconv; #endif - return mystrdup(expw); + return expw; } // language to encoding default map @@ -2606,10 +2411,10 @@ static struct lang_map lang2enc[] = {"tr_TR", LANG_tr}, // for back-compatibility {"ru", LANG_ru}, {"uk", LANG_uk}}; -int get_lang_num(const char* lang) { +int get_lang_num(const std::string& lang) { int n = sizeof(lang2enc) / sizeof(lang2enc[0]); for (int i = 0; i < n; i++) { - if (strcmp(lang, lang2enc[i].lang) == 0) { + if (strcmp(lang.c_str(), lang2enc[i].lang) == 0) { return lang2enc[i].num; } } @@ -2618,26 +2423,21 @@ int get_lang_num(const char* lang) { #ifndef OPENOFFICEORG #ifndef MOZILLA_CLIENT -int initialize_utf_tbl() { +void initialize_utf_tbl() { utf_tbl_count++; if (utf_tbl) - return 0; - utf_tbl = (unicode_info2*)malloc(CONTSIZE * sizeof(unicode_info2)); - if (utf_tbl) { - size_t j; - for (j = 0; j < CONTSIZE; j++) { - utf_tbl[j].cletter = 0; - utf_tbl[j].clower = (unsigned short)j; - utf_tbl[j].cupper = (unsigned short)j; - } - for (j = 0; j < UTF_LST_LEN; j++) { - utf_tbl[utf_lst[j].c].cletter = 1; - utf_tbl[utf_lst[j].c].clower = utf_lst[j].clower; - utf_tbl[utf_lst[j].c].cupper = utf_lst[j].cupper; - } - } else - return 1; - return 0; + return; + utf_tbl = new unicode_info2[CONTSIZE]; + for (size_t j = 0; j < CONTSIZE; ++j) { + utf_tbl[j].cletter = 0; + utf_tbl[j].clower = (unsigned short)j; + utf_tbl[j].cupper = (unsigned short)j; + } + for (size_t j = 0; j < UTF_LST_LEN; ++j) { + utf_tbl[utf_lst[j].c].cletter = 1; + utf_tbl[utf_lst[j].c].clower = utf_lst[j].clower; + utf_tbl[utf_lst[j].c].cupper = utf_lst[j].cupper; + } } #endif #endif @@ -2646,7 +2446,7 @@ void free_utf_tbl() { if (utf_tbl_count > 0) utf_tbl_count--; if (utf_tbl && (utf_tbl_count == 0)) { - free(utf_tbl); + delete[] utf_tbl; utf_tbl = NULL; } } @@ -2775,18 +2575,6 @@ size_t remove_ignored_chars_utf(std::string& word, return w2.size(); } -namespace { -class is_any_of { - public: - is_any_of(const std::string& in) : chars(in) {} - - bool operator()(char c) { return chars.find(c) != std::string::npos; } - - private: - std::string chars; -}; -} - // strip all ignored characters in the string size_t remove_ignored_chars(std::string& word, const std::string& ignored_chars) { @@ -2796,54 +2584,48 @@ size_t remove_ignored_chars(std::string& word, return word.size(); } -int parse_string(char* line, char** out, int ln) { - char* tp = line; - char* piece; +bool parse_string(const std::string& line, std::string& out, int ln) { + if (!out.empty()) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions\n", ln); + return false; + } int i = 0; int np = 0; - if (*out) { - HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions\n", ln); - return 1; - } - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - np++; - break; - } - case 1: { - *out = mystrdup(piece); - if (!*out) - return 1; - np++; - break; - } - default: - break; + std::string::const_iterator iter = line.begin(); + std::string::const_iterator start_piece = mystrsep(line, iter); + while (start_piece != line.end()) { + switch (i) { + case 0: { + np++; + break; } - i++; + case 1: { + out.assign(start_piece, iter); + np++; + break; + } + default: + break; } - // free(piece); - piece = mystrsep(&tp, 0); + ++i; + start_piece = mystrsep(line, iter); } if (np != 2) { HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", ln); - return 1; + return false; } - return 0; + return true; } -bool parse_array(char* line, - char** out, +bool parse_array(const std::string& line, + std::string& out, std::vector& out_utf16, int utf8, int ln) { - if (parse_string(line, out, ln)) + if (!parse_string(line, out, ln)) return false; if (utf8) { - u8_u16(out_utf16, *out); + u8_u16(out_utf16, out); std::sort(out_utf16.begin(), out_utf16.end()); } return true; diff --git a/extensions/spellcheck/hunspell/src/csutil.hxx b/extensions/spellcheck/hunspell/src/csutil.hxx index ce7091df5539..302d7e909561 100644 --- a/extensions/spellcheck/hunspell/src/csutil.hxx +++ b/extensions/spellcheck/hunspell/src/csutil.hxx @@ -71,13 +71,14 @@ * SUCH DAMAGE. */ -#ifndef __CSUTILHXX__ -#define __CSUTILHXX__ +#ifndef CSUTIL_HXX_ +#define CSUTIL_HXX_ #include "hunvisapi.h" // First some base level utility routines +#include #include #include #include @@ -127,8 +128,9 @@ #define FORBIDDENWORD 65510 #define ONLYUPCASEFLAG 65511 -// fopen or optional _wfopen to fix long pathname problem of WIN32 -LIBHUNSPELL_DLL_EXPORTED FILE* myfopen(const char* path, const char* mode); +// fix long pathname problem of WIN32 by using w_char std::fstream::open override +LIBHUNSPELL_DLL_EXPORTED void myopen(std::ifstream& stream, const char* path, + std::ios_base::openmode mode); // convert UTF-16 characters to UTF-8 LIBHUNSPELL_DLL_EXPORTED std::string& u16_u8(std::string& dest, @@ -139,21 +141,16 @@ LIBHUNSPELL_DLL_EXPORTED int u8_u16(std::vector& dest, const std::string& src); // remove end of line char(s) -LIBHUNSPELL_DLL_EXPORTED void mychomp(char* s); +LIBHUNSPELL_DLL_EXPORTED void mychomp(std::string& s); // duplicate string LIBHUNSPELL_DLL_EXPORTED char* mystrdup(const char* s); -// strcat for limited length destination string -LIBHUNSPELL_DLL_EXPORTED char* mystrcat(char* dest, const char* st, int max); - // parse into tokens with char delimiter -LIBHUNSPELL_DLL_EXPORTED char* mystrsep(char** sptr, const char delim); +LIBHUNSPELL_DLL_EXPORTED std::string::const_iterator mystrsep(const std::string &str, + std::string::const_iterator& start); // replace pat by rep in word and return word -LIBHUNSPELL_DLL_EXPORTED char* mystrrep(char* word, - const char* pat, - const char* rep); LIBHUNSPELL_DLL_EXPORTED std::string& mystrrep(std::string& str, const std::string& search, const std::string& replace); @@ -163,13 +160,13 @@ LIBHUNSPELL_DLL_EXPORTED std::string& strlinecat(std::string& str, const std::string& apd); // tokenize into lines with new line -LIBHUNSPELL_DLL_EXPORTED int line_tok(const char* text, - char*** lines, - char breakchar); +LIBHUNSPELL_DLL_EXPORTED std::vector line_tok(const std::string& text, + char breakchar); // tokenize into lines with new line and uniq in place -LIBHUNSPELL_DLL_EXPORTED char* line_uniq(char* text, char breakchar); -LIBHUNSPELL_DLL_EXPORTED char* line_uniq_app(char** text, char breakchar); +LIBHUNSPELL_DLL_EXPORTED void line_uniq(std::string& text, char breakchar); + +LIBHUNSPELL_DLL_EXPORTED void line_uniq_app(std::string& text, char breakchar); // reverse word LIBHUNSPELL_DLL_EXPORTED size_t reverseword(std::string& word); @@ -178,10 +175,7 @@ LIBHUNSPELL_DLL_EXPORTED size_t reverseword(std::string& word); LIBHUNSPELL_DLL_EXPORTED size_t reverseword_utf(std::string&); // remove duplicates -LIBHUNSPELL_DLL_EXPORTED int uniqlist(char** list, int n); - -// free character array list -LIBHUNSPELL_DLL_EXPORTED void freelist(char*** list, int n); +LIBHUNSPELL_DLL_EXPORTED void uniqlist(std::vector& list); // character encoding information struct cs_info { @@ -190,7 +184,7 @@ struct cs_info { unsigned char cupper; }; -LIBHUNSPELL_DLL_EXPORTED int initialize_utf_tbl(); +LIBHUNSPELL_DLL_EXPORTED void initialize_utf_tbl(); LIBHUNSPELL_DLL_EXPORTED void free_utf_tbl(); LIBHUNSPELL_DLL_EXPORTED unsigned short unicodetoupper(unsigned short c, int langnum); @@ -200,13 +194,13 @@ LIBHUNSPELL_DLL_EXPORTED unsigned short unicodetolower(unsigned short c, int langnum); LIBHUNSPELL_DLL_EXPORTED int unicodeisalpha(unsigned short c); -LIBHUNSPELL_DLL_EXPORTED struct cs_info* get_current_cs(const char* es); +LIBHUNSPELL_DLL_EXPORTED struct cs_info* get_current_cs(const std::string& es); // get language identifiers of language codes -LIBHUNSPELL_DLL_EXPORTED int get_lang_num(const char* lang); +LIBHUNSPELL_DLL_EXPORTED int get_lang_num(const std::string& lang); // get characters of the given 8bit encoding with lower- and uppercase forms -LIBHUNSPELL_DLL_EXPORTED char* get_casechars(const char* enc); +LIBHUNSPELL_DLL_EXPORTED std::string get_casechars(const char* enc); // convert std::string to all caps LIBHUNSPELL_DLL_EXPORTED std::string& mkallcap(std::string& s, @@ -256,10 +250,12 @@ LIBHUNSPELL_DLL_EXPORTED size_t remove_ignored_chars( std::string& word, const std::string& ignored_chars); -LIBHUNSPELL_DLL_EXPORTED int parse_string(char* line, char** out, int ln); +LIBHUNSPELL_DLL_EXPORTED bool parse_string(const std::string& line, + std::string& out, + int ln); -LIBHUNSPELL_DLL_EXPORTED bool parse_array(char* line, - char** out, +LIBHUNSPELL_DLL_EXPORTED bool parse_array(const std::string& line, + std::string& out, std::vector& out_utf16, int utf8, int ln); @@ -270,10 +266,6 @@ LIBHUNSPELL_DLL_EXPORTED bool copy_field(std::string& dest, const std::string& morph, const std::string& var); -LIBHUNSPELL_DLL_EXPORTED int morphcmp(const char* s, const char* t); - -LIBHUNSPELL_DLL_EXPORTED int get_sfxcount(const char* morph); - // conversion function for protected memory LIBHUNSPELL_DLL_EXPORTED void store_pointer(char* dest, char* source); diff --git a/extensions/spellcheck/hunspell/src/filemgr.cxx b/extensions/spellcheck/hunspell/src/filemgr.cxx index 2218bc79e1a4..b7c89b2bea0c 100644 --- a/extensions/spellcheck/hunspell/src/filemgr.cxx +++ b/extensions/spellcheck/hunspell/src/filemgr.cxx @@ -86,33 +86,33 @@ int FileMgr::fail(const char* err, const char* par) { FileMgr::FileMgr(const char* file, const char* key) : hin(NULL), linenum(0) { in[0] = '\0'; - fin = myfopen(file, "r"); - if (!fin) { + myopen(fin, file, std::ios_base::in); + if (!fin.is_open()) { // check hzipped file std::string st(file); st.append(HZIP_EXTENSION); hin = new Hunzip(st.c_str(), key); } - if (!fin && !hin) + if (!fin.is_open() && !hin->is_open()) fail(MSG_OPEN, file); } FileMgr::~FileMgr() { - if (fin) - fclose(fin); - if (hin) - delete hin; + delete hin; } -char* FileMgr::getline() { - const char* l; - linenum++; - if (fin) - return fgets(in, BUFSIZE - 1, fin); - if (hin && ((l = hin->getline()) != NULL)) - return strcpy(in, l); - linenum--; - return NULL; +bool FileMgr::getline(std::string& dest) { + bool ret = false; + ++linenum; + if (fin.is_open()) { + ret = static_cast(std::getline(fin, dest)); + } else if (hin->is_open()) { + ret = hin->getline(dest); + } + if (!ret) { + --linenum; + } + return ret; } int FileMgr::getlinenum() { diff --git a/extensions/spellcheck/hunspell/src/filemgr.hxx b/extensions/spellcheck/hunspell/src/filemgr.hxx index 8b69931ddb43..991e924a2524 100644 --- a/extensions/spellcheck/hunspell/src/filemgr.hxx +++ b/extensions/spellcheck/hunspell/src/filemgr.hxx @@ -72,21 +72,21 @@ */ /* file manager class - read lines of files [filename] OR [filename.hz] */ -#ifndef _FILEMGR_HXX_ -#define _FILEMGR_HXX_ - -#include "hunvisapi.h" +#ifndef FILEMGR_HXX_ +#define FILEMGR_HXX_ #include "hunzip.hxx" #include +#include +#include -class LIBHUNSPELL_DLL_EXPORTED FileMgr { +class FileMgr { private: FileMgr(const FileMgr&); FileMgr& operator=(const FileMgr&); protected: - FILE* fin; + std::ifstream fin; Hunzip* hin; char in[BUFSIZE + 50]; // input buffer int fail(const char* err, const char* par); @@ -95,7 +95,7 @@ class LIBHUNSPELL_DLL_EXPORTED FileMgr { public: FileMgr(const char* filename, const char* key = NULL); ~FileMgr(); - char* getline(); + bool getline(std::string&); int getlinenum(); }; #endif diff --git a/extensions/spellcheck/hunspell/src/hashmgr.cxx b/extensions/spellcheck/hunspell/src/hashmgr.cxx index c3cd95420f7e..1de16901e8d6 100644 --- a/extensions/spellcheck/hunspell/src/hashmgr.cxx +++ b/extensions/spellcheck/hunspell/src/hashmgr.cxx @@ -98,20 +98,19 @@ HashMgr::HashMgr(const char* tpath, const char* apath, const char* key) numaliasm(0), aliasm(NULL) { langnum = 0; - lang = NULL; - enc = NULL; csconv = 0; - ignorechars = NULL; load_config(apath, key); int ec = load_tables(tpath, key); if (ec) { /* error condition - what should we do here */ HUNSPELL_WARNING(stderr, "Hash Manager Error : %d\n", ec); - if (tableptr) { - free(tableptr); - tableptr = NULL; + free(tableptr); + //keep tablesize to 1 to fix possible division with zero + tablesize = 1; + tableptr = (struct hentry**)calloc(tablesize, sizeof(struct hentry*)); + if (!tableptr) { + tablesize = 0; } - tablesize = 0; } } @@ -159,14 +158,6 @@ HashMgr::~HashMgr() { #endif #endif - if (enc) - free(enc); - if (lang) - free(lang); - - if (ignorechars) - free(ignorechars); - #ifdef MOZILLA_CLIENT delete[] csconv; #endif @@ -189,20 +180,21 @@ struct hentry* HashMgr::lookup(const char* word) const { } // add a word to the hash table (private) -int HashMgr::add_word(const char* word, - int wbl, +int HashMgr::add_word(const std::string& in_word, int wcl, unsigned short* aff, int al, - const char* desc, + const std::string* in_desc, bool onlyupcase) { + const std::string* word = &in_word; + const std::string* desc = in_desc; std::string *word_copy = NULL; std::string *desc_copy = NULL; - if (ignorechars || complexprefixes) { - word_copy = new std::string(word, wbl); + if (!ignorechars.empty() || complexprefixes) { + word_copy = new std::string(in_word); - if (ignorechars != NULL) { + if (!ignorechars.empty()) { if (utf8) { wcl = remove_ignored_chars_utf(*word_copy, ignorechars_utf16); } else { @@ -216,8 +208,8 @@ int HashMgr::add_word(const char* word, else reverseword(*word_copy); - if (desc && !aliasm) { - desc_copy = new std::string(desc); + if (in_desc && !aliasm) { + desc_copy = new std::string(*in_desc); if (complexprefixes) { if (utf8) @@ -225,19 +217,18 @@ int HashMgr::add_word(const char* word, else reverseword(*desc_copy); } - desc = desc_copy->c_str(); + desc = desc_copy; } } - wbl = word_copy->size(); - word = word_copy->c_str(); + word = word_copy; } bool upcasehomonym = false; - int descl = desc ? (aliasm ? sizeof(char*) : strlen(desc) + 1) : 0; + int descl = desc ? (aliasm ? sizeof(char*) : desc->size() + 1) : 0; // variable-length hash record with word and optional fields struct hentry* hp = - (struct hentry*)malloc(sizeof(struct hentry) + wbl + descl); + (struct hentry*)malloc(sizeof(struct hentry) + word->size() + descl); if (!hp) { delete desc_copy; delete word_copy; @@ -245,11 +236,11 @@ int HashMgr::add_word(const char* word, } char* hpw = hp->word; - strcpy(hpw, word); + strcpy(hpw, word->c_str()); int i = hash(hpw); - hp->blen = (unsigned char)wbl; + hp->blen = (unsigned char)word->size(); hp->clen = (unsigned char)wcl; hp->alen = (short)al; hp->astr = aff; @@ -261,9 +252,9 @@ int HashMgr::add_word(const char* word, hp->var = H_OPT; if (aliasm) { hp->var += H_OPT_ALIASM; - store_pointer(hpw + wbl + 1, get_aliasm(atoi(desc))); + store_pointer(hpw + word->size() + 1, get_aliasm(atoi(desc->c_str()))); } else { - strcpy(hpw + wbl + 1, desc); + strcpy(hpw + word->size() + 1, desc->c_str()); } if (strstr(HENTRY_DATA(hp), MORPH_PHON)) hp->var += H_OPT_PHON; @@ -334,7 +325,7 @@ int HashMgr::add_hidden_capitalized_word(const std::string& word, int wcl, unsigned short* flags, int flagslen, - char* dp, + const std::string* dp, int captype) { if (flags == NULL) flagslen = 0; @@ -359,12 +350,12 @@ int HashMgr::add_hidden_capitalized_word(const std::string& word, mkallsmall_utf(w, langnum); mkinitcap_utf(w, langnum); u16_u8(st, w); - return add_word(st.c_str(), st.size(), wcl, flags2, flagslen + 1, dp, true); + return add_word(st, wcl, flags2, flagslen + 1, dp, true); } else { std::string new_word(word); mkallsmall(new_word, csconv); mkinitcap(new_word, csconv); - int ret = add_word(new_word.c_str(), new_word.size(), wcl, flags2, flagslen + 1, dp, true); + int ret = add_word(new_word, wcl, flags2, flagslen + 1, dp, true); return ret; } } @@ -386,8 +377,8 @@ int HashMgr::get_clen_and_captype(const std::string& word, int* captype) { } // remove word (personal dictionary function for standalone applications) -int HashMgr::remove(const char* word) { - struct hentry* dp = lookup(word); +int HashMgr::remove(const std::string& word) { + struct hentry* dp = lookup(word.c_str()); while (dp) { if (dp->alen == 0 || !TESTAFF(dp->astr, forbiddenword, dp->alen)) { unsigned short* flags = @@ -397,6 +388,7 @@ int HashMgr::remove(const char* word) { for (int i = 0; i < dp->alen; i++) flags[i] = dp->astr[i]; flags[dp->alen] = forbiddenword; + free(dp->astr); dp->astr = flags; dp->alen++; std::sort(flags, flags + dp->alen); @@ -426,6 +418,7 @@ int HashMgr::remove_forbidden_flag(const std::string& word) { flags2[j++] = dp->astr[i]; } dp->alen--; + free(dp->astr); dp->astr = flags2; // XXX allowed forbidden words } } @@ -436,36 +429,34 @@ int HashMgr::remove_forbidden_flag(const std::string& word) { // add a custom dic. word to the hash table (public) int HashMgr::add(const std::string& word) { - unsigned short* flags = NULL; - int al = 0; if (remove_forbidden_flag(word)) { int captype; - int wbl = word.size(); + int al = 0; + unsigned short* flags = NULL; int wcl = get_clen_and_captype(word, &captype); - add_word(word.c_str(), wbl, wcl, flags, al, NULL, false); + add_word(word, wcl, flags, al, NULL, false); return add_hidden_capitalized_word(word, wcl, flags, al, NULL, captype); } return 0; } -int HashMgr::add_with_affix(const char* word, const char* example) { +int HashMgr::add_with_affix(const std::string& word, const std::string& example) { // detect captype and modify word length for UTF-8 encoding - struct hentry* dp = lookup(example); + struct hentry* dp = lookup(example.c_str()); remove_forbidden_flag(word); if (dp && dp->astr) { int captype; - int wbl = strlen(word); int wcl = get_clen_and_captype(word, &captype); if (aliasf) { - add_word(word, wbl, wcl, dp->astr, dp->alen, NULL, false); + add_word(word, wcl, dp->astr, dp->alen, NULL, false); } else { unsigned short* flags = (unsigned short*)malloc(dp->alen * sizeof(unsigned short)); if (flags) { memcpy((void*)flags, (void*)dp->astr, dp->alen * sizeof(unsigned short)); - add_word(word, wbl, wcl, flags, dp->alen, NULL, false); + add_word(word, wcl, flags, dp->alen, NULL, false); } else return 1; } @@ -491,20 +482,14 @@ struct hentry* HashMgr::walk_hashtable(int& col, struct hentry* hp) const { // load a munched word list and build a hash table on the fly int HashMgr::load_tables(const char* tpath, const char* key) { - int al; - char* ap; - char* dp; - char* dp2; - unsigned short* flags; - char* ts; - // open dictionary file FileMgr* dict = new FileMgr(tpath, key); if (dict == NULL) return 1; // first read the first line of file to get hash table size */ - if ((ts = dict->getline()) == NULL) { + std::string ts; + if (!dict->getline(ts)) { HUNSPELL_WARNING(stderr, "error: empty dic file %s\n", tpath); delete dict; return 2; @@ -512,13 +497,11 @@ int HashMgr::load_tables(const char* tpath, const char* key) { mychomp(ts); /* remove byte order mark */ - if (strncmp(ts, "\xEF\xBB\xBF", 3) == 0) { - memmove(ts, ts + 3, strlen(ts + 3) + 1); - // warning: dic file begins with byte order mark: possible incompatibility - // with old Hunspell versions + if (ts.compare(0, 3, "\xEF\xBB\xBF", 3) == 0) { + ts.erase(0, 3); } - tablesize = atoi(ts); + tablesize = atoi(ts.c_str()); int nExtra = 5 + USERWORD; @@ -544,60 +527,65 @@ int HashMgr::load_tables(const char* tpath, const char* key) { // loop through all words on much list and add to hash // table and create word and affix strings - while ((ts = dict->getline()) != NULL) { + while (dict->getline(ts)) { mychomp(ts); // split each line into word and morphological description - dp = ts; - while ((dp = strchr(dp, ':')) != NULL) { - if ((dp > ts + 3) && (*(dp - 3) == ' ' || *(dp - 3) == '\t')) { - for (dp -= 4; dp >= ts && (*dp == ' ' || *dp == '\t'); dp--) + size_t dp_pos = 0; + while ((dp_pos = ts.find(':', dp_pos)) != std::string::npos) { + if ((dp_pos > 3) && (ts[dp_pos - 3] == ' ' || ts[dp_pos - 3] == '\t')) { + for (dp_pos -= 3; dp_pos > 0 && (ts[dp_pos-1] == ' ' || ts[dp_pos-1] == '\t'); --dp_pos) ; - if (dp < ts) { // missing word - dp = NULL; + if (dp_pos == 0) { // missing word + dp_pos = std::string::npos; } else { - *(dp + 1) = '\0'; - dp = dp + 2; + ++dp_pos; } break; } - dp++; + ++dp_pos; } // tabulator is the old morphological field separator - dp2 = strchr(ts, '\t'); - if (dp2 && (!dp || dp2 < dp)) { - *dp2 = '\0'; - dp = dp2 + 1; + size_t dp2_pos = ts.find('\t'); + if (dp2_pos != std::string::npos && (dp_pos == std::string::npos || dp2_pos < dp_pos)) { + dp_pos = dp2_pos + 1; + } + + std::string dp; + if (dp_pos != std::string::npos) { + dp.assign(ts.substr(dp_pos)); + ts.resize(dp_pos - 1); } // split each line into word and affix char strings // "\/" signs slash in words (not affix separator) // "/" at beginning of the line is word character (not affix separator) - ap = strchr(ts, '/'); - while (ap) { - if (ap == ts) { - ap++; + size_t ap_pos = ts.find('/'); + while (ap_pos != std::string::npos) { + if (ap_pos == 0) { + ++ap_pos; continue; - } else if (*(ap - 1) != '\\') + } else if (ts[ap_pos - 1] != '\\') break; // replace "\/" with "/" - for (char *sp = ap - 1; *sp; *sp = *(sp + 1), sp++) - ; - ap = strchr(ap, '/'); + ts.erase(ap_pos - 1, 1); + ap_pos = ts.find('/', ap_pos); } - if (ap) { - *ap = '\0'; + unsigned short* flags; + int al; + if (ap_pos != std::string::npos && ap_pos != ts.size()) { + std::string ap(ts.substr(ap_pos + 1)); + ts.resize(ap_pos); if (aliasf) { - int index = atoi(ap + 1); + int index = atoi(ap.c_str()); al = get_aliasf(index, &flags, dict); if (!al) { HUNSPELL_WARNING(stderr, "error: line %d: bad flag vector alias\n", dict->getlinenum()); - *ap = '\0'; } } else { - al = decode_flags(&flags, ap + 1, dict); + al = decode_flags(&flags, ap.c_str(), dict); if (al == -1) { HUNSPELL_WARNING(stderr, "Can't allocate memory.\n"); delete dict; @@ -607,16 +595,15 @@ int HashMgr::load_tables(const char* tpath, const char* key) { } } else { al = 0; - ap = NULL; flags = NULL; } int captype; - int wbl = strlen(ts); int wcl = get_clen_and_captype(ts, &captype); + const std::string *dp_str = dp.empty() ? NULL : &dp; // add the word and its index plus its capitalized form optionally - if (add_word(ts, wbl, wcl, flags, al, dp, false) || - add_hidden_capitalized_word(ts, wcl, flags, al, dp, captype)) { + if (add_word(ts, wcl, flags, al, dp_str, false) || + add_hidden_capitalized_word(ts, wcl, flags, al, dp_str, captype)) { delete dict; return 5; } @@ -639,15 +626,15 @@ int HashMgr::hash(const char* word) const { return (unsigned long)hv % tablesize; } -int HashMgr::decode_flags(unsigned short** result, char* flags, FileMgr* af) { +int HashMgr::decode_flags(unsigned short** result, const std::string& flags, FileMgr* af) const { int len; - if (*flags == '\0') { + if (flags.empty()) { *result = NULL; return 0; } switch (flag_mode) { case FLAG_LONG: { // two-character flags (1x2yZz -> 1x 2y Zz) - len = strlen(flags); + len = flags.size(); if (len % 2 == 1) HUNSPELL_WARNING(stderr, "error: line %d: bad flagvector\n", af->getlinenum()); @@ -656,29 +643,27 @@ int HashMgr::decode_flags(unsigned short** result, char* flags, FileMgr* af) { if (!*result) return -1; for (int i = 0; i < len; i++) { - (*result)[i] = (((unsigned short)flags[i * 2]) << 8) + - (unsigned short)flags[i * 2 + 1]; + (*result)[i] = ((unsigned short)((unsigned char)flags[i * 2]) << 8) + + (unsigned char)flags[i * 2 + 1]; } break; } case FLAG_NUM: { // decimal numbers separated by comma (4521,23,233 -> 4521 // 23 233) - int i; len = 1; - char* src = flags; unsigned short* dest; - char* p; - for (p = flags; *p; p++) { - if (*p == ',') + for (size_t i = 0; i < flags.size(); ++i) { + if (flags[i] == ',') len++; } *result = (unsigned short*)malloc(len * sizeof(unsigned short)); if (!*result) return -1; dest = *result; - for (p = flags; *p; p++) { + const char* src = flags.c_str(); + for (const char* p = src; *p; p++) { if (*p == ',') { - i = atoi(src); + int i = atoi(src); if (i >= DEFAULTFLAGS) HUNSPELL_WARNING( stderr, "error: line %d: flag id %d is too large (max: %d)\n", @@ -691,7 +676,7 @@ int HashMgr::decode_flags(unsigned short** result, char* flags, FileMgr* af) { dest++; } } - i = atoi(src); + int i = atoi(src); if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: line %d: flag id %d is too large (max: %d)\n", @@ -714,13 +699,13 @@ int HashMgr::decode_flags(unsigned short** result, char* flags, FileMgr* af) { } default: { // Ispell's one-character flags (erfg -> e r f g) unsigned short* dest; - len = strlen(flags); + len = flags.size(); *result = (unsigned short*)malloc(len * sizeof(unsigned short)); if (!*result) return -1; dest = *result; - for (unsigned char* p = (unsigned char*)flags; *p; p++) { - *dest = (unsigned short)*p; + for (size_t i = 0; i < flags.size(); ++i) { + *dest = (unsigned char)flags[i]; dest++; } } @@ -728,12 +713,77 @@ int HashMgr::decode_flags(unsigned short** result, char* flags, FileMgr* af) { return len; } -unsigned short HashMgr::decode_flag(const char* f) { +bool HashMgr::decode_flags(std::vector& result, const std::string& flags, FileMgr* af) const { + if (flags.empty()) { + return false; + } + switch (flag_mode) { + case FLAG_LONG: { // two-character flags (1x2yZz -> 1x 2y Zz) + size_t len = flags.size(); + if (len % 2 == 1) + HUNSPELL_WARNING(stderr, "error: line %d: bad flagvector\n", + af->getlinenum()); + len /= 2; + result.reserve(result.size() + len); + for (size_t i = 0; i < len; ++i) { + result.push_back(((unsigned short)((unsigned char)flags[i * 2]) << 8) + + (unsigned char)flags[i * 2 + 1]); + } + break; + } + case FLAG_NUM: { // decimal numbers separated by comma (4521,23,233 -> 4521 + // 23 233) + const char* src = flags.c_str(); + for (const char* p = src; *p; p++) { + if (*p == ',') { + int i = atoi(src); + if (i >= DEFAULTFLAGS) + HUNSPELL_WARNING( + stderr, "error: line %d: flag id %d is too large (max: %d)\n", + af->getlinenum(), i, DEFAULTFLAGS - 1); + result.push_back((unsigned short)i); + if (result.back() == 0) + HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n", + af->getlinenum()); + src = p + 1; + } + } + int i = atoi(src); + if (i >= DEFAULTFLAGS) + HUNSPELL_WARNING(stderr, + "error: line %d: flag id %d is too large (max: %d)\n", + af->getlinenum(), i, DEFAULTFLAGS - 1); + result.push_back((unsigned short)i); + if (result.back() == 0) + HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n", + af->getlinenum()); + break; + } + case FLAG_UNI: { // UTF-8 characters + std::vector w; + u8_u16(w, flags); + size_t len = w.size(); + size_t origsize = result.size(); + result.resize(origsize + len); + memcpy(&result[origsize], &w[0], len * sizeof(short)); + break; + } + default: { // Ispell's one-character flags (erfg -> e r f g) + result.reserve(flags.size()); + for (size_t i = 0; i < flags.size(); ++i) { + result.push_back((unsigned char)flags[i]); + } + } + } + return true; +} + +unsigned short HashMgr::decode_flag(const char* f) const { unsigned short s = 0; int i; switch (flag_mode) { case FLAG_LONG: - s = ((unsigned short)f[0] << 8) + (unsigned short)f[1]; + s = ((unsigned short)((unsigned char)f[0]) << 8) + (unsigned char)f[1]; break; case FLAG_NUM: i = atoi(f); @@ -750,14 +800,14 @@ unsigned short HashMgr::decode_flag(const char* f) { break; } default: - s = (unsigned short)*((unsigned char*)f); + s = *(unsigned char*)f; } if (s == 0) HUNSPELL_WARNING(stderr, "error: 0 is wrong flag id\n"); return s; } -char* HashMgr::encode_flag(unsigned short f) { +char* HashMgr::encode_flag(unsigned short f) const { if (f == 0) return mystrdup("(NULL)"); std::string ch; @@ -780,7 +830,6 @@ char* HashMgr::encode_flag(unsigned short f) { // read in aff file and set flag mode int HashMgr::load_config(const char* affpath, const char* key) { - char* line; // io buffers int firstline = 1; // open the affix file @@ -794,29 +843,31 @@ int HashMgr::load_config(const char* affpath, const char* key) { // read in each line ignoring any that do not // start with a known line type indicator - while ((line = afflst->getline()) != NULL) { + std::string line; + while (afflst->getline(line)) { mychomp(line); /* remove byte order mark */ if (firstline) { firstline = 0; - if (strncmp(line, "\xEF\xBB\xBF", 3) == 0) - memmove(line, line + 3, strlen(line + 3) + 1); + if (line.compare(0, 3, "\xEF\xBB\xBF", 3) == 0) { + line.erase(0, 3); + } } /* parse in the try string */ - if ((strncmp(line, "FLAG", 4) == 0) && isspace(line[4])) { + if ((line.compare(0, 4, "FLAG", 4) == 0) && line.size() > 4 && isspace(line[4])) { if (flag_mode != FLAG_CHAR) { HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of the FLAG " "affix file parameter\n", afflst->getlinenum()); } - if (strstr(line, "long")) + if (line.find("long") != std::string::npos) flag_mode = FLAG_LONG; - if (strstr(line, "num")) + if (line.find("num") != std::string::npos) flag_mode = FLAG_NUM; - if (strstr(line, "UTF-8")) + if (line.find("UTF-8") != std::string::npos) flag_mode = FLAG_UNI; if (flag_mode == FLAG_CHAR) { HUNSPELL_WARNING( @@ -825,21 +876,22 @@ int HashMgr::load_config(const char* affpath, const char* key) { afflst->getlinenum()); } } - if (strncmp(line, "FORBIDDENWORD", 13) == 0) { - char* st = NULL; - if (parse_string(line, &st, afflst->getlinenum())) { + + if (line.compare(0, 13, "FORBIDDENWORD", 13) == 0) { + std::string st; + if (!parse_string(line, st, afflst->getlinenum())) { delete afflst; return 1; } - forbiddenword = decode_flag(st); - free(st); + forbiddenword = decode_flag(st.c_str()); } - if (strncmp(line, "SET", 3) == 0) { - if (parse_string(line, &enc, afflst->getlinenum())) { + + if (line.compare(0, 3, "SET", 3) == 0) { + if (!parse_string(line, enc, afflst->getlinenum())) { delete afflst; return 1; } - if (strcmp(enc, "UTF-8") == 0) { + if (enc == "UTF-8") { utf8 = 1; #ifndef OPENOFFICEORG #ifndef MOZILLA_CLIENT @@ -849,8 +901,9 @@ int HashMgr::load_config(const char* affpath, const char* key) { } else csconv = get_current_cs(enc); } - if (strncmp(line, "LANG", 4) == 0) { - if (parse_string(line, &lang, afflst->getlinenum())) { + + if (line.compare(0, 4, "LANG", 4) == 0) { + if (!parse_string(line, lang, afflst->getlinenum())) { delete afflst; return 1; } @@ -859,34 +912,36 @@ int HashMgr::load_config(const char* affpath, const char* key) { /* parse in the ignored characters (for example, Arabic optional diacritics * characters */ - if (strncmp(line, "IGNORE", 6) == 0) { - if (!parse_array(line, &ignorechars, ignorechars_utf16, + if (line.compare(0, 6, "IGNORE", 6) == 0) { + if (!parse_array(line, ignorechars, ignorechars_utf16, utf8, afflst->getlinenum())) { delete afflst; return 1; } } - if ((strncmp(line, "AF", 2) == 0) && isspace(line[2])) { - if (parse_aliasf(line, afflst)) { + if ((line.compare(0, 2, "AF", 2) == 0) && line.size() > 2 && isspace(line[2])) { + if (!parse_aliasf(line, afflst)) { delete afflst; return 1; } } - if ((strncmp(line, "AM", 2) == 0) && isspace(line[2])) { - if (parse_aliasm(line, afflst)) { + if ((line.compare(0, 2, "AM", 2) == 0) && line.size() > 2 && isspace(line[2])) { + if (!parse_aliasm(line, afflst)) { delete afflst; return 1; } } - if (strncmp(line, "COMPLEXPREFIXES", 15) == 0) + if (line.compare(0, 15, "COMPLEXPREFIXES", 15) == 0) complexprefixes = 1; - if (((strncmp(line, "SFX", 3) == 0) || (strncmp(line, "PFX", 3) == 0)) && - isspace(line[3])) + + if (((line.compare(0, 3, "SFX", 3) == 0) || + (line.compare(0, 3, "PFX", 3) == 0)) && line.size() > 3 && isspace(line[3])) break; } + if (csconv == NULL) csconv = get_current_cs(SPELL_ENCODING); delete afflst; @@ -894,57 +949,54 @@ int HashMgr::load_config(const char* affpath, const char* key) { } /* parse in the ALIAS table */ -int HashMgr::parse_aliasf(char* line, FileMgr* af) { +bool HashMgr::parse_aliasf(const std::string& line, FileMgr* af) { if (numaliasf != 0) { HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); - return 1; + return false; } - char* tp = line; - char* piece; int i = 0; int np = 0; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - np++; - break; - } - case 1: { - numaliasf = atoi(piece); - if (numaliasf < 1) { - numaliasf = 0; - aliasf = NULL; - aliasflen = NULL; - HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", - af->getlinenum()); - return 1; - } - aliasf = - (unsigned short**)malloc(numaliasf * sizeof(unsigned short*)); - aliasflen = - (unsigned short*)malloc(numaliasf * sizeof(unsigned short)); - if (!aliasf || !aliasflen) { - numaliasf = 0; - if (aliasf) - free(aliasf); - if (aliasflen) - free(aliasflen); - aliasf = NULL; - aliasflen = NULL; - return 1; - } - np++; - break; - } - default: - break; + std::string::const_iterator iter = line.begin(); + std::string::const_iterator start_piece = mystrsep(line, iter); + while (start_piece != line.end()) { + switch (i) { + case 0: { + np++; + break; } - i++; + case 1: { + numaliasf = atoi(std::string(start_piece, iter).c_str()); + if (numaliasf < 1) { + numaliasf = 0; + aliasf = NULL; + aliasflen = NULL; + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", + af->getlinenum()); + return false; + } + aliasf = + (unsigned short**)malloc(numaliasf * sizeof(unsigned short*)); + aliasflen = + (unsigned short*)malloc(numaliasf * sizeof(unsigned short)); + if (!aliasf || !aliasflen) { + numaliasf = 0; + if (aliasf) + free(aliasf); + if (aliasflen) + free(aliasflen); + aliasf = NULL; + aliasflen = NULL; + return false; + } + np++; + break; + } + default: + break; } - piece = mystrsep(&tp, 0); + ++i; + start_piece = mystrsep(line, iter); } if (np != 2) { numaliasf = 0; @@ -954,48 +1006,47 @@ int HashMgr::parse_aliasf(char* line, FileMgr* af) { aliasflen = NULL; HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); - return 1; + return false; } /* now parse the numaliasf lines to read in the remainder of the table */ - char* nl; for (int j = 0; j < numaliasf; j++) { - if ((nl = af->getline()) == NULL) - return 1; + std::string nl; + if (!af->getline(nl)) + return false; mychomp(nl); - tp = nl; i = 0; aliasf[j] = NULL; aliasflen[j] = 0; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - if (strncmp(piece, "AF", 2) != 0) { - numaliasf = 0; - free(aliasf); - free(aliasflen); - aliasf = NULL; - aliasflen = NULL; - HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", - af->getlinenum()); - return 1; - } - break; + iter = nl.begin(); + start_piece = mystrsep(nl, iter); + while (start_piece != nl.end()) { + switch (i) { + case 0: { + if (nl.compare(start_piece - nl.begin(), 2, "AF", 2) != 0) { + numaliasf = 0; + free(aliasf); + free(aliasflen); + aliasf = NULL; + aliasflen = NULL; + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", + af->getlinenum()); + return false; } - case 1: { - aliasflen[j] = - (unsigned short)decode_flags(&(aliasf[j]), piece, af); - std::sort(aliasf[j], aliasf[j] + aliasflen[j]); - break; - } - default: - break; + break; } - i++; + case 1: { + std::string piece(start_piece, iter); + aliasflen[j] = + (unsigned short)decode_flags(&(aliasf[j]), piece, af); + std::sort(aliasf[j], aliasf[j] + aliasflen[j]); + break; + } + default: + break; } - piece = mystrsep(&tp, 0); + ++i; + start_piece = mystrsep(nl, iter); } if (!aliasf[j]) { free(aliasf); @@ -1005,17 +1056,17 @@ int HashMgr::parse_aliasf(char* line, FileMgr* af) { numaliasf = 0; HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); - return 1; + return false; } } - return 0; + return true; } -int HashMgr::is_aliasf() { +int HashMgr::is_aliasf() const { return (aliasf != NULL); } -int HashMgr::get_aliasf(int index, unsigned short** fvec, FileMgr* af) { +int HashMgr::get_aliasf(int index, unsigned short** fvec, FileMgr* af) const { if ((index > 0) && (index <= numaliasf)) { *fvec = aliasf[index - 1]; return aliasflen[index - 1]; @@ -1027,45 +1078,42 @@ int HashMgr::get_aliasf(int index, unsigned short** fvec, FileMgr* af) { } /* parse morph alias definitions */ -int HashMgr::parse_aliasm(char* line, FileMgr* af) { +bool HashMgr::parse_aliasm(const std::string& line, FileMgr* af) { if (numaliasm != 0) { HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); - return 1; + return false; } - char* tp = line; - char* piece; int i = 0; int np = 0; - piece = mystrsep(&tp, 0); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - np++; - break; - } - case 1: { - numaliasm = atoi(piece); - if (numaliasm < 1) { - HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", - af->getlinenum()); - return 1; - } - aliasm = (char**)malloc(numaliasm * sizeof(char*)); - if (!aliasm) { - numaliasm = 0; - return 1; - } - np++; - break; - } - default: - break; + std::string::const_iterator iter = line.begin(); + std::string::const_iterator start_piece = mystrsep(line, iter); + while (start_piece != line.end()) { + switch (i) { + case 0: { + np++; + break; } - i++; + case 1: { + numaliasm = atoi(std::string(start_piece, iter).c_str()); + if (numaliasm < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", + af->getlinenum()); + return false; + } + aliasm = (char**)malloc(numaliasm * sizeof(char*)); + if (!aliasm) { + numaliasm = 0; + return false; + } + np++; + break; + } + default: + break; } - piece = mystrsep(&tp, 0); + ++i; + start_piece = mystrsep(line, iter); } if (np != 2) { numaliasm = 0; @@ -1073,55 +1121,50 @@ int HashMgr::parse_aliasm(char* line, FileMgr* af) { aliasm = NULL; HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); - return 1; + return false; } /* now parse the numaliasm lines to read in the remainder of the table */ - char* nl = line; for (int j = 0; j < numaliasm; j++) { - if ((nl = af->getline()) == NULL) - return 1; + std::string nl; + if (!af->getline(nl)) + return false; mychomp(nl); - tp = nl; - i = 0; aliasm[j] = NULL; - piece = mystrsep(&tp, ' '); - while (piece) { - if (*piece != '\0') { - switch (i) { - case 0: { - if (strncmp(piece, "AM", 2) != 0) { - HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", - af->getlinenum()); - numaliasm = 0; - free(aliasm); - aliasm = NULL; - return 1; - } - break; + iter = nl.begin(); + i = 0; + start_piece = mystrsep(nl, iter); + while (start_piece != nl.end()) { + switch (i) { + case 0: { + if (nl.compare(start_piece - nl.begin(), 2, "AM", 2) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", + af->getlinenum()); + numaliasm = 0; + free(aliasm); + aliasm = NULL; + return false; } - case 1: { - // add the remaining of the line - if (*tp) { - *(tp - 1) = ' '; - tp = tp + strlen(tp); - } - std::string chunk(piece); - if (complexprefixes) { - if (utf8) - reverseword_utf(chunk); - else - reverseword(chunk); - } - aliasm[j] = mystrdup(chunk.c_str()); - break; - } - default: - break; + break; } - i++; + case 1: { + // add the remaining of the line + std::string::const_iterator end = nl.end(); + std::string chunk(start_piece, end); + if (complexprefixes) { + if (utf8) + reverseword_utf(chunk); + else + reverseword(chunk); + } + aliasm[j] = mystrdup(chunk.c_str()); + break; + } + default: + break; } - piece = mystrsep(&tp, ' '); + ++i; + start_piece = mystrsep(nl, iter); } if (!aliasm[j]) { numaliasm = 0; @@ -1129,17 +1172,17 @@ int HashMgr::parse_aliasm(char* line, FileMgr* af) { aliasm = NULL; HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); - return 1; + return false; } } - return 0; + return true; } -int HashMgr::is_aliasm() { +int HashMgr::is_aliasm() const { return (aliasm != NULL); } -char* HashMgr::get_aliasm(int index) { +char* HashMgr::get_aliasm(int index) const { if ((index > 0) && (index <= numaliasm)) return aliasm[index - 1]; HUNSPELL_WARNING(stderr, "error: bad morph. alias index: %d\n", index); diff --git a/extensions/spellcheck/hunspell/src/hashmgr.hxx b/extensions/spellcheck/hunspell/src/hashmgr.hxx index 95b06b13f97c..812171af2b5e 100644 --- a/extensions/spellcheck/hunspell/src/hashmgr.hxx +++ b/extensions/spellcheck/hunspell/src/hashmgr.hxx @@ -71,10 +71,8 @@ * SUCH DAMAGE. */ -#ifndef _HASHMGR_HXX_ -#define _HASHMGR_HXX_ - -#include "hunvisapi.h" +#ifndef HASHMGR_HXX_ +#define HASHMGR_HXX_ #include #include @@ -86,7 +84,7 @@ enum flag { FLAG_CHAR, FLAG_LONG, FLAG_NUM, FLAG_UNI }; -class LIBHUNSPELL_DLL_EXPORTED HashMgr { +class HashMgr { int tablesize; struct hentry** tableptr; flag flag_mode; @@ -94,10 +92,10 @@ class LIBHUNSPELL_DLL_EXPORTED HashMgr { int utf8; unsigned short forbiddenword; int langnum; - char* enc; - char* lang; + std::string enc; + std::string lang; struct cs_info* csconv; - char* ignorechars; + std::string ignorechars; std::vector ignorechars_utf16; int numaliasf; // flag vector `compression' with aliases unsigned short** aliasf; @@ -114,35 +112,35 @@ class LIBHUNSPELL_DLL_EXPORTED HashMgr { struct hentry* walk_hashtable(int& col, struct hentry* hp) const; int add(const std::string& word); - int add_with_affix(const char* word, const char* pattern); - int remove(const char* word); - int decode_flags(unsigned short** result, char* flags, FileMgr* af); - unsigned short decode_flag(const char* flag); - char* encode_flag(unsigned short flag); - int is_aliasf(); - int get_aliasf(int index, unsigned short** fvec, FileMgr* af); - int is_aliasm(); - char* get_aliasm(int index); + int add_with_affix(const std::string& word, const std::string& pattern); + int remove(const std::string& word); + int decode_flags(unsigned short** result, const std::string& flags, FileMgr* af) const; + bool decode_flags(std::vector& result, const std::string& flags, FileMgr* af) const; + unsigned short decode_flag(const char* flag) const; + char* encode_flag(unsigned short flag) const; + int is_aliasf() const; + int get_aliasf(int index, unsigned short** fvec, FileMgr* af) const; + int is_aliasm() const; + char* get_aliasm(int index) const; private: int get_clen_and_captype(const std::string& word, int* captype); int load_tables(const char* tpath, const char* key); - int add_word(const char* word, - int wbl, + int add_word(const std::string& word, int wcl, unsigned short* ap, int al, - const char* desc, + const std::string* desc, bool onlyupcase); int load_config(const char* affpath, const char* key); - int parse_aliasf(char* line, FileMgr* af); + bool parse_aliasf(const std::string& line, FileMgr* af); int add_hidden_capitalized_word(const std::string& word, int wcl, unsigned short* flags, int al, - char* dp, + const std::string* dp, int captype); - int parse_aliasm(char* line, FileMgr* af); + bool parse_aliasm(const std::string& line, FileMgr* af); int remove_forbidden_flag(const std::string& word); }; diff --git a/extensions/spellcheck/hunspell/src/htypes.hxx b/extensions/spellcheck/hunspell/src/htypes.hxx index d24439441690..1e6c11839099 100644 --- a/extensions/spellcheck/hunspell/src/htypes.hxx +++ b/extensions/spellcheck/hunspell/src/htypes.hxx @@ -38,8 +38,8 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef _HTYPES_HXX_ -#define _HTYPES_HXX_ +#ifndef HTYPES_HXX_ +#define HTYPES_HXX_ #define ROTATE_LEN 5 diff --git a/extensions/spellcheck/hunspell/src/hunspell.cxx b/extensions/spellcheck/hunspell/src/hunspell.cxx index f7c1581087d2..abcdb8f3e07f 100644 --- a/extensions/spellcheck/hunspell/src/hunspell.cxx +++ b/extensions/spellcheck/hunspell/src/hunspell.cxx @@ -75,35 +75,100 @@ #include #include +#include "affixmgr.hxx" #include "hunspell.hxx" +#include "suggestmgr.hxx" #include "hunspell.h" -#ifndef MOZILLA_CLIENT -#include "config.h" -#endif #include "csutil.hxx" #include #include -#define MAXWORDLEN 176 #define MAXWORDUTF8LEN (MAXWORDLEN * 3) -Hunspell::Hunspell(const char* affpath, const char* dpath, const char* key) { - encoding = NULL; +class HunspellImpl +{ +public: + HunspellImpl(const char* affpath, const char* dpath, const char* key); + ~HunspellImpl(); + int add_dic(const char* dpath, const char* key); + std::vector suffix_suggest(const std::string& root_word); + std::vector generate(const std::string& word, const std::vector& pl); + std::vector generate(const std::string& word, const std::string& pattern); + std::vector stem(const std::string& word); + std::vector stem(const std::vector& morph); + std::vector analyze(const std::string& word); + int get_langnum() const; + bool input_conv(const std::string& word, std::string& dest); + bool spell(const std::string& word, int* info = NULL, std::string* root = NULL); + std::vector suggest(const std::string& word); + const std::string& get_wordchars() const; + const std::vector& get_wordchars_utf16() const; + const std::string& get_dict_encoding() const; + int add(const std::string& word); + int add_with_affix(const std::string& word, const std::string& example); + int remove(const std::string& word); + const std::string& get_version() const; + struct cs_info* get_csconv(); + std::vector dic_encoding_vec; + +private: + AffixMgr* pAMgr; + std::vector m_HMgrs; + SuggestMgr* pSMgr; + char* affixpath; + std::string encoding; + struct cs_info* csconv; + int langnum; + int utf8; + int complexprefixes; + std::vector wordbreak; + +private: + void cleanword(std::string& dest, const std::string&, int* pcaptype, int* pabbrev); + size_t cleanword2(std::string& dest, + std::vector& dest_u, + const std::string& src, + int* pcaptype, + size_t* pabbrev); + void mkinitcap(std::string& u8); + int mkinitcap2(std::string& u8, std::vector& u16); + int mkinitsmall2(std::string& u8, std::vector& u16); + void mkallcap(std::string& u8); + int mkallsmall2(std::string& u8, std::vector& u16); + struct hentry* checkword(const std::string& source, int* info, std::string* root); + std::string sharps_u8_l1(const std::string& source); + hentry* + spellsharps(std::string& base, size_t start_pos, int, int, int* info, std::string* root); + int is_keepcase(const hentry* rv); + void insert_sug(std::vector& slst, const std::string& word); + void cat_result(std::string& result, const std::string& st); + std::vector spellml(const std::string& word); + std::string get_xml_par(const char* par); + const char* get_xml_pos(const char* s, const char* attr); + std::vector get_xml_list(const char* list, const char* tag); + int check_xml_par(const char* q, const char* attr, const char* value); +private: + HunspellImpl(const HunspellImpl&); + HunspellImpl& operator=(const HunspellImpl&); +}; + +Hunspell::Hunspell(const char* affpath, const char* dpath, const char* key) + : m_Impl(new HunspellImpl(affpath, dpath, key)) { +} + +HunspellImpl::HunspellImpl(const char* affpath, const char* dpath, const char* key) { csconv = NULL; utf8 = 0; complexprefixes = 0; affixpath = mystrdup(affpath); - maxdic = 0; /* first set up the hash manager */ - pHMgr[0] = new HashMgr(dpath, affpath, key); - if (pHMgr[0]) - maxdic = 1; + m_HMgrs.push_back(new HashMgr(dpath, affpath, key)); /* next set up the affix manager */ /* it needs access to the hash manager lookup methods */ - pAMgr = new AffixMgr(affpath, pHMgr, &maxdic, key); + pAMgr = new AffixMgr(affpath, m_HMgrs, key); /* get the preferred try string and the dictionary */ /* encoding from the Affix Manager for that dictionary */ @@ -116,6 +181,9 @@ Hunspell::Hunspell(const char* affpath, const char* dpath, const char* key) { complexprefixes = pAMgr->get_complexprefixes(); wordbreak = pAMgr->get_breaktable(); + dic_encoding_vec.resize(encoding.size()+1); + strcpy(&dic_encoding_vec[0], encoding.c_str()); + /* and finally set up the suggestion manager */ pSMgr = new SuggestMgr(try_string, MAXSUGGESTION, pAMgr); if (try_string) @@ -123,20 +191,20 @@ Hunspell::Hunspell(const char* affpath, const char* dpath, const char* key) { } Hunspell::~Hunspell() { + delete m_Impl; +} + +HunspellImpl::~HunspellImpl() { delete pSMgr; delete pAMgr; - for (int i = 0; i < maxdic; i++) - delete pHMgr[i]; - maxdic = 0; + for (size_t i = 0; i < m_HMgrs.size(); ++i) + delete m_HMgrs[i]; pSMgr = NULL; pAMgr = NULL; #ifdef MOZILLA_CLIENT delete[] csconv; #endif csconv = NULL; - if (encoding) - free(encoding); - encoding = NULL; if (affixpath) free(affixpath); affixpath = NULL; @@ -144,13 +212,14 @@ Hunspell::~Hunspell() { // load extra dictionaries int Hunspell::add_dic(const char* dpath, const char* key) { - if (maxdic == MAXDIC || !affixpath) - return 1; - pHMgr[maxdic] = new HashMgr(dpath, affixpath, key); - if (pHMgr[maxdic]) - maxdic++; - else + return m_Impl->add_dic(dpath, key); +} + +// load extra dictionaries +int HunspellImpl::add_dic(const char* dpath, const char* key) { + if (!affixpath) return 1; + m_HMgrs.push_back(new HashMgr(dpath, affixpath, key)); return 0; } @@ -161,16 +230,15 @@ int Hunspell::add_dic(const char* dpath, const char* key) { // set the capitalization type // return the length of the "cleaned" (and UTF-8 encoded) word -size_t Hunspell::cleanword2(std::string& dest, +size_t HunspellImpl::cleanword2(std::string& dest, std::vector& dest_utf, - const char* src, - int* nc, + const std::string& src, int* pcaptype, size_t* pabbrev) { dest.clear(); dest_utf.clear(); - const char* q = src; + const char* q = src.c_str(); // first skip over any leading blanks while ((*q != '\0') && (*q == ' ')) @@ -193,21 +261,20 @@ size_t Hunspell::cleanword2(std::string& dest, dest.append(q, nl); nl = dest.size(); if (utf8) { - *nc = u8_u16(dest_utf, dest); + u8_u16(dest_utf, dest); *pcaptype = get_captype_utf8(dest_utf, langnum); } else { *pcaptype = get_captype(dest, csconv); - *nc = nl; } return nl; } -void Hunspell::cleanword(std::string& dest, - const char* src, +void HunspellImpl::cleanword(std::string& dest, + const std::string& src, int* pcaptype, int* pabbrev) { dest.clear(); - const unsigned char* q = (const unsigned char*)src; + const unsigned char* q = (const unsigned char*)src.c_str(); int firstcap = 0; // first skip over any leading blanks @@ -277,7 +344,7 @@ void Hunspell::cleanword(std::string& dest, } } -void Hunspell::mkallcap(std::string& u8) { +void HunspellImpl::mkallcap(std::string& u8) { if (utf8) { std::vector u16; u8_u16(u16, u8); @@ -288,7 +355,7 @@ void Hunspell::mkallcap(std::string& u8) { } } -int Hunspell::mkallsmall2(std::string& u8, std::vector& u16) { +int HunspellImpl::mkallsmall2(std::string& u8, std::vector& u16) { if (utf8) { ::mkallsmall_utf(u16, langnum); u16_u8(u8, u16); @@ -299,19 +366,19 @@ int Hunspell::mkallsmall2(std::string& u8, std::vector& u16) { } // convert UTF-8 sharp S codes to latin 1 -std::string Hunspell::sharps_u8_l1(const std::string& source) { +std::string HunspellImpl::sharps_u8_l1(const std::string& source) { std::string dest(source); mystrrep(dest, "\xC3\x9F", "\xDF"); return dest; } // recursive search for right ss - sharp s permutations -hentry* Hunspell::spellsharps(std::string& base, +hentry* HunspellImpl::spellsharps(std::string& base, size_t n_pos, int n, int repnum, int* info, - char** root) { + std::string* root) { size_t pos = base.find("ss", n_pos); if (pos != std::string::npos && (n < MAXSHARPS)) { base[pos] = '\xC3'; @@ -326,36 +393,28 @@ hentry* Hunspell::spellsharps(std::string& base, return h; } else if (repnum > 0) { if (utf8) - return checkword(base.c_str(), info, root); + return checkword(base, info, root); std::string tmp(sharps_u8_l1(base)); - return checkword(tmp.c_str(), info, root); + return checkword(tmp, info, root); } return NULL; } -int Hunspell::is_keepcase(const hentry* rv) { +int HunspellImpl::is_keepcase(const hentry* rv) { return pAMgr && rv->astr && pAMgr->get_keepcase() && TESTAFF(rv->astr, pAMgr->get_keepcase(), rv->alen); } -/* insert a word to the beginning of the suggestion array and return ns */ -int Hunspell::insert_sug(char*** slst, const char* word, int ns) { - if (!*slst) - return ns; - char* dup = mystrdup(word); - if (!dup) - return ns; - if (ns == MAXSUGGESTION) { - ns--; - free((*slst)[ns]); - } - for (int k = ns; k > 0; k--) - (*slst)[k] = (*slst)[k - 1]; - (*slst)[0] = dup; - return ns + 1; +/* insert a word to the beginning of the suggestion array */ +void HunspellImpl::insert_sug(std::vector& slst, const std::string& word) { + slst.insert(slst.begin(), word); } -int Hunspell::spell(const char* word, int* info, char** root) { +bool Hunspell::spell(const std::string& word, int* info, std::string* root) { + return m_Impl->spell(word, info, root); +} + +bool HunspellImpl::spell(const std::string& word, int* info, std::string* root) { struct hentry* rv = NULL; int info2 = 0; @@ -365,15 +424,14 @@ int Hunspell::spell(const char* word, int* info, char** root) { *info = 0; // Hunspell supports XML input of the simplified API (see manual) - if (strcmp(word, SPELL_XML) == 0) - return 1; - int nc = strlen(word); + if (word == SPELL_XML) + return true; if (utf8) { - if (nc >= MAXWORDUTF8LEN) - return 0; + if (word.size() >= MAXWORDUTF8LEN) + return false; } else { - if (nc >= MAXWORDLEN) - return 0; + if (word.size() >= MAXWORDLEN) + return false; } int captype = NOCAP; size_t abbv = 0; @@ -383,17 +441,15 @@ int Hunspell::spell(const char* word, int* info, char** root) { std::vector sunicw; // input conversion - RepList* rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL; + RepList* rl = pAMgr ? pAMgr->get_iconvtable() : NULL; { std::string wspace; - int convstatus = rl ? rl->conv(word, wspace) : 0; - if (convstatus < 0) - return 0; - else if (convstatus > 0) - wl = cleanword2(scw, sunicw, wspace.c_str(), &nc, &captype, &abbv); + bool convstatus = rl ? rl->conv(word, wspace) : false; + if (convstatus) + wl = cleanword2(scw, sunicw, wspace, &captype, &abbv); else - wl = cleanword2(scw, sunicw, word, &nc, &captype, &abbv); + wl = cleanword2(scw, sunicw, word, &captype, &abbv); } #ifdef MOZILLA_CLIENT @@ -402,10 +458,10 @@ int Hunspell::spell(const char* word, int* info, char** root) { abbv = 1; #endif - if (wl == 0 || maxdic == 0) - return 1; + if (wl == 0 || m_HMgrs.empty()) + return true; if (root) - *root = NULL; + root->clear(); // allow numbers with dots, dashes and commas (but forbid double separators: // "..", "--" etc.) @@ -424,7 +480,7 @@ int Hunspell::spell(const char* word, int* info, char** root) { break; } if ((i == wl) && (nstate == NNUM)) - return 1; + return true; switch (captype) { case HUHCAP: @@ -433,22 +489,22 @@ int Hunspell::spell(const char* word, int* info, char** root) { *info += SPELL_ORIGCAP; /* FALLTHROUGH */ case NOCAP: - rv = checkword(scw.c_str(), info, root); + rv = checkword(scw, info, root); if ((abbv) && !(rv)) { std::string u8buffer(scw); u8buffer.push_back('.'); - rv = checkword(u8buffer.c_str(), info, root); + rv = checkword(u8buffer, info, root); } break; case ALLCAP: { *info += SPELL_ORIGCAP; - rv = checkword(scw.c_str(), info, root); + rv = checkword(scw, info, root); if (rv) break; if (abbv) { std::string u8buffer(scw); u8buffer.push_back('.'); - rv = checkword(u8buffer.c_str(), info, root); + rv = checkword(u8buffer, info, root); if (rv) break; } @@ -470,18 +526,18 @@ int Hunspell::spell(const char* word, int* info, char** root) { scw = part1 + part2; sunicw = part1u; sunicw.insert(sunicw.end(), part2u.begin(), part2u.end()); - rv = checkword(scw.c_str(), info, root); + rv = checkword(scw, info, root); if (rv) break; } else { mkinitcap2(part2, sunicw); scw = part1 + part2; - rv = checkword(scw.c_str(), info, root); + rv = checkword(scw, info, root); if (rv) break; } mkinitcap2(scw, sunicw); - rv = checkword(scw.c_str(), info, root); + rv = checkword(scw, info, root); if (rv) break; } @@ -516,7 +572,7 @@ int Hunspell::spell(const char* word, int* info, char** root) { mkinitcap2(scw, sunicw); if (captype == INITCAP) *info += SPELL_INITCAP; - rv = checkword(scw.c_str(), info, root); + rv = checkword(scw, info, root); if (captype == INITCAP) *info -= SPELL_INITCAP; // forbid bad capitalization @@ -531,16 +587,16 @@ int Hunspell::spell(const char* word, int* info, char** root) { if (rv) break; - rv = checkword(u8buffer.c_str(), info, root); + rv = checkword(u8buffer, info, root); if (abbv && !rv) { u8buffer.push_back('.'); - rv = checkword(u8buffer.c_str(), info, root); + rv = checkword(u8buffer, info, root); if (!rv) { u8buffer = scw; u8buffer.push_back('.'); if (captype == INITCAP) *info += SPELL_INITCAP; - rv = checkword(u8buffer.c_str(), info, root); + rv = checkword(u8buffer, info, root); if (captype == INITCAP) *info -= SPELL_INITCAP; if (rv && is_keepcase(rv) && (captype == ALLCAP)) @@ -565,89 +621,86 @@ int Hunspell::spell(const char* word, int* info, char** root) { TESTAFF(rv->astr, pAMgr->get_warn(), rv->alen)) { *info += SPELL_WARN; if (pAMgr->get_forbidwarn()) - return 0; - return HUNSPELL_OK_WARN; + return false; + return true; } - return HUNSPELL_OK; + return true; } // recursive breaking at break points - if (wordbreak) { + if (!wordbreak.empty()) { int nbr = 0; wl = scw.size(); - int numbreak = pAMgr ? pAMgr->get_numbreak() : 0; // calculate break points for recursion limit - for (int j = 0; j < numbreak; j++) { - size_t len = strlen(wordbreak[j]); + for (size_t j = 0; j < wordbreak.size(); ++j) { size_t pos = 0; - while ((pos = scw.find(wordbreak[j], pos, len)) != std::string::npos) { + while ((pos = scw.find(wordbreak[j], pos)) != std::string::npos) { ++nbr; - pos += len; + pos += wordbreak[j].size(); } } if (nbr >= 10) - return 0; + return false; // check boundary patterns (^begin and end$) - for (int j = 0; j < numbreak; j++) { - size_t plen = strlen(wordbreak[j]); + for (size_t j = 0; j < wordbreak.size(); ++j) { + size_t plen = wordbreak[j].size(); if (plen == 1 || plen > wl) continue; if (wordbreak[j][0] == '^' && - scw.compare(0, plen - 1, wordbreak[j] + 1, plen -1) == 0 && spell(scw.c_str() + plen - 1)) - return 1; + scw.compare(0, plen - 1, wordbreak[j], 1, plen -1) == 0 && spell(scw.substr(plen - 1))) + return true; if (wordbreak[j][plen - 1] == '$' && - scw.compare(wl - plen + 1, plen - 1, wordbreak[j], plen - 1) == 0) { - char r = scw[wl - plen + 1]; - scw[wl - plen + 1] = '\0'; - if (spell(scw.c_str())) - return 1; - scw[wl - plen + 1] = r; + scw.compare(wl - plen + 1, plen - 1, wordbreak[j], 0, plen - 1) == 0) { + std::string suffix(scw.substr(wl - plen + 1)); + scw.resize(wl - plen + 1); + if (spell(scw)) + return true; + scw.append(suffix); } } // other patterns - for (int j = 0; j < numbreak; j++) { - size_t plen = strlen(wordbreak[j]); + for (size_t j = 0; j < wordbreak.size(); ++j) { + size_t plen = wordbreak[j].size(); size_t found = scw.find(wordbreak[j]); if ((found > 0) && (found < wl - plen)) { - if (!spell(scw.c_str() + found + plen)) + if (!spell(scw.substr(found + plen))) continue; - char r = scw[found]; - scw[found] = '\0'; + std::string suffix(scw.substr(found)); + scw.resize(found); // examine 2 sides of the break point - if (spell(scw.c_str())) - return 1; - scw[found] = r; + if (spell(scw)) + return true; + scw.append(suffix); // LANG_hu: spec. dash rule - if (langnum == LANG_hu && strcmp(wordbreak[j], "-") == 0) { - r = scw[found + 1]; - scw[found + 1] = '\0'; - if (spell(scw.c_str())) - return 1; // check the first part with dash - scw[found + 1] = r; + if (langnum == LANG_hu && wordbreak[j] == "-") { + suffix = scw.substr(found + 1); + scw.resize(found + 1); + if (spell(scw)) + return true; // check the first part with dash + scw.append(suffix); } // end of LANG specific region } } } - return 0; + return false; } -struct hentry* Hunspell::checkword(const char* w, int* info, char** root) { - struct hentry* he = NULL; +struct hentry* HunspellImpl::checkword(const std::string& w, int* info, std::string* root) { bool usebuffer = false; - int len, i; std::string w2; const char* word; + int len; - char* ignoredchars = pAMgr ? pAMgr->get_ignore() : NULL; + const char* ignoredchars = pAMgr ? pAMgr->get_ignore() : NULL; if (ignoredchars != NULL) { w2.assign(w); if (utf8) { @@ -658,11 +711,12 @@ struct hentry* Hunspell::checkword(const char* w, int* info, char** root) { remove_ignored_chars(w2, ignoredchars); } word = w2.c_str(); + len = w2.size(); usebuffer = true; - } else - word = w; - - len = strlen(word); + } else { + word = w.c_str(); + len = w.size(); + } if (!len) return NULL; @@ -684,8 +738,9 @@ struct hentry* Hunspell::checkword(const char* w, int* info, char** root) { } // look word in hash table - for (i = 0; (i < maxdic) && !he; i++) { - he = (pHMgr[i])->lookup(word); + struct hentry* he = NULL; + for (size_t i = 0; (i < m_HMgrs.size()) && !he; ++i) { + he = m_HMgrs[i]->lookup(word); // check forbidden and onlyincompound words if ((he) && (he->astr) && (pAMgr) && @@ -736,40 +791,33 @@ struct hentry* Hunspell::checkword(const char* w, int* info, char** root) { return NULL; } if (root) { - std::string word_root(he->word); + root->assign(he->word); if (complexprefixes) { if (utf8) - reverseword_utf(word_root); + reverseword_utf(*root); else - reverseword(word_root); + reverseword(*root); } - *root = mystrdup(word_root.c_str()); } // try check compound word } else if (pAMgr->get_compound()) { struct hentry* rwords[100]; // buffer for COMPOUND pattern checking - he = pAMgr->compound_check(word, len, 0, 0, 100, 0, NULL, (hentry**)&rwords, 0, 0, info); + he = pAMgr->compound_check(word, 0, 0, 100, 0, NULL, (hentry**)&rwords, 0, 0, info); // LANG_hu section: `moving rule' with last dash if ((!he) && (langnum == LANG_hu) && (word[len - 1] == '-')) { - char* dup = mystrdup(word); - if (!dup) - return NULL; - dup[len - 1] = '\0'; - he = pAMgr->compound_check(dup, len - 1, -5, 0, 100, 0, NULL, (hentry**)&rwords, 1, 0, - info); - free(dup); + std::string dup(word, len - 1); + he = pAMgr->compound_check(dup, -5, 0, 100, 0, NULL, (hentry**)&rwords, 1, 0, info); } // end of LANG specific region if (he) { if (root) { - std::string word_root(he->word); + root->assign(he->word); if (complexprefixes) { if (utf8) - reverseword_utf(word_root); + reverseword_utf(*root); else - reverseword(word_root); + reverseword(*root); } - *root = mystrdup(word_root.c_str()); } if (info) *info += SPELL_COMPOUND; @@ -780,22 +828,27 @@ struct hentry* Hunspell::checkword(const char* w, int* info, char** root) { return he; } -int Hunspell::suggest(char*** slst, const char* word) { +std::vector Hunspell::suggest(const std::string& word) { + return m_Impl->suggest(word); +} + +std::vector HunspellImpl::suggest(const std::string& word) { + std::vector slst; + int onlycmpdsug = 0; - if (!pSMgr || maxdic == 0) - return 0; - *slst = NULL; + if (!pSMgr || m_HMgrs.empty()) + return slst; + // process XML input of the simplified API (see manual) - if (strncmp(word, SPELL_XML, sizeof(SPELL_XML) - 3) == 0) { - return spellml(slst, word); + if (word.compare(0, sizeof(SPELL_XML) - 3, SPELL_XML, sizeof(SPELL_XML) - 3) == 0) { + return spellml(word); } - int nc = strlen(word); if (utf8) { - if (nc >= MAXWORDUTF8LEN) - return 0; + if (word.size() >= MAXWORDUTF8LEN) + return slst; } else { - if (nc >= MAXWORDLEN) - return 0; + if (word.size() >= MAXWORDLEN) + return slst; } int captype = NOCAP; size_t abbv = 0; @@ -809,121 +862,102 @@ int Hunspell::suggest(char*** slst, const char* word) { { std::string wspace; - int convstatus = rl ? rl->conv(word, wspace) : 0; - if (convstatus < 0) - return 0; - else if (convstatus > 0) - wl = cleanword2(scw, sunicw, wspace.c_str(), &nc, &captype, &abbv); + bool convstatus = rl ? rl->conv(word, wspace) : false; + if (convstatus) + wl = cleanword2(scw, sunicw, wspace, &captype, &abbv); else - wl = cleanword2(scw, sunicw, word, &nc, &captype, &abbv); + wl = cleanword2(scw, sunicw, word, &captype, &abbv); if (wl == 0) - return 0; + return slst; } - int ns = 0; int capwords = 0; // check capitalized form for FORCEUCASE if (pAMgr && captype == NOCAP && pAMgr->get_forceucase()) { int info = SPELL_ORIGCAP; - if (checkword(scw.c_str(), &info, NULL)) { + if (checkword(scw, &info, NULL)) { std::string form(scw); mkinitcap(form); - - char** wlst = (char**)malloc(MAXSUGGESTION * sizeof(char*)); - if (wlst == NULL) - return -1; - *slst = wlst; - wlst[0] = mystrdup(form.c_str()); - for (int i = 1; i < MAXSUGGESTION; ++i) { - wlst[i] = NULL; - } - - return 1; + slst.push_back(form); + return slst; } } switch (captype) { case NOCAP: { - ns = pSMgr->suggest(slst, scw.c_str(), ns, &onlycmpdsug); + pSMgr->suggest(slst, scw.c_str(), &onlycmpdsug); break; } case INITCAP: { capwords = 1; - ns = pSMgr->suggest(slst, scw.c_str(), ns, &onlycmpdsug); - if (ns == -1) - break; + pSMgr->suggest(slst, scw.c_str(), &onlycmpdsug); std::string wspace(scw); mkallsmall2(wspace, sunicw); - ns = pSMgr->suggest(slst, wspace.c_str(), ns, &onlycmpdsug); + pSMgr->suggest(slst, wspace.c_str(), &onlycmpdsug); break; } case HUHINITCAP: capwords = 1; case HUHCAP: { - ns = pSMgr->suggest(slst, scw.c_str(), ns, &onlycmpdsug); - if (ns != -1) { - // something.The -> something. The - size_t dot_pos = scw.find('.'); - if (dot_pos != std::string::npos) { - std::string postdot = scw.substr(dot_pos + 1); - int captype_; - if (utf8) { - std::vector postdotu; - u8_u16(postdotu, postdot); - captype_ = get_captype_utf8(postdotu, langnum); - } else { - captype_ = get_captype(postdot, csconv); - } - if (captype_ == INITCAP) { - std::string str(scw); - str.insert(dot_pos + 1, 1, ' '); - ns = insert_sug(slst, str.c_str(), ns); - } + pSMgr->suggest(slst, scw.c_str(), &onlycmpdsug); + // something.The -> something. The + size_t dot_pos = scw.find('.'); + if (dot_pos != std::string::npos) { + std::string postdot = scw.substr(dot_pos + 1); + int captype_; + if (utf8) { + std::vector postdotu; + u8_u16(postdotu, postdot); + captype_ = get_captype_utf8(postdotu, langnum); + } else { + captype_ = get_captype(postdot, csconv); } - - std::string wspace; - - if (captype == HUHINITCAP) { - // TheOpenOffice.org -> The OpenOffice.org - wspace = scw; - mkinitsmall2(wspace, sunicw); - ns = pSMgr->suggest(slst, wspace.c_str(), ns, &onlycmpdsug); + if (captype_ == INITCAP) { + std::string str(scw); + str.insert(dot_pos + 1, 1, ' '); + insert_sug(slst, str); } + } + + std::string wspace; + + if (captype == HUHINITCAP) { + // TheOpenOffice.org -> The OpenOffice.org wspace = scw; - mkallsmall2(wspace, sunicw); + mkinitsmall2(wspace, sunicw); + pSMgr->suggest(slst, wspace.c_str(), &onlycmpdsug); + } + wspace = scw; + mkallsmall2(wspace, sunicw); + if (spell(wspace.c_str())) + insert_sug(slst, wspace); + size_t prevns = slst.size(); + pSMgr->suggest(slst, wspace.c_str(), &onlycmpdsug); + if (captype == HUHINITCAP) { + mkinitcap2(wspace, sunicw); if (spell(wspace.c_str())) - ns = insert_sug(slst, wspace.c_str(), ns); - int prevns = ns; - ns = pSMgr->suggest(slst, wspace.c_str(), ns, &onlycmpdsug); - if (captype == HUHINITCAP) { - mkinitcap2(wspace, sunicw); - if (spell(wspace.c_str())) - ns = insert_sug(slst, wspace.c_str(), ns); - ns = pSMgr->suggest(slst, wspace.c_str(), ns, &onlycmpdsug); - } - // aNew -> "a New" (instead of "a new") - for (int j = prevns; j < ns; j++) { - char* space = strchr((*slst)[j], ' '); - if (space) { - size_t slen = strlen(space + 1); - // different case after space (need capitalisation) - if ((slen < wl) && strcmp(scw.c_str() + wl - slen, space + 1)) { - std::string first((*slst)[j], space + 1); - std::string second(space + 1); - std::vector w; - if (utf8) - u8_u16(w, second); - mkinitcap2(second, w); - // set as first suggestion - char* r = (*slst)[j]; - for (int k = j; k > 0; k--) - (*slst)[k] = (*slst)[k - 1]; - free(r); - (*slst)[0] = mystrdup((first + second).c_str()); - } + insert_sug(slst, wspace); + pSMgr->suggest(slst, wspace.c_str(), &onlycmpdsug); + } + // aNew -> "a New" (instead of "a new") + for (size_t j = prevns; j < slst.size(); ++j) { + const char* space = strchr(slst[j].c_str(), ' '); + if (space) { + size_t slen = strlen(space + 1); + // different case after space (need capitalisation) + if ((slen < wl) && strcmp(scw.c_str() + wl - slen, space + 1)) { + std::string first(slst[j].c_str(), space + 1); + std::string second(space + 1); + std::vector w; + if (utf8) + u8_u16(w, second); + mkinitcap2(second, w); + // set as first suggestion + slst.erase(slst.begin() + j); + slst.insert(slst.begin(), first + second); } } } @@ -933,28 +967,20 @@ int Hunspell::suggest(char*** slst, const char* word) { case ALLCAP: { std::string wspace(scw); mkallsmall2(wspace, sunicw); - ns = pSMgr->suggest(slst, wspace.c_str(), ns, &onlycmpdsug); - if (ns == -1) - break; + pSMgr->suggest(slst, wspace.c_str(), &onlycmpdsug); if (pAMgr && pAMgr->get_keepcase() && spell(wspace.c_str())) - ns = insert_sug(slst, wspace.c_str(), ns); + insert_sug(slst, wspace); mkinitcap2(wspace, sunicw); - ns = pSMgr->suggest(slst, wspace.c_str(), ns, &onlycmpdsug); - for (int j = 0; j < ns; j++) { - std::string form((*slst)[j]); - mkallcap(form); - + pSMgr->suggest(slst, wspace.c_str(), &onlycmpdsug); + for (size_t j = 0; j < slst.size(); ++j) { + mkallcap(slst[j]); if (pAMgr && pAMgr->get_checksharps()) { if (utf8) { - mystrrep(form, "\xC3\x9F", "SS"); + mystrrep(slst[j], "\xC3\x9F", "SS"); } else { - mystrrep(form, "\xDF", "SS"); + mystrrep(slst[j], "\xDF", "SS"); } } - - free((*slst)[j]); - (*slst)[j] = mystrdup(form.c_str()); - } break; } @@ -962,29 +988,27 @@ int Hunspell::suggest(char*** slst, const char* word) { // LANG_hu section: replace '-' with ' ' in Hungarian if (langnum == LANG_hu) { - for (int j = 0; j < ns; j++) { - char* pos = strchr((*slst)[j], '-'); - if (pos) { + for (size_t j = 0; j < slst.size(); ++j) { + size_t pos = slst[j].find('-'); + if (pos != std::string::npos) { int info; - *pos = '\0'; - std::string w((*slst)[j]); - w.append(pos + 1); - (void)spell(w.c_str(), &info, NULL); + std::string w(slst[j].substr(0, pos)); + w.append(slst[j].substr(pos + 1)); + (void)spell(w, &info, NULL); if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) { - *pos = ' '; + slst[j][pos] = ' '; } else - *pos = '-'; + slst[j][pos] = '-'; } } } // END OF LANG_hu section // try ngram approach since found nothing or only compound words - if (pAMgr && (ns == 0 || onlycmpdsug) && (pAMgr->get_maxngramsugs() != 0) && - (*slst)) { + if (pAMgr && (slst.empty() || onlycmpdsug) && (pAMgr->get_maxngramsugs() != 0)) { switch (captype) { case NOCAP: { - ns = pSMgr->ngsuggest(*slst, scw.c_str(), ns, pHMgr, maxdic); + pSMgr->ngsuggest(slst, scw.c_str(), m_HMgrs); break; } case HUHINITCAP: @@ -992,26 +1016,23 @@ int Hunspell::suggest(char*** slst, const char* word) { case HUHCAP: { std::string wspace(scw); mkallsmall2(wspace, sunicw); - ns = pSMgr->ngsuggest(*slst, wspace.c_str(), ns, pHMgr, maxdic); + pSMgr->ngsuggest(slst, wspace.c_str(), m_HMgrs); break; } case INITCAP: { capwords = 1; std::string wspace(scw); mkallsmall2(wspace, sunicw); - ns = pSMgr->ngsuggest(*slst, wspace.c_str(), ns, pHMgr, maxdic); + pSMgr->ngsuggest(slst, wspace.c_str(), m_HMgrs); break; } case ALLCAP: { std::string wspace(scw); mkallsmall2(wspace, sunicw); - int oldns = ns; - ns = pSMgr->ngsuggest(*slst, wspace.c_str(), ns, pHMgr, maxdic); - for (int j = oldns; j < ns; j++) { - std::string form((*slst)[j]); - mkallcap(form); - free((*slst)[j]); - (*slst)[j] = mystrdup(form.c_str()); + size_t oldns = slst.size(); + pSMgr->ngsuggest(slst, wspace.c_str(), m_HMgrs); + for (size_t j = oldns; j < slst.size(); ++j) { + mkallcap(slst[j]); } break; } @@ -1022,8 +1043,8 @@ int Hunspell::suggest(char*** slst, const char* word) { size_t dash_pos = scw.find('-'); if (dash_pos != std::string::npos) { int nodashsug = 1; - for (int j = 0; j < ns && nodashsug == 1; j++) { - if (strchr((*slst)[j], '-')) + for (size_t j = 0; j < slst.size() && nodashsug == 1; ++j) { + if (slst[j].find('-') != std::string::npos) nodashsug = 0; } @@ -1035,20 +1056,16 @@ int Hunspell::suggest(char*** slst, const char* word) { last = 1; std::string chunk = scw.substr(prev_pos, dash_pos - prev_pos); if (!spell(chunk.c_str())) { - char** nlst = NULL; - int nn = suggest(&nlst, chunk.c_str()); - for (int j = nn - 1; j >= 0; j--) { + std::vector nlst = suggest(chunk.c_str()); + for (std::vector::reverse_iterator j = nlst.rbegin(); j != nlst.rend(); ++j) { std::string wspace = scw.substr(0, prev_pos); - wspace.append(nlst[j]); + wspace.append(*j); if (!last) { wspace.append("-"); wspace.append(scw.substr(dash_pos + 1)); } - ns = insert_sug(slst, wspace.c_str(), ns); - free(nlst[j]); + insert_sug(slst, wspace); } - if (nlst != NULL) - free(nlst); nodashsug = 0; } if (!last) { @@ -1062,31 +1079,24 @@ int Hunspell::suggest(char*** slst, const char* word) { // word reversing wrapper for complex prefixes if (complexprefixes) { - for (int j = 0; j < ns; j++) { - std::string root((*slst)[j]); - free((*slst)[j]); + for (size_t j = 0; j < slst.size(); ++j) { if (utf8) - reverseword_utf(root); + reverseword_utf(slst[j]); else - reverseword(root); - (*slst)[j] = mystrdup(root.c_str()); + reverseword(slst[j]); } } // capitalize if (capwords) - for (int j = 0; j < ns; j++) { - std::string form((*slst)[j]); - free((*slst)[j]); - mkinitcap(form); - (*slst)[j] = mystrdup(form.c_str()); + for (size_t j = 0; j < slst.size(); ++j) { + mkinitcap(slst[j]); } // expand suggestions with dot(s) if (abbv && pAMgr && pAMgr->get_sugswithdots()) { - for (int j = 0; j < ns; j++) { - (*slst)[j] = (char*)realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv); - strcat((*slst)[j], word + strlen(word) - abbv); + for (size_t j = 0; j < slst.size(); ++j) { + slst[j].append(word.substr(word.size() - abbv)); } } @@ -1095,96 +1105,90 @@ int Hunspell::suggest(char*** slst, const char* word) { switch (captype) { case INITCAP: case ALLCAP: { - int l = 0; - for (int j = 0; j < ns; j++) { - if (!strchr((*slst)[j], ' ') && !spell((*slst)[j])) { + size_t l = 0; + for (size_t j = 0; j < slst.size(); ++j) { + if (slst[j].find(' ') == std::string::npos && !spell(slst[j])) { std::string s; std::vector w; if (utf8) { - u8_u16(w, (*slst)[j]); + u8_u16(w, slst[j]); } else { - s = (*slst)[j]; + s = slst[j]; } mkallsmall2(s, w); - free((*slst)[j]); - if (spell(s.c_str())) { - (*slst)[l] = mystrdup(s.c_str()); - if ((*slst)[l]) - l++; + if (spell(s)) { + slst[l] = s; + ++l; } else { mkinitcap2(s, w); - if (spell(s.c_str())) { - (*slst)[l] = mystrdup(s.c_str()); - if ((*slst)[l]) - l++; + if (spell(s)) { + slst[l] = s; + ++l; } } } else { - (*slst)[l] = (*slst)[j]; - l++; + slst[l] = slst[j]; + ++l; } } - ns = l; + slst.resize(l); } } } // remove duplications - int l = 0; - for (int j = 0; j < ns; j++) { - (*slst)[l] = (*slst)[j]; - for (int k = 0; k < l; k++) { - if (strcmp((*slst)[k], (*slst)[j]) == 0) { - free((*slst)[j]); - l--; + size_t l = 0; + for (size_t j = 0; j < slst.size(); ++j) { + slst[l] = slst[j]; + for (size_t k = 0; k < l; ++k) { + if (slst[k] == slst[j]) { + --l; break; } } - l++; + ++l; } - ns = l; + slst.resize(l); // output conversion rl = (pAMgr) ? pAMgr->get_oconvtable() : NULL; - for (int j = 0; rl && j < ns; j++) { + for (size_t j = 0; rl && j < slst.size(); ++j) { std::string wspace; - if (rl->conv((*slst)[j], wspace) > 0) { - free((*slst)[j]); - (*slst)[j] = mystrdup(wspace.c_str()); + if (rl->conv(slst[j], wspace)) { + slst[j] = wspace; } } - // if suggestions removed by nosuggest, onlyincompound parameters - if (l == 0 && *slst) { - free(*slst); - *slst = NULL; - } - return l; + return slst; } -void Hunspell::free_list(char*** slst, int n) { - freelist(slst, n); +const std::string& Hunspell::get_dict_encoding() const { + return m_Impl->get_dict_encoding(); } -char* Hunspell::get_dic_encoding() { +const std::string& HunspellImpl::get_dict_encoding() const { return encoding; } -int Hunspell::stem(char*** slst, char** desc, int n) { +std::vector Hunspell::stem(const std::vector& desc) { + return m_Impl->stem(desc); +} + +std::vector HunspellImpl::stem(const std::vector& desc) { + std::vector slst; std::string result2; - *slst = NULL; - if (n == 0) - return 0; - for (int i = 0; i < n; i++) { + if (desc.empty()) + return slst; + for (size_t i = 0; i < desc.size(); ++i) { std::string result; // add compound word parts (except the last one) - char* s = (char*)desc[i]; - char* part = strstr(s, MORPH_PART); + const char* s = desc[i].c_str(); + const char* part = strstr(s, MORPH_PART); if (part) { - char* nextpart = strstr(part + 1, MORPH_PART); + const char* nextpart = strstr(part + 1, MORPH_PART); while (nextpart) { std::string field; copy_field(field, part, MORPH_PART); @@ -1195,36 +1199,34 @@ int Hunspell::stem(char*** slst, char** desc, int n) { s = part; } - char** pl; std::string tok(s); size_t alt = 0; while ((alt = tok.find(" | ", alt)) != std::string::npos) { tok[alt + 1] = MSEP_ALT; } - int pln = line_tok(tok.c_str(), &pl, MSEP_ALT); - for (int k = 0; k < pln; k++) { + std::vector pl = line_tok(tok, MSEP_ALT); + for (size_t k = 0; k < pl.size(); ++k) { // add derivational suffixes - if (strstr(pl[k], MORPH_DERI_SFX)) { + if (pl[k].find(MORPH_DERI_SFX) != std::string::npos) { // remove inflectional suffixes - char* is = strstr(pl[k], MORPH_INFL_SFX); - if (is) - *is = '\0'; - char* sg = pSMgr->suggest_gen(&(pl[k]), 1, pl[k]); - if (sg) { - char** gen; - int genl = line_tok(sg, &gen, MSEP_REC); - free(sg); - for (int j = 0; j < genl; j++) { + const size_t is = pl[k].find(MORPH_INFL_SFX); + if (is != std::string::npos) + pl[k].resize(is); + std::vector singlepl; + singlepl.push_back(pl[k]); + std::string sg = pSMgr->suggest_gen(singlepl, pl[k]); + if (!sg.empty()) { + std::vector gen = line_tok(sg, MSEP_REC); + for (size_t j = 0; j < gen.size(); ++j) { result2.push_back(MSEP_REC); result2.append(result); result2.append(gen[j]); } - freelist(&gen, genl); } } else { result2.push_back(MSEP_REC); result2.append(result); - if (strstr(pl[k], MORPH_SURF_PFX)) { + if (pl[k].find(MORPH_SURF_PFX) != std::string::npos) { std::string field; copy_field(field, pl[k], MORPH_SURF_PFX); result2.append(field); @@ -1234,29 +1236,41 @@ int Hunspell::stem(char*** slst, char** desc, int n) { result2.append(field); } } - freelist(&pl, pln); } - int sln = line_tok(result2.c_str(), slst, MSEP_REC); - return uniqlist(*slst, sln); + slst = line_tok(result2, MSEP_REC); + uniqlist(slst); + return slst; } -int Hunspell::stem(char*** slst, const char* word) { - char** pl; - int pln = analyze(&pl, word); - int pln2 = stem(slst, pl, pln); - freelist(&pl, pln); - return pln2; +std::vector Hunspell::stem(const std::string& word) { + return m_Impl->stem(word); } -const char* Hunspell::get_wordchars() { +std::vector HunspellImpl::stem(const std::string& word) { + return stem(analyze(word)); +} + +const char* Hunspell::get_wordchars() const { + return m_Impl->get_wordchars().c_str(); +} + +const std::string& Hunspell::get_wordchars_cpp() const { + return m_Impl->get_wordchars(); +} + +const std::string& HunspellImpl::get_wordchars() const { return pAMgr->get_wordchars(); } -const std::vector& Hunspell::get_wordchars_utf16() { +const std::vector& Hunspell::get_wordchars_utf16() const { + return m_Impl->get_wordchars_utf16(); +} + +const std::vector& HunspellImpl::get_wordchars_utf16() const { return pAMgr->get_wordchars_utf16(); } -void Hunspell::mkinitcap(std::string& u8) { +void HunspellImpl::mkinitcap(std::string& u8) { if (utf8) { std::vector u16; u8_u16(u16, u8); @@ -1267,7 +1281,7 @@ void Hunspell::mkinitcap(std::string& u8) { } } -int Hunspell::mkinitcap2(std::string& u8, std::vector& u16) { +int HunspellImpl::mkinitcap2(std::string& u8, std::vector& u16) { if (utf8) { ::mkinitcap_utf(u16, langnum); u16_u8(u8, u16); @@ -1277,7 +1291,7 @@ int Hunspell::mkinitcap2(std::string& u8, std::vector& u16) { return u8.size(); } -int Hunspell::mkinitsmall2(std::string& u8, std::vector& u16) { +int HunspellImpl::mkinitsmall2(std::string& u8, std::vector& u16) { if (utf8) { ::mkinitsmall_utf(u16, langnum); u16_u8(u8, u16); @@ -1287,52 +1301,78 @@ int Hunspell::mkinitsmall2(std::string& u8, std::vector& u16) { return u8.size(); } -int Hunspell::add(const char* word) { - if (pHMgr[0]) - return (pHMgr[0])->add(word); +int Hunspell::add(const std::string& word) { + return m_Impl->add(word); +} + +int HunspellImpl::add(const std::string& word) { + if (!m_HMgrs.empty()) + return m_HMgrs[0]->add(word); return 0; } -int Hunspell::add_with_affix(const char* word, const char* example) { - if (pHMgr[0]) - return (pHMgr[0])->add_with_affix(word, example); +int Hunspell::add_with_affix(const std::string& word, const std::string& example) { + return m_Impl->add_with_affix(word, example); +} + +int HunspellImpl::add_with_affix(const std::string& word, const std::string& example) { + if (!m_HMgrs.empty()) + return m_HMgrs[0]->add_with_affix(word, example); return 0; } -int Hunspell::remove(const char* word) { - if (pHMgr[0]) - return (pHMgr[0])->remove(word); +int Hunspell::remove(const std::string& word) { + return m_Impl->remove(word); +} + +int HunspellImpl::remove(const std::string& word) { + if (!m_HMgrs.empty()) + return m_HMgrs[0]->remove(word); return 0; } -const char* Hunspell::get_version() { +const char* Hunspell::get_version() const { + return m_Impl->get_version().c_str(); +} + +const std::string& Hunspell::get_version_cpp() const { + return m_Impl->get_version(); +} + +const std::string& HunspellImpl::get_version() const { return pAMgr->get_version(); } -struct cs_info* Hunspell::get_csconv() { +struct cs_info* HunspellImpl::get_csconv() { return csconv; } -void Hunspell::cat_result(std::string& result, char* st) { - if (st) { +struct cs_info* Hunspell::get_csconv() { + return m_Impl->get_csconv(); +} + +void HunspellImpl::cat_result(std::string& result, const std::string& st) { + if (!st.empty()) { if (!result.empty()) result.append("\n"); result.append(st); - free(st); } } -int Hunspell::analyze(char*** slst, const char* word) { - *slst = NULL; - if (!pSMgr || maxdic == 0) - return 0; - int nc = strlen(word); +std::vector Hunspell::analyze(const std::string& word) { + return m_Impl->analyze(word); +} + +std::vector HunspellImpl::analyze(const std::string& word) { + std::vector slst; + if (!pSMgr || m_HMgrs.empty()) + return slst; if (utf8) { - if (nc >= MAXWORDUTF8LEN) - return 0; + if (word.size() >= MAXWORDUTF8LEN) + return slst; } else { - if (nc >= MAXWORDLEN) - return 0; + if (word.size() >= MAXWORDLEN) + return slst; } int captype = NOCAP; size_t abbv = 0; @@ -1346,13 +1386,11 @@ int Hunspell::analyze(char*** slst, const char* word) { { std::string wspace; - int convstatus = rl ? rl->conv(word, wspace) : 0; - if (convstatus < 0) - return 0; - else if (convstatus > 0) - wl = cleanword2(scw, sunicw, wspace.c_str(), &nc, &captype, &abbv); + bool convstatus = rl ? rl->conv(word, wspace) : false; + if (convstatus) + wl = cleanword2(scw, sunicw, wspace, &captype, &abbv); else - wl = cleanword2(scw, sunicw, word, &nc, &captype, &abbv); + wl = cleanword2(scw, sunicw, word, &captype, &abbv); } if (wl == 0) { @@ -1362,18 +1400,18 @@ int Hunspell::analyze(char*** slst, const char* word) { scw.push_back('.'); abbv = 0; } else - return 0; + return slst; } std::string result; size_t n = 0; - size_t n2 = 0; - size_t n3 = 0; - // test numbers // LANG_hu section: set dash information for suggestions if (langnum == LANG_hu) { + size_t n2 = 0; + size_t n3 = 0; + while ((n < wl) && (((scw[n] <= '9') && (scw[n] >= '0')) || (((scw[n] == '.') || (scw[n] == ',')) && (n > 0)))) { n++; @@ -1387,22 +1425,20 @@ int Hunspell::analyze(char*** slst, const char* word) { } if ((n == wl) && (n3 > 0) && (n - n3 > 3)) - return 0; + return slst; if ((n == wl) || ((n > 0) && ((scw[n] == '%') || (scw[n] == '\xB0')) && - checkword(scw.c_str() + n, NULL, NULL))) { + checkword(scw.substr(n), NULL, NULL))) { result.append(scw); result.resize(n - 1); if (n == wl) - cat_result(result, pSMgr->suggest_morph(scw.c_str() + n - 1)); + cat_result(result, pSMgr->suggest_morph(scw.substr(n - 1))); else { - char sign = scw[n]; - scw[n] = '\0'; - cat_result(result, pSMgr->suggest_morph(scw.c_str() + n - 1)); + std::string chunk = scw.substr(n - 1, 1); + cat_result(result, pSMgr->suggest_morph(chunk)); result.push_back('+'); // XXX SPEC. MORPHCODE - scw[n] = sign; - cat_result(result, pSMgr->suggest_morph(scw.c_str() + n)); + cat_result(result, pSMgr->suggest_morph(scw.substr(n))); } - return line_tok(result.c_str(), slst, MSEP_REC); + return line_tok(result, MSEP_REC); } } // END OF LANG_hu section @@ -1411,52 +1447,52 @@ int Hunspell::analyze(char*** slst, const char* word) { case HUHCAP: case HUHINITCAP: case NOCAP: { - cat_result(result, pSMgr->suggest_morph(scw.c_str())); + cat_result(result, pSMgr->suggest_morph(scw)); if (abbv) { std::string u8buffer(scw); u8buffer.push_back('.'); - cat_result(result, pSMgr->suggest_morph(u8buffer.c_str())); + cat_result(result, pSMgr->suggest_morph(u8buffer)); } break; } case INITCAP: { - wl = mkallsmall2(scw, sunicw); + mkallsmall2(scw, sunicw); std::string u8buffer(scw); mkinitcap2(scw, sunicw); - cat_result(result, pSMgr->suggest_morph(u8buffer.c_str())); - cat_result(result, pSMgr->suggest_morph(scw.c_str())); + cat_result(result, pSMgr->suggest_morph(u8buffer)); + cat_result(result, pSMgr->suggest_morph(scw)); if (abbv) { u8buffer.push_back('.'); - cat_result(result, pSMgr->suggest_morph(u8buffer.c_str())); + cat_result(result, pSMgr->suggest_morph(u8buffer)); u8buffer = scw; u8buffer.push_back('.'); - cat_result(result, pSMgr->suggest_morph(u8buffer.c_str())); + cat_result(result, pSMgr->suggest_morph(u8buffer)); } break; } case ALLCAP: { - cat_result(result, pSMgr->suggest_morph(scw.c_str())); + cat_result(result, pSMgr->suggest_morph(scw)); if (abbv) { std::string u8buffer(scw); u8buffer.push_back('.'); - cat_result(result, pSMgr->suggest_morph(u8buffer.c_str())); + cat_result(result, pSMgr->suggest_morph(u8buffer)); } mkallsmall2(scw, sunicw); std::string u8buffer(scw); mkinitcap2(scw, sunicw); - cat_result(result, pSMgr->suggest_morph(u8buffer.c_str())); - cat_result(result, pSMgr->suggest_morph(scw.c_str())); + cat_result(result, pSMgr->suggest_morph(u8buffer)); + cat_result(result, pSMgr->suggest_morph(scw)); if (abbv) { u8buffer.push_back('.'); - cat_result(result, pSMgr->suggest_morph(u8buffer.c_str())); + cat_result(result, pSMgr->suggest_morph(u8buffer)); u8buffer = scw; u8buffer.push_back('.'); - cat_result(result, pSMgr->suggest_morph(u8buffer.c_str())); + cat_result(result, pSMgr->suggest_morph(u8buffer)); } break; } @@ -1470,62 +1506,58 @@ int Hunspell::analyze(char*** slst, const char* word) { else reverseword(result); } - return line_tok(result.c_str(), slst, MSEP_REC); + return line_tok(result, MSEP_REC); } // compound word with dash (HU) I18n // LANG_hu section: set dash information for suggestions size_t dash_pos = langnum == LANG_hu ? scw.find('-') : std::string::npos; - int nresult = 0; if (dash_pos != std::string::npos) { + int nresult = 0; + std::string part1 = scw.substr(0, dash_pos); std::string part2 = scw.substr(dash_pos+1); // examine 2 sides of the dash if (part2.empty()) { // base word ending with dash - if (spell(part1.c_str())) { - char* p = pSMgr->suggest_morph(part1.c_str()); - if (p) { - int ret = line_tok(p, slst, MSEP_REC); - free(p); - return ret; + if (spell(part1)) { + std::string p = pSMgr->suggest_morph(part1); + if (!p.empty()) { + slst = line_tok(p, MSEP_REC); + return slst; } } } else if (part2.size() == 1 && part2[0] == 'e') { // XXX (HU) -e hat. - if (spell(part1.c_str()) && (spell("-e"))) { - char* st = pSMgr->suggest_morph(part1.c_str()); - if (st) { + if (spell(part1) && (spell("-e"))) { + std::string st = pSMgr->suggest_morph(part1); + if (!st.empty()) { result.append(st); - free(st); } result.push_back('+'); // XXX spec. separator in MORPHCODE st = pSMgr->suggest_morph("-e"); - if (st) { + if (!st.empty()) { result.append(st); - free(st); } - return line_tok(result.c_str(), slst, MSEP_REC); + return line_tok(result, MSEP_REC); } } else { // first word ending with dash: word- XXX ??? part1.push_back(' '); - nresult = spell(part1.c_str()); + nresult = spell(part1); part1.erase(part1.size() - 1); - if (nresult && spell(part2.c_str()) && + if (nresult && spell(part2) && ((part2.size() > 1) || ((part2[0] > '0') && (part2[0] < '9')))) { - char* st = pSMgr->suggest_morph(part1.c_str()); - if (st) { + std::string st = pSMgr->suggest_morph(part1); + if (!st.empty()) { result.append(st); - free(st); result.push_back('+'); // XXX spec. separator in MORPHCODE } - st = pSMgr->suggest_morph(part2.c_str()); - if (st) { + st = pSMgr->suggest_morph(part2); + if (!st.empty()) { result.append(st); - free(st); } - return line_tok(result.c_str(), slst, MSEP_REC); + return line_tok(result, MSEP_REC); } } // affixed number in correct word @@ -1550,37 +1582,38 @@ int Hunspell::analyze(char*** slst, const char* word) { continue; } std::string chunk = scw.substr(dash_pos - n); - if (checkword(chunk.c_str(), NULL, NULL)) { + if (checkword(chunk, NULL, NULL)) { result.append(chunk); - char* st = pSMgr->suggest_morph(chunk.c_str()); - if (st) { + std::string st = pSMgr->suggest_morph(chunk); + if (!st.empty()) { result.append(st); - free(st); } - return line_tok(result.c_str(), slst, MSEP_REC); + return line_tok(result, MSEP_REC); } } } } - return 0; + return slst; } -int Hunspell::generate(char*** slst, const char* word, char** pl, int pln) { - *slst = NULL; - if (!pSMgr || !pln) - return 0; - char** pl2; - int pl2n = analyze(&pl2, word); +std::vector Hunspell::generate(const std::string& word, const std::vector& pl) { + return m_Impl->generate(word, pl); +} + +std::vector HunspellImpl::generate(const std::string& word, const std::vector& pl) { + std::vector slst; + if (!pSMgr || pl.empty()) + return slst; + std::vector pl2 = analyze(word); int captype = NOCAP; int abbv = 0; std::string cw; cleanword(cw, word, &captype, &abbv); std::string result; - for (int i = 0; i < pln; i++) { - cat_result(result, pSMgr->suggest_gen(pl2, pl2n, pl[i])); + for (size_t i = 0; i < pl.size(); ++i) { + cat_result(result, pSMgr->suggest_gen(pl2, pl[i])); } - freelist(&pl2, pl2n); if (!result.empty()) { // allcap @@ -1588,50 +1621,42 @@ int Hunspell::generate(char*** slst, const char* word, char** pl, int pln) { mkallcap(result); // line split - int linenum = line_tok(result.c_str(), slst, MSEP_REC); + slst = line_tok(result, MSEP_REC); // capitalize if (captype == INITCAP || captype == HUHINITCAP) { - for (int j = 0; j < linenum; j++) { - std::string form((*slst)[j]); - free((*slst)[j]); - mkinitcap(form); - (*slst)[j] = mystrdup(form.c_str()); + for (size_t j = 0; j < slst.size(); ++j) { + mkinitcap(slst[j]); } } // temporary filtering of prefix related errors (eg. // generate("undrinkable", "eats") --> "undrinkables" and "*undrinks") - - int r = 0; - for (int j = 0; j < linenum; j++) { - if (!spell((*slst)[j])) { - free((*slst)[j]); - (*slst)[j] = NULL; - } else { - if (r < j) - (*slst)[r] = (*slst)[j]; - r++; + std::vector::iterator it = slst.begin(); + while (it != slst.end()) { + if (!spell(*it)) { + it = slst.erase(it); + } else { + ++it; } } - if (r > 0) - return r; - free(*slst); - *slst = NULL; } - return 0; + return slst; } -int Hunspell::generate(char*** slst, const char* word, const char* pattern) { - char** pl; - int pln = analyze(&pl, pattern); - int n = generate(slst, word, pl, pln); - freelist(&pl, pln); - return uniqlist(*slst, n); +std::vector Hunspell::generate(const std::string& word, const std::string& pattern) { + return m_Impl->generate(word, pattern); +} + +std::vector HunspellImpl::generate(const std::string& word, const std::string& pattern) { + std::vector pl = analyze(pattern); + std::vector slst = generate(word, pl); + uniqlist(slst); + return slst; } // minimal XML parser functions -std::string Hunspell::get_xml_par(const char* par) { +std::string HunspellImpl::get_xml_par(const char* par) { std::string dest; if (!par) return dest; @@ -1649,16 +1674,38 @@ std::string Hunspell::get_xml_par(const char* par) { } int Hunspell::get_langnum() const { + return m_Impl->get_langnum(); +} + +int HunspellImpl::get_langnum() const { return langnum; } +bool Hunspell::input_conv(const std::string& word, std::string& dest) { + return m_Impl->input_conv(word, dest); +} + int Hunspell::input_conv(const char* word, char* dest, size_t destsize) { - RepList* rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL; - return (rl && rl->conv(word, dest, destsize) > 0); + std::string d; + bool ret = input_conv(word, d); + if (ret && d.size() < destsize) { + strncpy(dest, d.c_str(), destsize); + return 1; + } + return 0; +} + +bool HunspellImpl::input_conv(const std::string& word, std::string& dest) { + RepList* rl = pAMgr ? pAMgr->get_iconvtable() : NULL; + if (rl) { + return rl->conv(word, dest); + } + dest.assign(word); + return false; } // return the beginning of the element (attr == NULL) or the attribute -const char* Hunspell::get_xml_pos(const char* s, const char* attr) { +const char* HunspellImpl::get_xml_pos(const char* s, const char* attr) { const char* end = strchr(s, '>'); const char* p = s; if (attr == NULL) @@ -1671,7 +1718,7 @@ const char* Hunspell::get_xml_pos(const char* s, const char* attr) { return p + strlen(attr); } -int Hunspell::check_xml_par(const char* q, +int HunspellImpl::check_xml_par(const char* q, const char* attr, const char* value) { std::string cw = get_xml_par(get_xml_pos(q, attr)); @@ -1680,53 +1727,48 @@ int Hunspell::check_xml_par(const char* q, return 0; } -int Hunspell::get_xml_list(char*** slst, const char* list, const char* tag) { +std::vector HunspellImpl::get_xml_list(const char* list, const char* tag) { + std::vector slst; if (!list) - return 0; - int n = 0; - const char* p; - for (p = list; ((p = strstr(p, tag)) != NULL); p++) - n++; - if (n == 0) - return 0; - *slst = (char**)malloc(sizeof(char*) * n); - if (!*slst) - return 0; - for (p = list, n = 0; ((p = strstr(p, tag)) != NULL); p++, n++) { + return slst; + const char* p = list; + for (size_t n = 0; ((p = strstr(p, tag)) != NULL); ++p, ++n) { std::string cw = get_xml_par(p + strlen(tag) - 1); if (cw.empty()) { break; } - (*slst)[n] = mystrdup(cw.c_str()); + slst.push_back(cw); } - return n; + return slst; } -int Hunspell::spellml(char*** slst, const char* word) { +std::vector HunspellImpl::spellml(const std::string& in_word) { + std::vector slst; + + const char* word = in_word.c_str(); + const char* q = strstr(word, "'); if (!q2) - return 0; // bad XML input + return slst; // bad XML input q2 = strstr(q2, "')); if (!cw.empty()) - n = analyze(slst, cw.c_str()); - if (n == 0) - return 0; + slst = analyze(cw); + if (slst.empty()) + return slst; // convert the result to ana1ana2 format std::string r; r.append(""); - for (int i = 0; i < n; i++) { + for (size_t i = 0; i < slst.size(); ++i) { r.append(""); - std::string entry((*slst)[i]); - free((*slst)[i]); + std::string entry(slst[i]); mystrrep(entry, "\t", " "); mystrrep(entry, "&", "&"); mystrrep(entry, "<", "<"); @@ -1735,36 +1777,101 @@ int Hunspell::spellml(char*** slst, const char* word) { r.append(""); } r.append(""); - (*slst)[0] = mystrdup(r.c_str()); - return 1; + slst.clear(); + slst.push_back(r); + return slst; } else if (check_xml_par(q, "type=", "stem")) { std::string cw = get_xml_par(strchr(q2, '>')); if (!cw.empty()) - return stem(slst, cw.c_str()); + return stem(cw); } else if (check_xml_par(q, "type=", "generate")) { std::string cw = get_xml_par(strchr(q2, '>')); if (cw.empty()) - return 0; + return slst; const char* q3 = strstr(q2 + 1, "')); if (!cw2.empty()) { - return generate(slst, cw.c_str(), cw2.c_str()); + return generate(cw, cw2); } } else { if ((q2 = strstr(q2 + 1, "'), ""); - if (n != 0) { - int n2 = generate(slst, cw.c_str(), slst2, n); - freelist(&slst2, n); - return uniqlist(*slst, n2); + std::vector slst2 = get_xml_list(strchr(q2, '>'), ""); + if (!slst2.empty()) { + slst = generate(cw, slst2); + uniqlist(slst); + return slst; } - freelist(&slst2, n); } } } - return 0; + return slst; +} + +int Hunspell::spell(const char* word, int* info, char** root) { + std::string sroot; + bool ret = m_Impl->spell(word, info, root ? &sroot : NULL); + if (root) { + if (sroot.empty()) { + *root = NULL; + } else { + *root = mystrdup(sroot.c_str()); + } + } + return ret; +} + +namespace { + int munge_vector(char*** slst, const std::vector& items) { + if (items.empty()) { + *slst = NULL; + return 0; + } else { + *slst = (char**)malloc(sizeof(char*) * items.size()); + if (!*slst) + return 0; + for (size_t i = 0; i < items.size(); ++i) + (*slst)[i] = mystrdup(items[i].c_str()); + } + return items.size(); + } +} + +void Hunspell::free_list(char*** slst, int n) { + Hunspell_free_list((Hunhandle*)(this), slst, n); +} + +int Hunspell::suggest(char*** slst, const char* word) { + return Hunspell_suggest((Hunhandle*)(this), slst, word); +} + +int Hunspell::suffix_suggest(char*** slst, const char* root_word) { + std::vector stems = m_Impl->suffix_suggest(root_word); + return munge_vector(slst, stems); +} + +char* Hunspell::get_dic_encoding() { + return &(m_Impl->dic_encoding_vec[0]); +} + +int Hunspell::stem(char*** slst, char** desc, int n) { + return Hunspell_stem2((Hunhandle*)(this), slst, desc, n); +} + +int Hunspell::stem(char*** slst, const char* word) { + return Hunspell_stem((Hunhandle*)(this), slst, word); +} + +int Hunspell::analyze(char*** slst, const char* word) { + return Hunspell_analyze((Hunhandle*)(this), slst, word); +} + +int Hunspell::generate(char*** slst, const char* word, char** pl, int pln) { + return Hunspell_generate2((Hunhandle*)(this), slst, word, pl, pln); +} + +int Hunspell::generate(char*** slst, const char* word, const char* pattern) { + return Hunspell_generate((Hunhandle*)(this), slst, word, pattern); } Hunhandle* Hunspell_create(const char* affpath, const char* dpath) { @@ -1774,46 +1881,56 @@ Hunhandle* Hunspell_create(const char* affpath, const char* dpath) { Hunhandle* Hunspell_create_key(const char* affpath, const char* dpath, const char* key) { - return (Hunhandle*)(new Hunspell(affpath, dpath, key)); + return reinterpret_cast(new Hunspell(affpath, dpath, key)); } void Hunspell_destroy(Hunhandle* pHunspell) { - delete (Hunspell*)(pHunspell); + delete reinterpret_cast(pHunspell); } int Hunspell_add_dic(Hunhandle* pHunspell, const char* dpath) { - return ((Hunspell*)pHunspell)->add_dic(dpath); + return reinterpret_cast(pHunspell)->add_dic(dpath); } int Hunspell_spell(Hunhandle* pHunspell, const char* word) { - return ((Hunspell*)pHunspell)->spell(word); + return reinterpret_cast(pHunspell)->spell(std::string(word)); } char* Hunspell_get_dic_encoding(Hunhandle* pHunspell) { - return ((Hunspell*)pHunspell)->get_dic_encoding(); + return reinterpret_cast(pHunspell)->get_dic_encoding(); } int Hunspell_suggest(Hunhandle* pHunspell, char*** slst, const char* word) { - return ((Hunspell*)pHunspell)->suggest(slst, word); + std::vector suggests = reinterpret_cast(pHunspell)->suggest(word); + return munge_vector(slst, suggests); } int Hunspell_analyze(Hunhandle* pHunspell, char*** slst, const char* word) { - return ((Hunspell*)pHunspell)->analyze(slst, word); + std::vector stems = reinterpret_cast(pHunspell)->analyze(word); + return munge_vector(slst, stems); } int Hunspell_stem(Hunhandle* pHunspell, char*** slst, const char* word) { - return ((Hunspell*)pHunspell)->stem(slst, word); + + std::vector stems = reinterpret_cast(pHunspell)->stem(word); + return munge_vector(slst, stems); } int Hunspell_stem2(Hunhandle* pHunspell, char*** slst, char** desc, int n) { - return ((Hunspell*)pHunspell)->stem(slst, desc, n); + std::vector morph; + for (int i = 0; i < n; ++i) + morph.push_back(desc[i]); + + std::vector stems = reinterpret_cast(pHunspell)->stem(morph); + return munge_vector(slst, stems); } int Hunspell_generate(Hunhandle* pHunspell, char*** slst, const char* word, - const char* word2) { - return ((Hunspell*)pHunspell)->generate(slst, word, word2); + const char* pattern) { + std::vector stems = reinterpret_cast(pHunspell)->generate(word, pattern); + return munge_vector(slst, stems); } int Hunspell_generate2(Hunhandle* pHunspell, @@ -1821,7 +1938,12 @@ int Hunspell_generate2(Hunhandle* pHunspell, const char* word, char** desc, int n) { - return ((Hunspell*)pHunspell)->generate(slst, word, desc, n); + std::vector morph; + for (int i = 0; i < n; ++i) + morph.push_back(desc[i]); + + std::vector stems = reinterpret_cast(pHunspell)->generate(word, morph); + return munge_vector(slst, stems); } /* functions for run-time modification of the dictionary */ @@ -1829,7 +1951,7 @@ int Hunspell_generate2(Hunhandle* pHunspell, /* add word to the run-time dictionary */ int Hunspell_add(Hunhandle* pHunspell, const char* word) { - return ((Hunspell*)pHunspell)->add(word); + return reinterpret_cast(pHunspell)->add(word); } /* add word to the run-time dictionary with affix flags of @@ -1840,25 +1962,35 @@ int Hunspell_add(Hunhandle* pHunspell, const char* word) { int Hunspell_add_with_affix(Hunhandle* pHunspell, const char* word, const char* example) { - return ((Hunspell*)pHunspell)->add_with_affix(word, example); + return reinterpret_cast(pHunspell)->add_with_affix(word, example); } /* remove word from the run-time dictionary */ int Hunspell_remove(Hunhandle* pHunspell, const char* word) { - return ((Hunspell*)pHunspell)->remove(word); + return reinterpret_cast(pHunspell)->remove(word); } -void Hunspell_free_list(Hunhandle*, char*** slst, int n) { - freelist(slst, n); +void Hunspell_free_list(Hunhandle*, char*** list, int n) { + if (list && *list) { + for (int i = 0; i < n; i++) + free((*list)[i]); + free(*list); + *list = NULL; + } } -int Hunspell::suffix_suggest(char*** slst, const char* root_word) { +std::vector Hunspell::suffix_suggest(const std::string& root_word) { + return m_Impl->suffix_suggest(root_word); +} + +std::vector HunspellImpl::suffix_suggest(const std::string& root_word) { + std::vector slst; struct hentry* he = NULL; int len; std::string w2; const char* word; - char* ignoredchars = pAMgr->get_ignore(); + const char* ignoredchars = pAMgr->get_ignore(); if (ignoredchars != NULL) { w2.assign(root_word); if (utf8) { @@ -1870,26 +2002,18 @@ int Hunspell::suffix_suggest(char*** slst, const char* root_word) { } word = w2.c_str(); } else - word = root_word; + word = root_word.c_str(); len = strlen(word); if (!len) - return 0; + return slst; - char** wlst = (char**)malloc(MAXSUGGESTION * sizeof(char*)); - if (wlst == NULL) - return -1; - *slst = wlst; - for (int i = 0; i < MAXSUGGESTION; i++) { - wlst[i] = NULL; - } - - for (int i = 0; (i < maxdic) && !he; i++) { - he = (pHMgr[i])->lookup(word); + for (size_t i = 0; (i < m_HMgrs.size()) && !he; ++i) { + he = m_HMgrs[i]->lookup(word); } if (he) { - return pAMgr->get_suffix_words(he->astr, he->alen, root_word, *slst); + slst = pAMgr->get_suffix_words(he->astr, he->alen, root_word.c_str()); } - return 0; + return slst; } diff --git a/extensions/spellcheck/hunspell/src/hunspell.h b/extensions/spellcheck/hunspell/src/hunspell.h index 726bbe2077c1..3aca30ab2fba 100644 --- a/extensions/spellcheck/hunspell/src/hunspell.h +++ b/extensions/spellcheck/hunspell/src/hunspell.h @@ -38,8 +38,8 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef _MYSPELLMGR_H_ -#define _MYSPELLMGR_H_ +#ifndef MYSPELLMGR_H_ +#define MYSPELLMGR_H_ #include "hunvisapi.h" diff --git a/extensions/spellcheck/hunspell/src/hunspell.hxx b/extensions/spellcheck/hunspell/src/hunspell.hxx index 401475309b52..48dd278fecaf 100644 --- a/extensions/spellcheck/hunspell/src/hunspell.hxx +++ b/extensions/spellcheck/hunspell/src/hunspell.hxx @@ -70,26 +70,30 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +#ifndef MYSPELLMGR_HXX_ +#define MYSPELLMGR_HXX_ #include "hunvisapi.h" - -#include "hashmgr.hxx" -#include "affixmgr.hxx" -#include "suggestmgr.hxx" -#include "langnum.hxx" +#include "w_char.hxx" +#include "atypes.hxx" +#include #include #define SPELL_XML "" -#define MAXDIC 20 #define MAXSUGGESTION 15 #define MAXSHARPS 5 +#define MAXWORDLEN 176 -#define HUNSPELL_OK (1 << 0) -#define HUNSPELL_OK_WARN (1 << 1) +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +# define H_DEPRECATED __attribute__((__deprecated__)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# define H_DEPRECATED __declspec(deprecated) +#else +# define H_DEPRECATED +#endif -#ifndef _MYSPELLMGR_HXX_ -#define _MYSPELLMGR_HXX_ +class HunspellImpl; class LIBHUNSPELL_DLL_EXPORTED Hunspell { private: @@ -97,17 +101,7 @@ class LIBHUNSPELL_DLL_EXPORTED Hunspell { Hunspell& operator=(const Hunspell&); private: - AffixMgr* pAMgr; - HashMgr* pHMgr[MAXDIC]; - int maxdic; - SuggestMgr* pSMgr; - char* affixpath; - char* encoding; - struct cs_info* csconv; - int langnum; - int utf8; - int complexprefixes; - char** wordbreak; + HunspellImpl* m_Impl; public: /* Hunspell(aff, dic) - constructor of Hunspell class @@ -118,7 +112,6 @@ class LIBHUNSPELL_DLL_EXPORTED Hunspell { * long path names (without the long path prefix Hunspell will use fopen() * with system-dependent character encoding instead of _wfopen()). */ - Hunspell(const char* affpath, const char* dpath, const char* key = NULL); ~Hunspell(); @@ -126,7 +119,7 @@ class LIBHUNSPELL_DLL_EXPORTED Hunspell { int add_dic(const char* dpath, const char* key = NULL); /* spell(word) - spellcheck word - * output: 0 = bad word, not 0 = good word + * output: false = bad word, true = good word * * plus output: * info: information bit array, fields: @@ -134,8 +127,8 @@ class LIBHUNSPELL_DLL_EXPORTED Hunspell { * SPELL_FORBIDDEN = an explicit forbidden word * root: root (stem), when input is a word with affix(es) */ - - int spell(const char* word, int* info = NULL, char** root = NULL); + bool spell(const std::string& word, int* info = NULL, std::string* root = NULL); + H_DEPRECATED int spell(const char* word, int* info = NULL, char** root = NULL); /* suggest(suggestions, word) - search suggestions * input: pointer to an array of strings pointer and the (bad) word @@ -144,8 +137,8 @@ class LIBHUNSPELL_DLL_EXPORTED Hunspell { * a newly allocated array of strings (*slts will be NULL when number * of suggestion equals 0.) */ - - int suggest(char*** slst, const char* word); + std::vector suggest(const std::string& word); + H_DEPRECATED int suggest(char*** slst, const char* word); /* Suggest words from suffix rules * suffix_suggest(suggestions, root_word) @@ -155,36 +148,37 @@ class LIBHUNSPELL_DLL_EXPORTED Hunspell { * a newly allocated array of strings (*slts will be NULL when number * of suggestion equals 0.) */ - int suffix_suggest(char*** slst, const char* root_word); + std::vector suffix_suggest(const std::string& root_word); + H_DEPRECATED int suffix_suggest(char*** slst, const char* root_word); /* deallocate suggestion lists */ + H_DEPRECATED void free_list(char*** slst, int n); - void free_list(char*** slst, int n); - + const std::string& get_dict_encoding() const; char* get_dic_encoding(); /* morphological functions */ /* analyze(result, word) - morphological analysis of the word */ + std::vector analyze(const std::string& word); + H_DEPRECATED int analyze(char*** slst, const char* word); - int analyze(char*** slst, const char* word); + /* stem(word) - stemmer function */ + std::vector stem(const std::string& word); + H_DEPRECATED int stem(char*** slst, const char* word); - /* stem(result, word) - stemmer function */ - - int stem(char*** slst, const char* word); - - /* stem(result, analysis, n) - get stems from a morph. analysis + /* stem(analysis, n) - get stems from a morph. analysis * example: * char ** result, result2; * int n1 = analyze(&result, "words"); * int n2 = stem(&result2, result, n1); */ - - int stem(char*** slst, char** morph, int n); + std::vector stem(const std::vector& morph); + H_DEPRECATED int stem(char*** slst, char** morph, int n); /* generate(result, word, word2) - morphological generation by example(s) */ - - int generate(char*** slst, const char* word, const char* word2); + std::vector generate(const std::string& word, const std::string& word2); + H_DEPRECATED int generate(char*** slst, const char* word, const char* word2); /* generate(result, word, desc, n) - generation by morph. description(s) * example: @@ -193,66 +187,43 @@ class LIBHUNSPELL_DLL_EXPORTED Hunspell { * int n = generate(&result, "word", &affix, 1); * for (int i = 0; i < n; i++) printf("%s\n", result[i]); */ - - int generate(char*** slst, const char* word, char** desc, int n); + std::vector generate(const std::string& word, const std::vector& pl); + H_DEPRECATED int generate(char*** slst, const char* word, char** desc, int n); /* functions for run-time modification of the dictionary */ /* add word to the run-time dictionary */ - int add(const char* word); + int add(const std::string& word); /* add word to the run-time dictionary with affix flags of * the example (a dictionary word): Hunspell will recognize * affixed forms of the new word, too. */ - int add_with_affix(const char* word, const char* example); + int add_with_affix(const std::string& word, const std::string& example); /* remove word from the run-time dictionary */ - int remove(const char* word); + int remove(const std::string& word); /* other */ /* get extra word characters definied in affix file for tokenization */ - const char* get_wordchars(); - const std::vector& get_wordchars_utf16(); + const char* get_wordchars() const; + const std::string& get_wordchars_cpp() const; + const std::vector& get_wordchars_utf16() const; struct cs_info* get_csconv(); - const char* get_version(); + + const char* get_version() const; + const std::string& get_version_cpp() const; int get_langnum() const; /* need for putdic */ - int input_conv(const char* word, char* dest, size_t destsize); - - private: - void cleanword(std::string& dest, const char*, int* pcaptype, int* pabbrev); - size_t cleanword2(std::string& dest, - std::vector& dest_u, - const char*, - int* w_len, - int* pcaptype, - size_t* pabbrev); - void mkinitcap(std::string& u8); - int mkinitcap2(std::string& u8, std::vector& u16); - int mkinitsmall2(std::string& u8, std::vector& u16); - void mkallcap(std::string& u8); - int mkallsmall2(std::string& u8, std::vector& u16); - struct hentry* checkword(const char*, int* info, char** root); - std::string sharps_u8_l1(const std::string& source); - hentry* - spellsharps(std::string& base, size_t start_pos, int, int, int* info, char** root); - int is_keepcase(const hentry* rv); - int insert_sug(char*** slst, const char* word, int ns); - void cat_result(std::string& result, char* st); - char* stem_description(const char* desc); - int spellml(char*** slst, const char* word); - std::string get_xml_par(const char* par); - const char* get_xml_pos(const char* s, const char* attr); - int get_xml_list(char*** slst, const char* list, const char* tag); - int check_xml_par(const char* q, const char* attr, const char* value); + bool input_conv(const std::string& word, std::string& dest); + H_DEPRECATED int input_conv(const char* word, char* dest, size_t destsize); }; #endif diff --git a/extensions/spellcheck/hunspell/src/hunvisapi.h b/extensions/spellcheck/hunspell/src/hunvisapi.h index 503c20f664e2..eb2b348091b7 100644 --- a/extensions/spellcheck/hunspell/src/hunvisapi.h +++ b/extensions/spellcheck/hunspell/src/hunvisapi.h @@ -1,5 +1,5 @@ -#ifndef _HUNSPELL_VISIBILITY_H_ -#define _HUNSPELL_VISIBILITY_H_ +#ifndef HUNSPELL_VISIBILITY_H_ +#define HUNSPELL_VISIBILITY_H_ #if defined(HUNSPELL_STATIC) # define LIBHUNSPELL_DLL_EXPORTED diff --git a/extensions/spellcheck/hunspell/src/hunzip.cxx b/extensions/spellcheck/hunspell/src/hunzip.cxx index b2788a105504..b96d06aac279 100644 --- a/extensions/spellcheck/hunspell/src/hunzip.cxx +++ b/extensions/spellcheck/hunspell/src/hunzip.cxx @@ -59,7 +59,7 @@ int Hunzip::fail(const char* err, const char* par) { } Hunzip::Hunzip(const char* file, const char* key) - : fin(NULL), bufsiz(0), lastbit(0), inc(0), inbits(0), outc(0), dec(NULL) { + : bufsiz(0), lastbit(0), inc(0), inbits(0), outc(0) { in[0] = out[0] = line[0] = '\0'; filename = mystrdup(file); if (getcode(key) == -1) @@ -70,19 +70,19 @@ Hunzip::Hunzip(const char* file, const char* key) int Hunzip::getcode(const char* key) { unsigned char c[2]; - int i, j, n, p; + int i, j, n; int allocatedbit = BASEBITREC; const char* enc = key; if (!filename) return -1; - fin = myfopen(filename, "rb"); - if (!fin) + myopen(fin, filename, std::ios_base::in | std::ios_base::binary); + if (!fin.is_open()) return -1; // read magic number - if ((fread(in, 1, 3, fin) < MAGICLEN) || + if (!fin.read(in, 3) || !(strncmp(MAGIC, in, MAGICLEN) == 0 || strncmp(MAGIC_ENCRYPT, in, MAGICLEN) == 0)) { return fail(MSG_FORMAT, filename); @@ -93,7 +93,7 @@ int Hunzip::getcode(const char* key) { unsigned char cs; if (!key) return fail(MSG_KEY, filename); - if (fread(&c, 1, 1, fin) < 1) + if (!fin.read(reinterpret_cast(c), 1)) return fail(MSG_FORMAT, filename); for (cs = 0; *enc; enc++) cs ^= *enc; @@ -104,7 +104,7 @@ int Hunzip::getcode(const char* key) { key = NULL; // read record count - if (fread(&c, 1, 2, fin) < 2) + if (!fin.read(reinterpret_cast(c), 2)) return fail(MSG_FORMAT, filename); if (key) { @@ -115,16 +115,14 @@ int Hunzip::getcode(const char* key) { } n = ((int)c[0] << 8) + c[1]; - dec = (struct bit*)malloc(BASEBITREC * sizeof(struct bit)); - if (!dec) - return fail(MSG_MEMORY, filename); + dec.resize(BASEBITREC); dec[0].v[0] = 0; dec[0].v[1] = 0; // read codes for (i = 0; i < n; i++) { unsigned char l; - if (fread(c, 1, 2, fin) < 2) + if (!fin.read(reinterpret_cast(c), 2)) return fail(MSG_FORMAT, filename); if (key) { if (*(++enc) == '\0') @@ -134,14 +132,14 @@ int Hunzip::getcode(const char* key) { enc = key; c[1] ^= *enc; } - if (fread(&l, 1, 1, fin) < 1) + if (!fin.read(reinterpret_cast(&l), 1)) return fail(MSG_FORMAT, filename); if (key) { if (*(++enc) == '\0') enc = key; l ^= *enc; } - if (fread(in, 1, l / 8 + 1, fin) < (size_t)l / 8 + 1) + if (!fin.read(in, l / 8 + 1)) return fail(MSG_FORMAT, filename); if (key) for (j = 0; j <= l / 8; j++) { @@ -149,7 +147,7 @@ int Hunzip::getcode(const char* key) { enc = key; in[j] ^= *enc; } - p = 0; + int p = 0; for (j = 0; j < l; j++) { int b = (in[j / 8] & (1 << (7 - (j % 8)))) ? 1 : 0; int oldp = p; @@ -158,7 +156,7 @@ int Hunzip::getcode(const char* key) { lastbit++; if (lastbit == allocatedbit) { allocatedbit += BASEBITREC; - dec = (struct bit*)realloc(dec, allocatedbit * sizeof(struct bit)); + dec.resize(allocatedbit); } dec[lastbit].v[0] = 0; dec[lastbit].v[1] = 0; @@ -173,10 +171,6 @@ int Hunzip::getcode(const char* key) { } Hunzip::~Hunzip() { - if (dec) - free(dec); - if (fin) - fclose(fin); if (filename) free(filename); } @@ -185,16 +179,17 @@ int Hunzip::getbuf() { int p = 0; int o = 0; do { - if (inc == 0) - inbits = fread(in, 1, BUFSIZE, fin) * 8; + if (inc == 0) { + fin.read(in, BUFSIZE); + inbits = fin.gcount() * 8; + } for (; inc < inbits; inc++) { int b = (in[inc / 8] & (1 << (7 - (inc % 8)))) ? 1 : 0; int oldp = p; p = dec[p].v[b]; if (p == 0) { if (oldp == lastbit) { - fclose(fin); - fin = NULL; + fin.close(); // add last odd byte if (dec[lastbit].c[0]) out[o++] = dec[lastbit].c[1]; @@ -212,11 +207,11 @@ int Hunzip::getbuf() { return fail(MSG_FORMAT, filename); } -const char* Hunzip::getline() { +bool Hunzip::getline(std::string& dest) { char linebuf[BUFSIZE]; int l = 0, eol = 0, left = 0, right = 0; if (bufsiz == -1) - return NULL; + return false; while (l < bufsiz && !eol) { linebuf[l++] = out[outc]; switch (out[outc]) { @@ -251,7 +246,7 @@ const char* Hunzip::getline() { } if (++outc == bufsiz) { outc = 0; - bufsiz = fin ? getbuf() : -1; + bufsiz = fin.is_open() ? getbuf() : -1; } } if (right) @@ -259,5 +254,6 @@ const char* Hunzip::getline() { else linebuf[l] = '\0'; strcpy(line + left, linebuf); - return line; + dest.assign(line); + return true; } diff --git a/extensions/spellcheck/hunspell/src/hunzip.hxx b/extensions/spellcheck/hunspell/src/hunzip.hxx index 5082adddb0ea..f4b02bff95b9 100644 --- a/extensions/spellcheck/hunspell/src/hunzip.hxx +++ b/extensions/spellcheck/hunspell/src/hunzip.hxx @@ -41,12 +41,14 @@ /* hunzip: file decompression for sorted dictionaries with optional encryption, * algorithm: prefix-suffix encoding and 16-bit Huffman encoding */ -#ifndef _HUNZIP_HXX_ -#define _HUNZIP_HXX_ +#ifndef HUNZIP_HXX_ +#define HUNZIP_HXX_ #include "hunvisapi.h" #include +#include +#include #define BUFSIZE 65536 #define HZIP_EXTENSION ".hz" @@ -68,9 +70,9 @@ class LIBHUNSPELL_DLL_EXPORTED Hunzip { protected: char* filename; - FILE* fin; + std::ifstream fin; int bufsiz, lastbit, inc, inbits, outc; - struct bit* dec; // code table + std::vector dec; // code table char in[BUFSIZE]; // input buffer char out[BUFSIZE + 1]; // Huffman-decoded buffer char line[BUFSIZE + 50]; // decoded line @@ -81,7 +83,8 @@ class LIBHUNSPELL_DLL_EXPORTED Hunzip { public: Hunzip(const char* filename, const char* key = NULL); ~Hunzip(); - const char* getline(); + bool is_open() { return fin.is_open(); } + bool getline(std::string& dest); }; #endif diff --git a/extensions/spellcheck/hunspell/src/langnum.hxx b/extensions/spellcheck/hunspell/src/langnum.hxx index af5c86e4fe6f..ef53f4ee1b1f 100644 --- a/extensions/spellcheck/hunspell/src/langnum.hxx +++ b/extensions/spellcheck/hunspell/src/langnum.hxx @@ -38,12 +38,12 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef _LANGNUM_HXX_ -#define _LANGNUM_HXX_ +#ifndef LANGNUM_HXX_ +#define LANGNUM_HXX_ /* language numbers for language specific codes - see http://l10n.openoffice.org/languages.html + see https://wiki.openoffice.org/w/index.php?title=Languages&oldid=230199 */ enum { diff --git a/extensions/spellcheck/hunspell/src/phonet.cxx b/extensions/spellcheck/hunspell/src/phonet.cxx index 17350e74a72b..b97bbe885c19 100644 --- a/extensions/spellcheck/hunspell/src/phonet.cxx +++ b/extensions/spellcheck/hunspell/src/phonet.cxx @@ -36,15 +36,13 @@ #include "phonet.hxx" void init_phonet_hash(phonetable& parms) { - int i, k; - - for (i = 0; i < HASHSIZE; i++) { + for (int i = 0; i < HASHSIZE; i++) { parms.hash[i] = -1; } - for (i = 0; parms.rules[i][0] != '\0'; i += 2) { + for (int i = 0; parms.rules[i][0] != '\0'; i += 2) { /** set hash value **/ - k = (unsigned char)parms.rules[i][0]; + int k = (unsigned char)parms.rules[i][0]; if (parms.hash[k] < 0) { parms.hash[k] = i; @@ -73,9 +71,8 @@ static int myisalpha(char ch) { std::string phonet(const std::string& inword, phonetable& parms) { int i, k = 0, p, z; - int k0, n0, p0 = -333, z0; + int k0, n0, p0 = -333; char c; - const char* s; typedef unsigned char uchar; size_t len = inword.size(); @@ -90,15 +87,15 @@ std::string phonet(const std::string& inword, phonetable& parms) { i = z = 0; while ((c = word[i]) != '\0') { int n = parms.hash[(uchar)c]; - z0 = 0; + int z0 = 0; - if (n >= 0) { + if (n >= 0 && !parms.rules[n].empty()) { /** check all rules for the same letter **/ while (parms.rules[n][0] == c) { /** check whole string **/ k = 1; /** number of found letters **/ p = 5; /** default priority **/ - s = parms.rules[n]; + const char*s = parms.rules[n].c_str(); s++; /** important for (see below) "*(s-1)" **/ while (*s != '\0' && word[i + k] == *s && !isdigit((unsigned char)*s) && @@ -142,13 +139,13 @@ std::string phonet(const std::string& inword, phonetable& parms) { n0 = parms.hash[(uchar)c0]; // if (parms.followup && k > 1 && n0 >= 0 - if (k > 1 && n0 >= 0 && p0 != (int)'-' && word[i + k] != '\0') { + if (k > 1 && n0 >= 0 && p0 != (int)'-' && word[i + k] != '\0' && !parms.rules[n0].empty()) { /** test follow-up rule for "word[i+k]" **/ while (parms.rules[n0][0] == c0) { /** check whole string **/ k0 = k; p0 = 5; - s = parms.rules[n0]; + s = parms.rules[n0].c_str(); s++; while (*s != '\0' && word[i + k0] == *s && !isdigit((unsigned char)*s) && @@ -206,9 +203,9 @@ std::string phonet(const std::string& inword, phonetable& parms) { } /** end of follow-up stuff **/ /** replace string **/ - s = parms.rules[n + 1]; - p0 = (parms.rules[n][0] != '\0' && - strchr(parms.rules[n] + 1, '<') != NULL) + s = parms.rules[n + 1].c_str(); + p0 = (!parms.rules[n].empty() && + strchr(parms.rules[n].c_str() + 1, '<') != NULL) ? 1 : 0; if (p0 == 1 && z == 0) { @@ -241,8 +238,8 @@ std::string phonet(const std::string& inword, phonetable& parms) { } /** new "actual letter" **/ c = *s; - if (parms.rules[n][0] != '\0' && - strstr(parms.rules[n] + 1, "^^") != NULL) { + if (!parms.rules[n].empty() && + strstr(parms.rules[n].c_str() + 1, "^^") != NULL) { if (c != '\0') { target.push_back(c); } diff --git a/extensions/spellcheck/hunspell/src/phonet.hxx b/extensions/spellcheck/hunspell/src/phonet.hxx index eb9fd0c628d9..2d58b3ba1b16 100644 --- a/extensions/spellcheck/hunspell/src/phonet.hxx +++ b/extensions/spellcheck/hunspell/src/phonet.hxx @@ -27,8 +27,8 @@ Porting from Aspell to Hunspell using C-like structs */ -#ifndef __PHONETHXX__ -#define __PHONETHXX__ +#ifndef PHONET_HXX_ +#define PHONET_HXX_ #define HASHSIZE 256 #define MAXPHONETLEN 256 @@ -38,9 +38,7 @@ struct phonetable { char utf8; - cs_info* lang; - int num; - char** rules; + std::vector rules; int hash[HASHSIZE]; }; diff --git a/extensions/spellcheck/hunspell/src/replist.cxx b/extensions/spellcheck/hunspell/src/replist.cxx index b3e6b37d20ec..89d4caa7400f 100644 --- a/extensions/spellcheck/hunspell/src/replist.cxx +++ b/extensions/spellcheck/hunspell/src/replist.cxx @@ -90,104 +90,122 @@ RepList::RepList(int n) { RepList::~RepList() { for (int i = 0; i < pos; i++) { - free(dat[i]->pattern); - free(dat[i]->pattern2); - free(dat[i]); + delete dat[i]; } free(dat); } -int RepList::get_pos() { - return pos; -} - replentry* RepList::item(int n) { return dat[n]; } -int RepList::near(const char* word) { +int RepList::find(const char* word) { int p1 = 0; - int p2 = pos; - while ((p2 - p1) > 1) { + int p2 = pos - 1; + while (p1 <= p2) { int m = (p1 + p2) / 2; - int c = strcmp(word, dat[m]->pattern); - if (c <= 0) { - if (c < 0) - p2 = m; - else - p1 = p2 = m; - } else - p1 = m; - } - return p1; -} - -int RepList::match(const char* word, int n) { - if (strncmp(word, dat[n]->pattern, strlen(dat[n]->pattern)) == 0) - return strlen(dat[n]->pattern); - return 0; -} - -int RepList::add(char* pat1, char* pat2) { - if (pos >= size || pat1 == NULL || pat2 == NULL) - return 1; - replentry* r = (replentry*)malloc(sizeof(replentry)); - if (r == NULL) - return 1; - r->pattern = mystrrep(pat1, "_", " "); - r->pattern2 = mystrrep(pat2, "_", " "); - r->start = false; - r->end = false; - dat[pos++] = r; - for (int i = pos - 1; i > 0; i--) { - r = dat[i]; - if (strcmp(r->pattern, dat[i - 1]->pattern) < 0) { - dat[i] = dat[i - 1]; - dat[i - 1] = r; - } else - break; - } - return 0; -} - -int RepList::conv(const char* word, char* dest, size_t destsize) { - size_t stl = 0; - int change = 0; - for (size_t i = 0; i < strlen(word); i++) { - int n = near(word + i); - int l = match(word + i, n); - if (l) { - size_t replen = strlen(dat[n]->pattern2); - if (stl + replen >= destsize) - return -1; - strcpy(dest + stl, dat[n]->pattern2); - stl += replen; - i += l - 1; - change = 1; - } else { - if (stl + 1 >= destsize) - return -1; - dest[stl++] = word[i]; + int c = strncmp(word, dat[m]->pattern.c_str(), dat[m]->pattern.size()); + if (c < 0) + p2 = m - 1; + else if (c > 0) + p1 = m + 1; + else { // scan back for a longer match + for (p1 = m - 1; p1 >= 0; --p1) + if (!strncmp(word, dat[p1]->pattern.c_str(), dat[p1]->pattern.size())) + m = p1; + else if (dat[p1]->pattern.size() < dat[m]->pattern.size()) + break; + return m; } } - dest[stl] = '\0'; - return change; + return -1; } -bool RepList::conv(const char* word, std::string& dest) { +std::string RepList::replace(const char* word, int ind, bool atstart) { + int type = atstart ? 1 : 0; + if (ind < 0) + return std::string(); + if (strlen(word) == dat[ind]->pattern.size()) + type = atstart ? 3 : 2; + while (type && dat[ind]->outstrings[type].empty()) + type = (type == 2 && !atstart) ? 0 : type - 1; + return dat[ind]->outstrings[type]; +} + +int RepList::add(const std::string& in_pat1, const std::string& pat2) { + if (pos >= size || in_pat1.empty() || pat2.empty()) { + return 1; + } + // analyse word context + int type = 0; + std::string pat1(in_pat1); + if (pat1[0] == '_') { + pat1.erase(0, 1); + type = 1; + } + if (!pat1.empty() && pat1[pat1.size() - 1] == '_') { + type = type + 2; + pat1.erase(pat1.size() - 1); + } + mystrrep(pat1, "_", " "); + + // find existing entry + int m = find(pat1.c_str()); + if (m >= 0 && dat[m]->pattern == pat1) { + // since already used + dat[m]->outstrings[type] = pat2; + mystrrep(dat[m]->outstrings[type], "_", " "); + return 0; + } + + // make a new entry if none exists + replentry* r = new replentry; + if (r == NULL) + return 1; + r->pattern = pat1; + r->outstrings[type] = pat2; + mystrrep(r->outstrings[type], "_", " "); + dat[pos++] = r; + // sort to the right place in the list + int i; + for (i = pos - 1; i > 0; i--) { + int c = strncmp(r->pattern.c_str(), dat[i-1]->pattern.c_str(), dat[i-1]->pattern.size()); + if (c > 0) + break; + else if (c == 0) { // subpatterns match. Patterns can't be identical since would catch earlier + for (int j = i - 2; j > 0 && !strncmp(dat[i-1]->pattern.c_str(), dat[j]->pattern.c_str(), dat[i-1]->pattern.size()); --j) + if (dat[j]->pattern.size() > r->pattern.size() || + (dat[j]->pattern.size() == r->pattern.size() && strncmp(dat[j]->pattern.c_str(), r->pattern.c_str(), r->pattern.size()) > 0)) { + i = j; + break; + } + break; + } + } + memmove(dat + i + 1, dat + i, (pos - i - 1) * sizeof(replentry *)); + dat[i] = r; + return 0; +} + +bool RepList::conv(const std::string& in_word, std::string& dest) { dest.clear(); + size_t wordlen = in_word.size(); + const char* word = in_word.c_str(); + bool change = false; - for (size_t i = 0; i < strlen(word); i++) { - int n = near(word + i); - int l = match(word + i, n); - if (l) { - dest.append(dat[n]->pattern2); - i += l - 1; + for (size_t i = 0; i < wordlen; ++i) { + int n = find(word + i); + std::string l = replace(word + i, n, i == 0); + if (!l.empty()) { + dest.append(l); + i += dat[n]->pattern.size() - 1; change = true; } else { dest.push_back(word[i]); } } + return change; } + diff --git a/extensions/spellcheck/hunspell/src/replist.hxx b/extensions/spellcheck/hunspell/src/replist.hxx index 0c5153625954..2f9d350c3842 100644 --- a/extensions/spellcheck/hunspell/src/replist.hxx +++ b/extensions/spellcheck/hunspell/src/replist.hxx @@ -72,17 +72,15 @@ */ /* string replacement list class */ -#ifndef _REPLIST_HXX_ -#define _REPLIST_HXX_ - -#include "hunvisapi.h" +#ifndef REPLIST_HXX_ +#define REPLIST_HXX_ #include "w_char.hxx" #include #include -class LIBHUNSPELL_DLL_EXPORTED RepList { +class RepList { private: RepList(const RepList&); RepList& operator=(const RepList&); @@ -93,15 +91,13 @@ class LIBHUNSPELL_DLL_EXPORTED RepList { int pos; public: - RepList(int n); + explicit RepList(int n); ~RepList(); - int get_pos(); - int add(char* pat1, char* pat2); + int add(const std::string& pat1, const std::string& pat2); replentry* item(int n); - int near(const char* word); - int match(const char* word, int n); - int conv(const char* word, char* dest, size_t destsize); - bool conv(const char* word, std::string& dest); + int find(const char* word); + std::string replace(const char* word, int n, bool atstart); + bool conv(const std::string& word, std::string& dest); }; #endif diff --git a/extensions/spellcheck/hunspell/src/suggestmgr.cxx b/extensions/spellcheck/hunspell/src/suggestmgr.cxx index 17becd75825f..6dff02f10237 100644 --- a/extensions/spellcheck/hunspell/src/suggestmgr.cxx +++ b/extensions/spellcheck/hunspell/src/suggestmgr.cxx @@ -82,7 +82,7 @@ const w_char W_VLINE = {'\0', '|'}; -SuggestMgr::SuggestMgr(const char* tryme, int maxn, AffixMgr* aptr) { +SuggestMgr::SuggestMgr(const char* tryme, unsigned int maxn, AffixMgr* aptr) { // register affix manager and check in string of chars to // try when building candidate suggestions pAMgr = aptr; @@ -91,11 +91,9 @@ SuggestMgr::SuggestMgr(const char* tryme, int maxn, AffixMgr* aptr) { ckeyl = 0; ckey = NULL; - ckey_utf = NULL; ctryl = 0; ctry = NULL; - ctry_utf = NULL; utf8 = 0; langnum = 0; @@ -116,22 +114,14 @@ SuggestMgr::SuggestMgr(const char* tryme, int maxn, AffixMgr* aptr) { if (pAMgr->get_maxcpdsugs() >= 0) maxcpdsugs = pAMgr->get_maxcpdsugs(); if (!utf8) { - char* enc = pAMgr->get_encoding(); - csconv = get_current_cs(enc); - free(enc); + csconv = get_current_cs(pAMgr->get_encoding()); } complexprefixes = pAMgr->get_complexprefixes(); } if (ckey) { if (utf8) { - std::vector t; - ckeyl = u8_u16(t, ckey); - ckey_utf = (w_char*)malloc(ckeyl * sizeof(w_char)); - if (ckey_utf) - memcpy(ckey_utf, &t[0], ckeyl * sizeof(w_char)); - else - ckeyl = 0; + ckeyl = u8_u16(ckey_utf, ckey); } else { ckeyl = strlen(ckey); } @@ -142,13 +132,7 @@ SuggestMgr::SuggestMgr(const char* tryme, int maxn, AffixMgr* aptr) { if (ctry) ctryl = strlen(ctry); if (ctry && utf8) { - std::vector t; - ctryl = u8_u16(t, tryme); - ctry_utf = (w_char*)malloc(ctryl * sizeof(w_char)); - if (ctry_utf) - memcpy(ctry_utf, &t[0], ctryl * sizeof(w_char)); - else - ctryl = 0; + ctryl = u8_u16(ctry_utf, tryme); } } } @@ -158,16 +142,10 @@ SuggestMgr::~SuggestMgr() { if (ckey) free(ckey); ckey = NULL; - if (ckey_utf) - free(ckey_utf); - ckey_utf = NULL; ckeyl = 0; if (ctry) free(ctry); ctry = NULL; - if (ctry_utf) - free(ctry_utf); - ctry_utf = NULL; ctryl = 0; maxSug = 0; #ifdef MOZILLA_CLIENT @@ -175,50 +153,38 @@ SuggestMgr::~SuggestMgr() { #endif } -int SuggestMgr::testsug(char** wlst, - const char* candidate, - int wl, - int ns, +void SuggestMgr::testsug(std::vector& wlst, + const std::string& candidate, int cpdsuggest, int* timer, clock_t* timelimit) { int cwrd = 1; - if (ns == maxSug) - return maxSug; - for (int k = 0; k < ns; k++) { - if (strcmp(candidate, wlst[k]) == 0) { + if (wlst.size() == maxSug) + return; + for (size_t k = 0; k < wlst.size(); ++k) { + if (wlst[k] == candidate) { cwrd = 0; break; } } - if ((cwrd) && checkword(candidate, wl, cpdsuggest, timer, timelimit)) { - wlst[ns] = mystrdup(candidate); - if (wlst[ns] == NULL) { - for (int j = 0; j < ns; j++) - free(wlst[j]); - return -1; - } - ns++; + if ((cwrd) && checkword(candidate, cpdsuggest, timer, timelimit)) { + wlst.push_back(candidate); } - return ns; } // generate suggestions for a misspelled word // pass in address of array of char * pointers // onlycompoundsug: probably bad suggestions (need for ngram sugs, too) - -int SuggestMgr::suggest(char*** slst, +void SuggestMgr::suggest(std::vector& slst, const char* w, - int nsug, int* onlycompoundsug) { int nocompoundtwowords = 0; - char** wlst; std::vector word_utf; int wl = 0; - int nsugorig = nsug; + size_t nsugorig = slst.size(); std::string w2; const char* word = w; - int oldSug = 0; + size_t oldSug = 0; // word reversing wrapper for complex prefixes if (complexprefixes) { @@ -230,22 +196,10 @@ int SuggestMgr::suggest(char*** slst, word = w2.c_str(); } - if (*slst) { - wlst = *slst; - } else { - wlst = (char**)malloc(maxSug * sizeof(char*)); - if (wlst == NULL) - return -1; - for (int i = 0; i < maxSug; i++) { - wlst[i] = NULL; - } - } - if (utf8) { wl = u8_u16(word_utf, word); if (wl == -1) { - *slst = wlst; - return nsug; + return; } } @@ -253,139 +207,131 @@ int SuggestMgr::suggest(char*** slst, cpdsuggest++) { // limit compound suggestion if (cpdsuggest > 0) - oldSug = nsug; + oldSug = slst.size(); // suggestions for an uppercase word (html -> HTML) - if ((nsug < maxSug) && (nsug > -1)) { - nsug = (utf8) ? capchars_utf(wlst, &word_utf[0], wl, nsug, cpdsuggest) - : capchars(wlst, word, nsug, cpdsuggest); + if (slst.size() < maxSug) { + if (utf8) + capchars_utf(slst, &word_utf[0], wl, cpdsuggest); + else + capchars(slst, word, cpdsuggest); } // perhaps we made a typical fault of spelling - if ((nsug < maxSug) && (nsug > -1) && - (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { - nsug = replchars(wlst, word, nsug, cpdsuggest); + if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { + replchars(slst, word, cpdsuggest); } // perhaps we made chose the wrong char from a related set - if ((nsug < maxSug) && (nsug > -1) && - (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { - nsug = mapchars(wlst, word, nsug, cpdsuggest); + if ((slst.size() < maxSug) && + (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { + mapchars(slst, word, cpdsuggest); } // only suggest compound words when no other suggestion - if ((cpdsuggest == 0) && (nsug > nsugorig)) + if ((cpdsuggest == 0) && (slst.size() > nsugorig)) nocompoundtwowords = 1; // did we swap the order of chars by mistake - if ((nsug < maxSug) && (nsug > -1) && - (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { - nsug = (utf8) ? swapchar_utf(wlst, &word_utf[0], wl, nsug, cpdsuggest) - : swapchar(wlst, word, nsug, cpdsuggest); + if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { + if (utf8) + swapchar_utf(slst, &word_utf[0], wl, cpdsuggest); + else + swapchar(slst, word, cpdsuggest); } // did we swap the order of non adjacent chars by mistake - if ((nsug < maxSug) && (nsug > -1) && - (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { - nsug = (utf8) ? longswapchar_utf(wlst, &word_utf[0], wl, nsug, cpdsuggest) - : longswapchar(wlst, word, nsug, cpdsuggest); + if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { + if (utf8) + longswapchar_utf(slst, &word_utf[0], wl, cpdsuggest); + else + longswapchar(slst, word, cpdsuggest); } // did we just hit the wrong key in place of a good char (case and keyboard) - if ((nsug < maxSug) && (nsug > -1) && - (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { - nsug = (utf8) ? badcharkey_utf(wlst, &word_utf[0], wl, nsug, cpdsuggest) - : badcharkey(wlst, word, nsug, cpdsuggest); + if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { + if (utf8) + badcharkey_utf(slst, &word_utf[0], wl, cpdsuggest); + else + badcharkey(slst, word, cpdsuggest); } // did we add a char that should not be there - if ((nsug < maxSug) && (nsug > -1) && - (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { - nsug = (utf8) ? extrachar_utf(wlst, &word_utf[0], wl, nsug, cpdsuggest) - : extrachar(wlst, word, nsug, cpdsuggest); + if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { + if (utf8) + extrachar_utf(slst, &word_utf[0], wl, cpdsuggest); + else + extrachar(slst, word, cpdsuggest); } // did we forgot a char - if ((nsug < maxSug) && (nsug > -1) && - (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { - nsug = (utf8) ? forgotchar_utf(wlst, &word_utf[0], wl, nsug, cpdsuggest) - : forgotchar(wlst, word, nsug, cpdsuggest); + if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { + if (utf8) + forgotchar_utf(slst, &word_utf[0], wl, cpdsuggest); + else + forgotchar(slst, word, cpdsuggest); } // did we move a char - if ((nsug < maxSug) && (nsug > -1) && - (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { - nsug = (utf8) ? movechar_utf(wlst, &word_utf[0], wl, nsug, cpdsuggest) - : movechar(wlst, word, nsug, cpdsuggest); + if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { + if (utf8) + movechar_utf(slst, &word_utf[0], wl, cpdsuggest); + else + movechar(slst, word, cpdsuggest); } // did we just hit the wrong key in place of a good char - if ((nsug < maxSug) && (nsug > -1) && - (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { - nsug = (utf8) ? badchar_utf(wlst, &word_utf[0], wl, nsug, cpdsuggest) - : badchar(wlst, word, nsug, cpdsuggest); + if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { + if (utf8) + badchar_utf(slst, &word_utf[0], wl, cpdsuggest); + else + badchar(slst, word, cpdsuggest); } // did we double two characters - if ((nsug < maxSug) && (nsug > -1) && - (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { - nsug = (utf8) ? doubletwochars_utf(wlst, &word_utf[0], wl, nsug, cpdsuggest) - : doubletwochars(wlst, word, nsug, cpdsuggest); + if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { + if (utf8) + doubletwochars_utf(slst, &word_utf[0], wl, cpdsuggest); + else + doubletwochars(slst, word, cpdsuggest); } // perhaps we forgot to hit space and two words ran together - if (!nosplitsugs && (nsug < maxSug) && (nsug > -1) && - (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { - nsug = twowords(wlst, word, nsug, cpdsuggest); + if (!nosplitsugs && (slst.size() < maxSug) && + (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { + twowords(slst, word, cpdsuggest); } } // repeating ``for'' statement compounding support - if (nsug < 0) { - // we ran out of memory - we should free up as much as possible - for (int i = 0; i < maxSug; i++) - if (wlst[i] != NULL) - free(wlst[i]); - free(wlst); - wlst = NULL; - } - - if (!nocompoundtwowords && (nsug > 0) && onlycompoundsug) + if (!nocompoundtwowords && (!slst.empty()) && onlycompoundsug) *onlycompoundsug = 1; - - *slst = wlst; - return nsug; } // suggestions for an uppercase word (html -> HTML) -int SuggestMgr::capchars_utf(char** wlst, - const w_char* word, - int wl, - int ns, - int cpdsuggest) { +void SuggestMgr::capchars_utf(std::vector& wlst, + const w_char* word, + int wl, + int cpdsuggest) { std::vector candidate_utf(word, word + wl); mkallcap_utf(candidate_utf, langnum); std::string candidate; u16_u8(candidate, candidate_utf); - return testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, - NULL); + testsug(wlst, candidate, cpdsuggest, NULL, NULL); } // suggestions for an uppercase word (html -> HTML) -int SuggestMgr::capchars(char** wlst, - const char* word, - int ns, - int cpdsuggest) { +void SuggestMgr::capchars(std::vector& wlst, + const char* word, + int cpdsuggest) { std::string candidate(word); mkallcap(candidate, csconv); - return testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, - NULL); + testsug(wlst, candidate, cpdsuggest, NULL, NULL); } // suggestions for when chose the wrong char out of a related set -int SuggestMgr::mapchars(char** wlst, +int SuggestMgr::mapchars(std::vector& wlst, const char* word, - int ns, int cpdsuggest) { std::string candidate; clock_t timelimit; @@ -393,120 +339,108 @@ int SuggestMgr::mapchars(char** wlst, int wl = strlen(word); if (wl < 2 || !pAMgr) - return ns; + return wlst.size(); - int nummap = pAMgr->get_nummap(); - struct mapentry* maptable = pAMgr->get_maptable(); - if (maptable == NULL) - return ns; + const std::vector& maptable = pAMgr->get_maptable(); + if (maptable.empty()) + return wlst.size(); timelimit = clock(); timer = MINTIMER; - return map_related(word, candidate, 0, wlst, cpdsuggest, ns, - maptable, nummap, &timer, &timelimit); + return map_related(word, candidate, 0, wlst, cpdsuggest, + maptable, &timer, &timelimit); } int SuggestMgr::map_related(const char* word, std::string& candidate, int wn, - char** wlst, + std::vector& wlst, int cpdsuggest, - int ns, - const mapentry* maptable, - int nummap, + const std::vector& maptable, int* timer, clock_t* timelimit) { if (*(word + wn) == '\0') { int cwrd = 1; - for (int m = 0; m < ns; m++) { - if (candidate == wlst[m]) { + for (size_t m = 0; m < wlst.size(); ++m) { + if (wlst[m] == candidate) { cwrd = 0; break; } } - if ((cwrd) && checkword(candidate.c_str(), candidate.size(), cpdsuggest, timer, timelimit)) { - if (ns < maxSug) { - wlst[ns] = mystrdup(candidate.c_str()); - if (wlst[ns] == NULL) - return -1; - ns++; + if ((cwrd) && checkword(candidate, cpdsuggest, timer, timelimit)) { + if (wlst.size() < maxSug) { + wlst.push_back(candidate); } } - return ns; + return wlst.size(); } int in_map = 0; - for (int j = 0; j < nummap; j++) { - for (int k = 0; k < maptable[j].len; k++) { - int len = strlen(maptable[j].set[k]); - if (strncmp(maptable[j].set[k], word + wn, len) == 0) { + for (size_t j = 0; j < maptable.size(); ++j) { + for (size_t k = 0; k < maptable[j].size(); ++k) { + size_t len = maptable[j][k].size(); + if (strncmp(maptable[j][k].c_str(), word + wn, len) == 0) { in_map = 1; size_t cn = candidate.size(); - for (int l = 0; l < maptable[j].len; l++) { + for (size_t l = 0; l < maptable[j].size(); ++l) { candidate.resize(cn); - candidate.append(maptable[j].set[l]); - ns = map_related(word, candidate, wn + len, wlst, - cpdsuggest, ns, maptable, nummap, timer, timelimit); + candidate.append(maptable[j][l]); + map_related(word, candidate, wn + len, wlst, + cpdsuggest, maptable, timer, timelimit); if (!(*timer)) - return ns; + return wlst.size(); } } } } if (!in_map) { candidate.push_back(*(word + wn)); - ns = map_related(word, candidate, wn + 1, wlst, cpdsuggest, ns, - maptable, nummap, timer, timelimit); + map_related(word, candidate, wn + 1, wlst, cpdsuggest, + maptable, timer, timelimit); } - return ns; + return wlst.size(); } // suggestions for a typical fault of spelling, that // differs with more, than 1 letter from the right form. -int SuggestMgr::replchars(char** wlst, +int SuggestMgr::replchars(std::vector& wlst, const char* word, - int ns, int cpdsuggest) { std::string candidate; int wl = strlen(word); if (wl < 2 || !pAMgr) - return ns; - int numrep = pAMgr->get_numrep(); - struct replentry* reptable = pAMgr->get_reptable(); - if (reptable == NULL) - return ns; - for (int i = 0; i < numrep; i++) { + return wlst.size(); + const std::vector& reptable = pAMgr->get_reptable(); + for (size_t i = 0; i < reptable.size(); ++i) { const char* r = word; // search every occurence of the pattern in the word - while ((r = strstr(r, reptable[i].pattern)) != NULL && - (!reptable[i].end || strlen(r) == strlen(reptable[i].pattern)) && - (!reptable[i].start || r == word)) { + while ((r = strstr(r, reptable[i].pattern.c_str())) != NULL) { + int type = (r == word) ? 1 : 0; + if (r - word + reptable[i].pattern.size() == strlen(word)) + type += 2; + while (type && reptable[i].outstrings[type].empty()) + type = (type == 2 && r != word) ? 0 : type - 1; + const std::string&out = reptable[i].outstrings[type]; + if (out.empty()) { + ++r; + continue; + } candidate.assign(word); candidate.resize(r - word); - candidate.append(reptable[i].pattern2); - int lenp = strlen(reptable[i].pattern); - candidate.append(r + lenp); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, - NULL); - if (ns == -1) - return -1; + candidate.append(reptable[i].outstrings[type]); + candidate.append(r + reptable[i].pattern.size()); + testsug(wlst, candidate, cpdsuggest, NULL, NULL); // check REP suggestions with space size_t sp = candidate.find(' '); if (sp != std::string::npos) { size_t prev = 0; while (sp != std::string::npos) { std::string prev_chunk = candidate.substr(prev, sp - prev); - if (checkword(prev_chunk.c_str(), prev_chunk.size(), 0, NULL, NULL)) { - int oldns = ns; + if (checkword(prev_chunk, 0, NULL, NULL)) { + size_t oldns = wlst.size(); std::string post_chunk = candidate.substr(sp + 1); - ns = testsug(wlst, post_chunk.c_str(), post_chunk.size(), ns, cpdsuggest, NULL, - NULL); - if (ns == -1) - return -1; - if (oldns < ns) { - free(wlst[ns - 1]); - wlst[ns - 1] = mystrdup(candidate.c_str()); - if (!wlst[ns - 1]) - return -1; + testsug(wlst, post_chunk, cpdsuggest, NULL, NULL); + if (oldns < wlst.size()) { + wlst[wlst.size() - 1] = candidate; } } prev = sp + 1; @@ -516,47 +450,43 @@ int SuggestMgr::replchars(char** wlst, r++; // search for the next letter } } - return ns; + return wlst.size(); } // perhaps we doubled two characters (pattern aba -> ababa, for example vacation // -> vacacation) -int SuggestMgr::doubletwochars(char** wlst, +int SuggestMgr::doubletwochars(std::vector& wlst, const char* word, - int ns, int cpdsuggest) { int state = 0; int wl = strlen(word); if (wl < 5 || !pAMgr) - return ns; + return wlst.size(); for (int i = 2; i < wl; i++) { if (word[i] == word[i - 2]) { state++; if (state == 3) { std::string candidate(word, word + i - 1); candidate.insert(candidate.end(), word + i + 1, word + wl); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); state = 0; } } else { state = 0; } } - return ns; + return wlst.size(); } // perhaps we doubled two characters (pattern aba -> ababa, for example vacation // -> vacacation) -int SuggestMgr::doubletwochars_utf(char** wlst, +int SuggestMgr::doubletwochars_utf(std::vector& wlst, const w_char* word, int wl, - int ns, int cpdsuggest) { int state = 0; if (wl < 5 || !pAMgr) - return ns; + return wlst.size(); for (int i = 2; i < wl; i++) { if (word[i] == word[i - 2]) { state++; @@ -565,24 +495,20 @@ int SuggestMgr::doubletwochars_utf(char** wlst, candidate_utf.insert(candidate_utf.end(), word + i + 1, word + wl); std::string candidate; u16_u8(candidate, candidate_utf); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, - NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); state = 0; } } else { state = 0; } } - return ns; + return wlst.size(); } // error is wrong char in place of correct one (case and keyboard related // version) -int SuggestMgr::badcharkey(char** wlst, +int SuggestMgr::badcharkey(std::vector& wlst, const char* word, - int ns, int cpdsuggest) { std::string candidate(word); @@ -593,9 +519,7 @@ int SuggestMgr::badcharkey(char** wlst, // check with uppercase letters candidate[i] = csconv[((unsigned char)tmpc)].cupper; if (tmpc != candidate[i]) { - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); candidate[i] = tmpc; } // check neighbor characters in keyboard string @@ -605,29 +529,24 @@ int SuggestMgr::badcharkey(char** wlst, while (loc) { if ((loc > ckey) && (*(loc - 1) != '|')) { candidate[i] = *(loc - 1); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); } if ((*(loc + 1) != '|') && (*(loc + 1) != '\0')) { candidate[i] = *(loc + 1); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); } loc = strchr(loc + 1, tmpc); } candidate[i] = tmpc; } - return ns; + return wlst.size(); } // error is wrong char in place of correct one (case and keyboard related // version) -int SuggestMgr::badcharkey_utf(char** wlst, +int SuggestMgr::badcharkey_utf(std::vector& wlst, const w_char* word, int wl, - int ns, int cpdsuggest) { std::string candidate; std::vector candidate_utf(word, word + wl); @@ -639,73 +558,61 @@ int SuggestMgr::badcharkey_utf(char** wlst, candidate_utf[i] = upper_utf(candidate_utf[i], 1); if (tmpc != candidate_utf[i]) { u16_u8(candidate, candidate_utf); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, - NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); candidate_utf[i] = tmpc; } // check neighbor characters in keyboard string if (!ckey) continue; - w_char* loc = ckey_utf; - while ((loc < (ckey_utf + ckeyl)) && *loc != tmpc) - loc++; - while (loc < (ckey_utf + ckeyl)) { - if ((loc > ckey_utf) && *(loc - 1) != W_VLINE) { - candidate_utf[i] = *(loc - 1); + size_t loc = 0; + while ((loc < ckeyl) && ckey_utf[loc] != tmpc) + ++loc; + while (loc < ckeyl) { + if ((loc > 0) && ckey_utf[loc - 1] != W_VLINE) { + candidate_utf[i] = ckey_utf[loc - 1]; u16_u8(candidate, candidate_utf); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, - NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); } - if (((loc + 1) < (ckey_utf + ckeyl)) && (*(loc + 1) != W_VLINE)) { - candidate_utf[i] = *(loc + 1); + if (((loc + 1) < ckeyl) && (ckey_utf[loc + 1] != W_VLINE)) { + candidate_utf[i] = ckey_utf[loc + 1]; u16_u8(candidate, candidate_utf); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, - NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); } do { loc++; - } while ((loc < (ckey_utf + ckeyl)) && *loc != tmpc); + } while ((loc < ckeyl) && ckey_utf[loc] != tmpc); } candidate_utf[i] = tmpc; } - return ns; + return wlst.size(); } // error is wrong char in place of correct one -int SuggestMgr::badchar(char** wlst, const char* word, int ns, int cpdsuggest) { +int SuggestMgr::badchar(std::vector& wlst, const char* word, int cpdsuggest) { std::string candidate(word); clock_t timelimit = clock(); int timer = MINTIMER; // swap out each char one by one and try all the tryme // chars in its place to see if that makes a good word - for (int j = 0; j < ctryl; j++) { + for (size_t j = 0; j < ctryl; ++j) { for (std::string::reverse_iterator aI = candidate.rbegin(), aEnd = candidate.rend(); aI != aEnd; ++aI) { char tmpc = *aI; if (ctry[j] == tmpc) continue; *aI = ctry[j]; - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, &timer, &timelimit); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, &timer, &timelimit); if (!timer) - return ns; + return wlst.size(); *aI = tmpc; } } - return ns; + return wlst.size(); } // error is wrong char in place of correct one -int SuggestMgr::badchar_utf(char** wlst, +int SuggestMgr::badchar_utf(std::vector& wlst, const w_char* word, int wl, - int ns, int cpdsuggest) { std::vector candidate_utf(word, word + wl); std::string candidate; @@ -713,34 +620,30 @@ int SuggestMgr::badchar_utf(char** wlst, int timer = MINTIMER; // swap out each char one by one and try all the tryme // chars in its place to see if that makes a good word - for (int j = 0; j < ctryl; j++) { + for (size_t j = 0; j < ctryl; ++j) { for (int i = wl - 1; i >= 0; i--) { w_char tmpc = candidate_utf[i]; if (tmpc == ctry_utf[j]) continue; candidate_utf[i] = ctry_utf[j]; u16_u8(candidate, candidate_utf); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, &timer, - &timelimit); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, &timer, &timelimit); if (!timer) - return ns; + return wlst.size(); candidate_utf[i] = tmpc; } } - return ns; + return wlst.size(); } // error is word has an extra letter it does not need -int SuggestMgr::extrachar_utf(char** wlst, +int SuggestMgr::extrachar_utf(std::vector& wlst, const w_char* word, int wl, - int ns, int cpdsuggest) { std::vector candidate_utf(word, word + wl); if (candidate_utf.size() < 2) - return ns; + return wlst.size(); // try omitting one char of word at a time for (size_t i = 0; i < candidate_utf.size(); ++i) { size_t index = candidate_utf.size() - 1 - i; @@ -748,39 +651,33 @@ int SuggestMgr::extrachar_utf(char** wlst, candidate_utf.erase(candidate_utf.begin() + index); std::string candidate; u16_u8(candidate, candidate_utf); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); candidate_utf.insert(candidate_utf.begin() + index, tmpc); } - return ns; + return wlst.size(); } // error is word has an extra letter it does not need -int SuggestMgr::extrachar(char** wlst, +int SuggestMgr::extrachar(std::vector& wlst, const char* word, - int ns, int cpdsuggest) { std::string candidate(word); if (candidate.size() < 2) - return ns; + return wlst.size(); // try omitting one char of word at a time for (size_t i = 0; i < candidate.size(); ++i) { size_t index = candidate.size() - 1 - i; char tmpc = candidate[index]; candidate.erase(candidate.begin() + index); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); candidate.insert(candidate.begin() + index, tmpc); } - return ns; + return wlst.size(); } // error is missing a letter it needs -int SuggestMgr::forgotchar(char** wlst, +int SuggestMgr::forgotchar(std::vector& wlst, const char* word, - int ns, int cpdsuggest) { std::string candidate(word); clock_t timelimit = clock(); @@ -788,26 +685,23 @@ int SuggestMgr::forgotchar(char** wlst, // try inserting a tryme character before every letter (and the null // terminator) - for (int k = 0; k < ctryl; ++k) { + for (size_t k = 0; k < ctryl; ++k) { for (size_t i = 0; i <= candidate.size(); ++i) { size_t index = candidate.size() - i; candidate.insert(candidate.begin() + index, ctry[k]); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, &timer, &timelimit); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, &timer, &timelimit); if (!timer) - return ns; + return wlst.size(); candidate.erase(candidate.begin() + index); } } - return ns; + return wlst.size(); } // error is missing a letter it needs -int SuggestMgr::forgotchar_utf(char** wlst, +int SuggestMgr::forgotchar_utf(std::vector& wlst, const w_char* word, int wl, - int ns, int cpdsuggest) { std::vector candidate_utf(word, word + wl); clock_t timelimit = clock(); @@ -815,36 +709,32 @@ int SuggestMgr::forgotchar_utf(char** wlst, // try inserting a tryme character at the end of the word and before every // letter - for (int k = 0; k < ctryl; ++k) { + for (size_t k = 0; k < ctryl; ++k) { for (size_t i = 0; i <= candidate_utf.size(); ++i) { size_t index = candidate_utf.size() - i; candidate_utf.insert(candidate_utf.begin() + index, ctry_utf[k]); std::string candidate; u16_u8(candidate, candidate_utf); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, &timer, - &timelimit); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, &timer, &timelimit); if (!timer) - return ns; + return wlst.size(); candidate_utf.erase(candidate_utf.begin() + index); } } - return ns; + return wlst.size(); } /* error is should have been two words */ -int SuggestMgr::twowords(char** wlst, +int SuggestMgr::twowords(std::vector& wlst, const char* word, - int ns, int cpdsuggest) { - int c1, c2; + int c2; int forbidden = 0; int cwrd; int wl = strlen(word); if (wl < 3) - return ns; + return wlst.size(); if (langnum == LANG_hu) forbidden = check_forbidden(word, wl); @@ -864,9 +754,9 @@ int SuggestMgr::twowords(char** wlst, if (utf8 && p[1] == '\0') break; // last UTF-8 character *p = '\0'; - c1 = checkword(candidate, strlen(candidate), cpdsuggest, NULL, NULL); + int c1 = checkword(candidate, cpdsuggest, NULL, NULL); if (c1) { - c2 = checkword((p + 1), strlen(p + 1), cpdsuggest, NULL, NULL); + c2 = checkword((p + 1), cpdsuggest, NULL, NULL); if (c2) { *p = ' '; @@ -880,24 +770,19 @@ int SuggestMgr::twowords(char** wlst, *p = '-'; cwrd = 1; - for (int k = 0; k < ns; k++) { - if (strcmp(candidate, wlst[k]) == 0) { + for (size_t k = 0; k < wlst.size(); ++k) { + if (wlst[k] == candidate) { cwrd = 0; break; } } - if (ns < maxSug) { + if (wlst.size() < maxSug) { if (cwrd) { - wlst[ns] = mystrdup(candidate); - if (wlst[ns] == NULL) { - free(candidate); - return -1; - } - ns++; + wlst.push_back(candidate); } } else { free(candidate); - return ns; + return wlst.size(); } // add two word suggestion with dash, if TRY string contains // "a" or "-" @@ -905,48 +790,40 @@ int SuggestMgr::twowords(char** wlst, if (ctry && (strchr(ctry, 'a') || strchr(ctry, '-')) && mystrlen(p + 1) > 1 && mystrlen(candidate) - mystrlen(p) > 1) { *p = '-'; - for (int k = 0; k < ns; k++) { - if (strcmp(candidate, wlst[k]) == 0) { + for (size_t k = 0; k < wlst.size(); ++k) { + if (wlst[k] == candidate) { cwrd = 0; break; } } - if (ns < maxSug) { + if (wlst.size() < maxSug) { if (cwrd) { - wlst[ns] = mystrdup(candidate); - if (wlst[ns] == NULL) { - free(candidate); - return -1; - } - ns++; + wlst.push_back(candidate); } } else { free(candidate); - return ns; + return wlst.size(); } } } } } free(candidate); - return ns; + return wlst.size(); } // error is adjacent letter were swapped -int SuggestMgr::swapchar(char** wlst, +int SuggestMgr::swapchar(std::vector& wlst, const char* word, - int ns, int cpdsuggest) { std::string candidate(word); if (candidate.size() < 2) - return ns; + return wlst.size(); // try swapping adjacent chars one by one for (size_t i = 0; i < candidate.size() - 1; ++i) { std::swap(candidate[i], candidate[i+1]); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); std::swap(candidate[i], candidate[i+1]); } @@ -958,40 +835,33 @@ int SuggestMgr::swapchar(char** wlst, candidate[2] = word[2]; candidate[candidate.size() - 2] = word[candidate.size() - 1]; candidate[candidate.size() - 1] = word[candidate.size() - 2]; - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); if (candidate.size() == 5) { candidate[0] = word[0]; candidate[1] = word[2]; candidate[2] = word[1]; - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); } } - return ns; + return wlst.size(); } // error is adjacent letter were swapped -int SuggestMgr::swapchar_utf(char** wlst, +int SuggestMgr::swapchar_utf(std::vector& wlst, const w_char* word, int wl, - int ns, int cpdsuggest) { std::vector candidate_utf(word, word + wl); if (candidate_utf.size() < 2) - return ns; + return wlst.size(); std::string candidate; // try swapping adjacent chars one by one for (size_t i = 0; i < candidate_utf.size() - 1; ++i) { std::swap(candidate_utf[i], candidate_utf[i+1]); u16_u8(candidate, candidate_utf); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); std::swap(candidate_utf[i], candidate_utf[i+1]); } @@ -1004,76 +874,64 @@ int SuggestMgr::swapchar_utf(char** wlst, candidate_utf[candidate_utf.size() - 2] = word[candidate_utf.size() - 1]; candidate_utf[candidate_utf.size() - 1] = word[candidate_utf.size() - 2]; u16_u8(candidate, candidate_utf); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); if (candidate_utf.size() == 5) { candidate_utf[0] = word[0]; candidate_utf[1] = word[2]; candidate_utf[2] = word[1]; u16_u8(candidate, candidate_utf); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); } } - return ns; + return wlst.size(); } // error is not adjacent letter were swapped -int SuggestMgr::longswapchar(char** wlst, +int SuggestMgr::longswapchar(std::vector& wlst, const char* word, - int ns, int cpdsuggest) { std::string candidate(word); // try swapping not adjacent chars one by one for (std::string::iterator p = candidate.begin(); p < candidate.end(); ++p) { for (std::string::iterator q = candidate.begin(); q < candidate.end(); ++q) { - if (abs(std::distance(q, p)) > 1) { + if (std::abs(std::distance(q, p)) > 1) { std::swap(*p, *q); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); std::swap(*p, *q); } } } - return ns; + return wlst.size(); } // error is adjacent letter were swapped -int SuggestMgr::longswapchar_utf(char** wlst, +int SuggestMgr::longswapchar_utf(std::vector& wlst, const w_char* word, int wl, - int ns, int cpdsuggest) { std::vector candidate_utf(word, word + wl); // try swapping not adjacent chars for (std::vector::iterator p = candidate_utf.begin(); p < candidate_utf.end(); ++p) { for (std::vector::iterator q = candidate_utf.begin(); q < candidate_utf.end(); ++q) { - if (abs(std::distance(q, p)) > 1) { + if (std::abs(std::distance(q, p)) > 1) { std::swap(*p, *q); std::string candidate; u16_u8(candidate, candidate_utf); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, - NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); std::swap(*p, *q); } } } - return ns; + return wlst.size(); } // error is a letter was moved -int SuggestMgr::movechar(char** wlst, +int SuggestMgr::movechar(std::vector& wlst, const char* word, - int ns, int cpdsuggest) { std::string candidate(word); if (candidate.size() < 2) - return ns; + return wlst.size(); // try moving a char for (std::string::iterator p = candidate.begin(); p < candidate.end(); ++p) { @@ -1081,9 +939,7 @@ int SuggestMgr::movechar(char** wlst, std::swap(*q, *(q - 1)); if (std::distance(p, q) < 2) continue; // omit swap char - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); } std::copy(word, word + candidate.size(), candidate.begin()); } @@ -1093,25 +949,22 @@ int SuggestMgr::movechar(char** wlst, std::swap(*q, *(q - 1)); if (std::distance(p, q) < 2) continue; // omit swap char - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); } std::copy(word, word + candidate.size(), candidate.begin()); } - return ns; + return wlst.size(); } // error is a letter was moved -int SuggestMgr::movechar_utf(char** wlst, +int SuggestMgr::movechar_utf(std::vector& wlst, const w_char* word, int wl, - int ns, int cpdsuggest) { std::vector candidate_utf(word, word + wl); if (candidate_utf.size() < 2) - return ns; + return wlst.size(); // try moving a char for (std::vector::iterator p = candidate_utf.begin(); p < candidate_utf.end(); ++p) { @@ -1121,39 +974,30 @@ int SuggestMgr::movechar_utf(char** wlst, continue; // omit swap char std::string candidate; u16_u8(candidate, candidate_utf); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, - NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); } std::copy(word, word + candidate_utf.size(), candidate_utf.begin()); } - for (std::vector::iterator p = candidate_utf.begin() + candidate_utf.size() - 1; p > candidate_utf.begin(); --p) { - for (std::vector::iterator q = p - 1; q >= candidate_utf.begin() && std::distance(q, p) < 10; --q) { - std::swap(*q, *(q + 1)); - if (std::distance(q, p) < 2) + for (std::vector::reverse_iterator p = candidate_utf.rbegin(); p < candidate_utf.rend(); ++p) { + for (std::vector::reverse_iterator q = p + 1; q < candidate_utf.rend() && std::distance(p, q) < 10; ++q) { + std::swap(*q, *(q - 1)); + if (std::distance(p, q) < 2) continue; // omit swap char std::string candidate; u16_u8(candidate, candidate_utf); - ns = testsug(wlst, candidate.c_str(), candidate.size(), ns, cpdsuggest, NULL, - NULL); - if (ns == -1) - return -1; + testsug(wlst, candidate, cpdsuggest, NULL, NULL); } std::copy(word, word + candidate_utf.size(), candidate_utf.begin()); } - return ns; + return wlst.size(); } // generate a set of suggestions for very poorly spelled words -int SuggestMgr::ngsuggest(char** wlst, +void SuggestMgr::ngsuggest(std::vector& wlst, const char* w, - int ns, - HashMgr** pHMgr, - int md) { - int i, j; + const std::vector& rHMgr) { int lval; int sc; int lp, lpphon; @@ -1165,7 +1009,7 @@ int SuggestMgr::ngsuggest(char** wlst, char* rootsphon[MAX_ROOTS]; int scores[MAX_ROOTS]; int scoresphon[MAX_ROOTS]; - for (i = 0; i < MAX_ROOTS; i++) { + for (int i = 0; i < MAX_ROOTS; i++) { roots[i] = NULL; scores[i] = -100 * i; rootsphon[i] = NULL; @@ -1225,8 +1069,8 @@ int SuggestMgr::ngsuggest(char** wlst, FLAG nongramsuggest = pAMgr ? pAMgr->get_nongramsuggest() : FLAG_NULL; FLAG onlyincompound = pAMgr ? pAMgr->get_onlyincompound() : FLAG_NULL; - for (i = 0; i < md; i++) { - while (0 != (hp = (pHMgr[i])->walk_hashtable(col, hp))) { + for (size_t i = 0; i < rHMgr.size(); ++i) { + while (0 != (hp = rHMgr[i]->walk_hashtable(col, hp))) { if ((hp->astr) && (pAMgr) && (TESTAFF(hp->astr, forbiddenword, hp->alen) || TESTAFF(hp->astr, ONLYUPCASEFLAG, hp->alen) || @@ -1267,7 +1111,7 @@ int SuggestMgr::ngsuggest(char** wlst, scores[lp] = sc; roots[lp] = hp; lval = sc; - for (j = 0; j < MAX_ROOTS; j++) + for (int j = 0; j < MAX_ROOTS; j++) if (scores[j] < lval) { lp = j; lval = scores[j]; @@ -1278,7 +1122,7 @@ int SuggestMgr::ngsuggest(char** wlst, scoresphon[lpphon] = scphon; rootsphon[lpphon] = HENTRY_WORD(hp); lval = scphon; - for (j = 0; j < MAX_ROOTS; j++) + for (int j = 0; j < MAX_ROOTS; j++) if (scoresphon[j] < lval) { lpphon = j; lval = scoresphon[j]; @@ -1293,6 +1137,7 @@ int SuggestMgr::ngsuggest(char** wlst, int thresh = 0; for (int sp = 1; sp < 4; sp++) { if (utf8) { + u8_u16(u8, word); for (int k = sp; k < n; k += 4) { u8[k].l = '*'; u8[k].h = 0; @@ -1316,7 +1161,7 @@ int SuggestMgr::ngsuggest(char** wlst, char* guess[MAX_GUESS]; char* guessorig[MAX_GUESS]; int gscore[MAX_GUESS]; - for (i = 0; i < MAX_GUESS; i++) { + for (int i = 0; i < MAX_GUESS; i++) { guess[i] = NULL; guessorig[i] = NULL; gscore[i] = -100 * i; @@ -1329,10 +1174,10 @@ int SuggestMgr::ngsuggest(char** wlst, if (!glst) { if (nonbmp) utf8 = 1; - return ns; + return; } - for (i = 0; i < MAX_ROOTS; i++) { + for (int i = 0; i < MAX_ROOTS; i++) { if (roots[i]) { struct hentry* rp = roots[i]; @@ -1361,7 +1206,7 @@ int SuggestMgr::ngsuggest(char** wlst, guess[lp] = glst[k].word; guessorig[lp] = glst[k].orig; lval = sc; - for (j = 0; j < MAX_GUESS; j++) + for (int j = 0; j < MAX_GUESS; j++) if (gscore[j] < lval) { lp = j; lval = gscore[j]; @@ -1400,7 +1245,7 @@ int SuggestMgr::ngsuggest(char** wlst, fact = (10.0 - maxd) / 5.0; } - for (i = 0; i < MAX_GUESS; i++) { + for (int i = 0; i < MAX_GUESS; i++) { if (guess[i]) { // lowering guess[i] std::string gl; @@ -1454,7 +1299,7 @@ int SuggestMgr::ngsuggest(char** wlst, // phonetic version if (ph) - for (i = 0; i < MAX_ROOTS; i++) { + for (int i = 0; i < MAX_ROOTS; i++) { if (rootsphon[i]) { // lowering rootphon[i] std::string gl; @@ -1482,12 +1327,12 @@ int SuggestMgr::ngsuggest(char** wlst, bubblesort(&rootsphon[0], NULL, &scoresphon[0], MAX_ROOTS); // copy over - int oldns = ns; + size_t oldns = wlst.size(); int same = 0; - for (i = 0; i < MAX_GUESS; i++) { + for (int i = 0; i < MAX_GUESS; i++) { if (guess[i]) { - if ((ns < oldns + maxngramsugs) && (ns < maxSug) && + if ((wlst.size() < oldns + maxngramsugs) && (wlst.size() < maxSug) && (!same || (gscore[i] > 1000))) { int unique = 1; // leave only excellent suggestions, if exists @@ -1496,35 +1341,34 @@ int SuggestMgr::ngsuggest(char** wlst, else if (gscore[i] < -100) { same = 1; // keep the best ngram suggestions, unless in ONLYMAXDIFF mode - if (ns > oldns || (pAMgr && pAMgr->get_onlymaxdiff())) { + if (wlst.size() > oldns || (pAMgr && pAMgr->get_onlymaxdiff())) { free(guess[i]); if (guessorig[i]) free(guessorig[i]); continue; } } - for (j = 0; j < ns; j++) { + for (size_t j = 0; j < wlst.size(); ++j) { // don't suggest previous suggestions or a previous suggestion with // prefixes or affixes - if ((!guessorig[i] && strstr(guess[i], wlst[j])) || - (guessorig[i] && strstr(guessorig[i], wlst[j])) || + if ((!guessorig[i] && strstr(guess[i], wlst[j].c_str())) || + (guessorig[i] && strstr(guessorig[i], wlst[j].c_str())) || // check forbidden words - !checkword(guess[i], strlen(guess[i]), 0, NULL, NULL)) { + !checkword(guess[i], 0, NULL, NULL)) { unique = 0; break; } } if (unique) { - wlst[ns++] = guess[i]; if (guessorig[i]) { - free(guess[i]); - wlst[ns - 1] = guessorig[i]; + wlst.push_back(guessorig[i]); + } else { + wlst.push_back(guess[i]); } - } else { - free(guess[i]); - if (guessorig[i]) - free(guessorig[i]); } + free(guess[i]); + if (guessorig[i]) + free(guessorig[i]); } else { free(guess[i]); if (guessorig[i]) @@ -1533,26 +1377,24 @@ int SuggestMgr::ngsuggest(char** wlst, } } - oldns = ns; + oldns = wlst.size(); if (ph) - for (i = 0; i < MAX_ROOTS; i++) { + for (int i = 0; i < MAX_ROOTS; i++) { if (rootsphon[i]) { - if ((ns < oldns + MAXPHONSUGS) && (ns < maxSug)) { + if ((wlst.size() < oldns + MAXPHONSUGS) && (wlst.size() < maxSug)) { int unique = 1; - for (j = 0; j < ns; j++) { + for (size_t j = 0; j < wlst.size(); ++j) { // don't suggest previous suggestions or a previous suggestion with // prefixes or affixes - if (strstr(rootsphon[i], wlst[j]) || + if (strstr(rootsphon[i], wlst[j].c_str()) || // check forbidden words - !checkword(rootsphon[i], strlen(rootsphon[i]), 0, NULL, NULL)) { + !checkword(rootsphon[i], 0, NULL, NULL)) { unique = 0; break; } } if (unique) { - wlst[ns++] = mystrdup(rootsphon[i]); - if (!wlst[ns - 1]) - return ns - 1; + wlst.push_back(rootsphon[i]); } } } @@ -1560,7 +1402,6 @@ int SuggestMgr::ngsuggest(char** wlst, if (nonbmp) utf8 = 1; - return ns; } // see if a candidate suggestion is spelled correctly @@ -1569,15 +1410,10 @@ int SuggestMgr::ngsuggest(char** wlst, // obsolote MySpell-HU modifications: // return value 2 and 3 marks compounding with hyphen (-) // `3' marks roots without suffix -int SuggestMgr::checkword(const char* word, - int len, +int SuggestMgr::checkword(const std::string& word, int cpdsuggest, int* timer, clock_t* timelimit) { - struct hentry* rv = NULL; - struct hentry* rv2 = NULL; - int nosuffix = 0; - // check time limit if (timer) { (*timer)--; @@ -1589,13 +1425,16 @@ int SuggestMgr::checkword(const char* word, } if (pAMgr) { + struct hentry* rv = NULL; + int nosuffix = 0; + if (cpdsuggest == 1) { if (pAMgr->get_compound()) { + struct hentry* rv2 = NULL; struct hentry* rwords[100]; // buffer for COMPOUND pattern checking - rv = pAMgr->compound_check(word, len, 0, 0, 100, 0, NULL, (hentry**)&rwords, 0, 1, - 0); // EXT + rv = pAMgr->compound_check(word, 0, 0, 100, 0, NULL, (hentry**)&rwords, 0, 1, 0); // EXT if (rv && - (!(rv2 = pAMgr->lookup(word)) || !rv2->astr || + (!(rv2 = pAMgr->lookup(word.c_str())) || !rv2->astr || !(TESTAFF(rv2->astr, pAMgr->get_forbiddenword(), rv2->alen) || TESTAFF(rv2->astr, pAMgr->get_nosuggest(), rv2->alen)))) return 3; // XXX obsolote categorisation + only ICONV needs affix @@ -1604,7 +1443,7 @@ int SuggestMgr::checkword(const char* word, return 0; } - rv = pAMgr->lookup(word); + rv = pAMgr->lookup(word.c_str()); if (rv) { if ((rv->astr) && @@ -1621,20 +1460,20 @@ int SuggestMgr::checkword(const char* word, break; } } else - rv = pAMgr->prefix_check(word, len, + rv = pAMgr->prefix_check(word.c_str(), word.size(), 0); // only prefix, and prefix + suffix XXX if (rv) { nosuffix = 1; } else { - rv = pAMgr->suffix_check(word, len, 0, NULL, NULL, 0, - NULL); // only suffix + rv = pAMgr->suffix_check(word.c_str(), word.size(), 0, NULL, + FLAG_NULL, FLAG_NULL, IN_CPD_NOT); // only suffix } if (!rv && pAMgr->have_contclass()) { - rv = pAMgr->suffix_check_twosfx(word, len, 0, NULL, FLAG_NULL); + rv = pAMgr->suffix_check_twosfx(word.c_str(), word.size(), 0, NULL, FLAG_NULL); if (!rv) - rv = pAMgr->prefix_check_twosfx(word, len, 1, FLAG_NULL); + rv = pAMgr->prefix_check_twosfx(word.c_str(), word.size(), 1, FLAG_NULL); } // check forbidden words @@ -1656,17 +1495,15 @@ int SuggestMgr::checkword(const char* word, } int SuggestMgr::check_forbidden(const char* word, int len) { - struct hentry* rv = NULL; - if (pAMgr) { - rv = pAMgr->lookup(word); + struct hentry* rv = pAMgr->lookup(word); if (rv && rv->astr && (TESTAFF(rv->astr, pAMgr->get_needaffix(), rv->alen) || TESTAFF(rv->astr, pAMgr->get_onlyincompound(), rv->alen))) rv = NULL; if (!(pAMgr->prefix_check(word, len, 1))) - rv = pAMgr->suffix_check(word, len, 0, NULL, NULL, 0, - NULL); // prefix+suffix, suffix + rv = pAMgr->suffix_check(word, len, 0, NULL, + FLAG_NULL, FLAG_NULL, IN_CPD_NOT); // prefix+suffix, suffix // check forbidden words if ((rv) && (rv->astr) && TESTAFF(rv->astr, pAMgr->get_forbiddenword(), rv->alen)) @@ -1675,32 +1512,25 @@ int SuggestMgr::check_forbidden(const char* word, int len) { return 0; } -char* SuggestMgr::suggest_morph(const char* w) { - char result[MAXLNLEN]; - char* r = (char*)result; - char* st; +std::string SuggestMgr::suggest_morph(const std::string& in_w) { + std::string result; struct hentry* rv = NULL; - *result = '\0'; - if (!pAMgr) - return NULL; + return std::string(); - std::string w2; - const char* word = w; + std::string w(in_w); // word reversing wrapper for complex prefixes if (complexprefixes) { - w2.assign(w); if (utf8) - reverseword_utf(w2); + reverseword_utf(w); else - reverseword(w2); - word = w2.c_str(); + reverseword(w); } - rv = pAMgr->lookup(word); + rv = pAMgr->lookup(w.c_str()); while (rv) { if ((!rv->astr) || @@ -1708,55 +1538,75 @@ char* SuggestMgr::suggest_morph(const char* w) { TESTAFF(rv->astr, pAMgr->get_needaffix(), rv->alen) || TESTAFF(rv->astr, pAMgr->get_onlyincompound(), rv->alen))) { if (!HENTRY_FIND(rv, MORPH_STEM)) { - mystrcat(result, " ", MAXLNLEN); - mystrcat(result, MORPH_STEM, MAXLNLEN); - mystrcat(result, word, MAXLNLEN); + result.append(" "); + result.append(MORPH_STEM); + result.append(w); } if (HENTRY_DATA(rv)) { - mystrcat(result, " ", MAXLNLEN); - mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + result.append(" "); + result.append(HENTRY_DATA2(rv)); } - mystrcat(result, "\n", MAXLNLEN); + result.append("\n"); } rv = rv->next_homonym; } - st = pAMgr->affix_check_morph(word, strlen(word)); - if (st) { - mystrcat(result, st, MAXLNLEN); - free(st); + std::string st = pAMgr->affix_check_morph(w.c_str(), w.size()); + if (!st.empty()) { + result.append(st); } - if (pAMgr->get_compound() && (*result == '\0')) { + if (pAMgr->get_compound() && result.empty()) { struct hentry* rwords[100]; // buffer for COMPOUND pattern checking - pAMgr->compound_check_morph(word, strlen(word), 0, 0, 100, 0, NULL, (hentry**)&rwords, 0, &r, + pAMgr->compound_check_morph(w.c_str(), w.size(), 0, 0, 100, 0, NULL, (hentry**)&rwords, 0, result, NULL); } - return (*result) ? mystrdup(line_uniq(result, MSEP_REC)) : NULL; + line_uniq(result, MSEP_REC); + + return result; +} + +static int get_sfxcount(const char* morph) { + if (!morph || !*morph) + return 0; + int n = 0; + const char* old = morph; + morph = strstr(morph, MORPH_DERI_SFX); + if (!morph) + morph = strstr(old, MORPH_INFL_SFX); + if (!morph) + morph = strstr(old, MORPH_TERM_SFX); + while (morph) { + n++; + old = morph; + morph = strstr(morph + 1, MORPH_DERI_SFX); + if (!morph) + morph = strstr(old + 1, MORPH_INFL_SFX); + if (!morph) + morph = strstr(old + 1, MORPH_TERM_SFX); + } + return n; } /* affixation */ -char* SuggestMgr::suggest_hentry_gen(hentry* rv, const char* pattern) { - char result[MAXLNLEN]; - *result = '\0'; +std::string SuggestMgr::suggest_hentry_gen(hentry* rv, const char* pattern) { + std::string result; int sfxcount = get_sfxcount(pattern); if (get_sfxcount(HENTRY_DATA(rv)) > sfxcount) - return NULL; + return result; if (HENTRY_DATA(rv)) { - char* aff = pAMgr->morphgen(HENTRY_WORD(rv), rv->blen, rv->astr, rv->alen, - HENTRY_DATA(rv), pattern, 0); - if (aff) { - mystrcat(result, aff, MAXLNLEN); - mystrcat(result, "\n", MAXLNLEN); - free(aff); + std::string aff = pAMgr->morphgen(HENTRY_WORD(rv), rv->blen, rv->astr, rv->alen, + HENTRY_DATA(rv), pattern, 0); + if (!aff.empty()) { + result.append(aff); + result.append("\n"); } } // check all allomorphs - char allomorph[MAXLNLEN]; char* p = NULL; if (HENTRY_DATA(rv)) p = (char*)strstr(HENTRY_DATA2(rv), MORPH_ALLOMORPH); @@ -1764,9 +1614,8 @@ char* SuggestMgr::suggest_hentry_gen(hentry* rv, const char* pattern) { struct hentry* rv2 = NULL; p += MORPH_TAG_LEN; int plen = fieldlen(p); - strncpy(allomorph, p, plen); - allomorph[plen] = '\0'; - rv2 = pAMgr->lookup(allomorph); + std::string allomorph(p, plen); + rv2 = pAMgr->lookup(allomorph.c_str()); while (rv2) { // if (HENTRY_DATA(rv2) && get_sfxcount(HENTRY_DATA(rv2)) <= // sfxcount) { @@ -1774,12 +1623,11 @@ char* SuggestMgr::suggest_hentry_gen(hentry* rv, const char* pattern) { char* st = (char*)strstr(HENTRY_DATA2(rv2), MORPH_STEM); if (st && (strncmp(st + MORPH_TAG_LEN, HENTRY_WORD(rv), fieldlen(st + MORPH_TAG_LEN)) == 0)) { - char* aff = pAMgr->morphgen(HENTRY_WORD(rv2), rv2->blen, rv2->astr, - rv2->alen, HENTRY_DATA(rv2), pattern, 0); - if (aff) { - mystrcat(result, aff, MAXLNLEN); - mystrcat(result, "\n", MAXLNLEN); - free(aff); + std::string aff = pAMgr->morphgen(HENTRY_WORD(rv2), rv2->blen, rv2->astr, + rv2->alen, HENTRY_DATA(rv2), pattern, 0); + if (!aff.empty()) { + result.append(aff); + result.append("\n"); } } } @@ -1788,27 +1636,28 @@ char* SuggestMgr::suggest_hentry_gen(hentry* rv, const char* pattern) { p = strstr(p + plen, MORPH_ALLOMORPH); } - return (*result) ? mystrdup(result) : NULL; + return result; } -char* SuggestMgr::suggest_gen(char** desc, int n, const char* pattern) { - if (n == 0 || !pAMgr) - return NULL; +std::string SuggestMgr::suggest_gen(const std::vector& desc, const std::string& in_pattern) { + if (desc.empty() || !pAMgr) + return std::string(); + const char* pattern = in_pattern.c_str(); std::string result2; std::string newpattern; struct hentry* rv = NULL; // search affixed forms with and without derivational suffixes while (1) { - for (int k = 0; k < n; k++) { + for (size_t k = 0; k < desc.size(); ++k) { std::string result; // add compound word parts (except the last one) - char* s = (char*)desc[k]; - char* part = strstr(s, MORPH_PART); + const char* s = desc[k].c_str(); + const char* part = strstr(s, MORPH_PART); if (part) { - char* nextpart = strstr(part + 1, MORPH_PART); + const char* nextpart = strstr(part + 1, MORPH_PART); while (nextpart) { std::string field; copy_field(field, part, MORPH_PART); @@ -1819,56 +1668,50 @@ char* SuggestMgr::suggest_gen(char** desc, int n, const char* pattern) { s = part; } - char** pl; std::string tok(s); size_t pos = tok.find(" | "); while (pos != std::string::npos) { tok[pos + 1] = MSEP_ALT; pos = tok.find(" | ", pos); } - int pln = line_tok(tok.c_str(), &pl, MSEP_ALT); - for (int i = 0; i < pln; i++) { + std::vector pl = line_tok(tok, MSEP_ALT); + for (size_t i = 0; i < pl.size(); ++i) { // remove inflectional and terminal suffixes - char* is = strstr(pl[i], MORPH_INFL_SFX); - if (is) - *is = '\0'; - char* ts = strstr(pl[i], MORPH_TERM_SFX); - while (ts) { - *ts = '_'; - ts = strstr(pl[i], MORPH_TERM_SFX); + size_t is = pl[i].find(MORPH_INFL_SFX); + if (is != std::string::npos) + pl[i].resize(is); + size_t ts = pl[i].find(MORPH_TERM_SFX); + while (ts != std::string::npos) { + pl[i][ts] = '_'; + ts = pl[i].find(MORPH_TERM_SFX); } - char* st = strstr(s, MORPH_STEM); + const char* st = strstr(s, MORPH_STEM); if (st) { copy_field(tok, st, MORPH_STEM); rv = pAMgr->lookup(tok.c_str()); while (rv) { std::string newpat(pl[i]); newpat.append(pattern); - char* sg = suggest_hentry_gen(rv, newpat.c_str()); - if (!sg) + std::string sg = suggest_hentry_gen(rv, newpat.c_str()); + if (sg.empty()) sg = suggest_hentry_gen(rv, pattern); - if (sg) { - char** gen; - int genl = line_tok(sg, &gen, MSEP_REC); - free(sg); - sg = NULL; - for (int j = 0; j < genl; j++) { + if (!sg.empty()) { + std::vector gen = line_tok(sg, MSEP_REC); + for (size_t j = 0; j < gen.size(); ++j) { result2.push_back(MSEP_REC); result2.append(result); - if (strstr(pl[i], MORPH_SURF_PFX)) { + if (pl[i].find(MORPH_SURF_PFX) != std::string::npos) { std::string field; copy_field(field, pl[i], MORPH_SURF_PFX); result2.append(field); } result2.append(gen[j]); } - freelist(&gen, genl); } rv = rv->next_homonym; } } } - freelist(&pl, pln); } if (!result2.empty() || !strstr(pattern, MORPH_DERI_SFX)) @@ -1878,7 +1721,7 @@ char* SuggestMgr::suggest_gen(char** desc, int n, const char* pattern) { mystrrep(newpattern, MORPH_DERI_SFX, MORPH_TERM_SFX); pattern = newpattern.c_str(); } - return (!result2.empty() ? mystrdup(result2.c_str()) : NULL); + return result2; } // generate an n-gram score comparing s1 and s2 @@ -1940,8 +1783,8 @@ int SuggestMgr::ngram(int n, for (int j = 1; j <= n; j++) { ns = 0; for (int i = 0; i <= (l1 - j); i++) { - std::string temp(s1.substr(i, j)); - if (t.find(temp) != std::string::npos) { + //t is haystack, s1[i..i+j) is needle + if (t.find(s1.c_str()+i, 0, j) != std::string::npos) { ns++; } else if (opt & NGRAM_WEIGHTED) { ns--; @@ -2054,7 +1897,7 @@ int SuggestMgr::commoncharacterpositions(const char* s1, } else { mkallsmall(t, csconv); } - for (i = 0; (*(s1 + i) != 0) && i < t.size(); i++) { + for (i = 0; i < t.size() && (*(s1 + i) != 0); ++i) { if (*(s1 + i) == t[i]) { num++; } else { diff --git a/extensions/spellcheck/hunspell/src/suggestmgr.hxx b/extensions/spellcheck/hunspell/src/suggestmgr.hxx index 675d98eb8f3e..ea6ad79e6f59 100644 --- a/extensions/spellcheck/hunspell/src/suggestmgr.hxx +++ b/extensions/spellcheck/hunspell/src/suggestmgr.hxx @@ -71,8 +71,8 @@ * SUCH DAMAGE. */ -#ifndef _SUGGESTMGR_HXX_ -#define _SUGGESTMGR_HXX_ +#ifndef SUGGESTMGR_HXX_ +#define SUGGESTMGR_HXX_ #define MAX_ROOTS 100 #define MAX_WORDS 100 @@ -91,8 +91,6 @@ #define NGRAM_LOWERING (1 << 2) #define NGRAM_WEIGHTED (1 << 3) -#include "hunvisapi.h" - #include "atypes.hxx" #include "affixmgr.hxx" #include "hashmgr.hxx" @@ -101,22 +99,22 @@ enum { LCS_UP, LCS_LEFT, LCS_UPLEFT }; -class LIBHUNSPELL_DLL_EXPORTED SuggestMgr { +class SuggestMgr { private: SuggestMgr(const SuggestMgr&); SuggestMgr& operator=(const SuggestMgr&); private: char* ckey; - int ckeyl; - w_char* ckey_utf; + size_t ckeyl; + std::vector ckey_utf; char* ctry; - int ctryl; - w_char* ctry_utf; + size_t ctryl; + std::vector ctry_utf; AffixMgr* pAMgr; - int maxSug; + unsigned int maxSug; struct cs_info* csconv; int utf8; int langnum; @@ -126,62 +124,53 @@ class LIBHUNSPELL_DLL_EXPORTED SuggestMgr { int complexprefixes; public: - SuggestMgr(const char* tryme, int maxn, AffixMgr* aptr); + SuggestMgr(const char* tryme, unsigned int maxn, AffixMgr* aptr); ~SuggestMgr(); - int suggest(char*** slst, const char* word, int nsug, int* onlycmpdsug); - int ngsuggest(char** wlst, const char* word, int ns, HashMgr** pHMgr, int md); - int suggest_auto(char*** slst, const char* word, int nsug); - int suggest_stems(char*** slst, const char* word, int nsug); - int suggest_pos_stems(char*** slst, const char* word, int nsug); + void suggest(std::vector& slst, const char* word, int* onlycmpdsug); + void ngsuggest(std::vector& slst, const char* word, const std::vector& rHMgr); - char* suggest_morph(const char* word); - char* suggest_gen(char** pl, int pln, const char* pattern); - char* suggest_morph_for_spelling_error(const char* word); + std::string suggest_morph(const std::string& word); + std::string suggest_gen(const std::vector& pl, const std::string& pattern); private: - int testsug(char** wlst, - const char* candidate, - int wl, - int ns, - int cpdsuggest, - int* timer, - clock_t* timelimit); - int checkword(const char*, int, int, int*, clock_t*); + void testsug(std::vector& wlst, + const std::string& candidate, + int cpdsuggest, + int* timer, + clock_t* timelimit); + int checkword(const std::string& word, int, int*, clock_t*); int check_forbidden(const char*, int); - int capchars(char**, const char*, int, int); - int replchars(char**, const char*, int, int); - int doubletwochars(char**, const char*, int, int); - int forgotchar(char**, const char*, int, int); - int swapchar(char**, const char*, int, int); - int longswapchar(char**, const char*, int, int); - int movechar(char**, const char*, int, int); - int extrachar(char**, const char*, int, int); - int badcharkey(char**, const char*, int, int); - int badchar(char**, const char*, int, int); - int twowords(char**, const char*, int, int); - int fixstems(char**, const char*, int); + void capchars(std::vector&, const char*, int); + int replchars(std::vector&, const char*, int); + int doubletwochars(std::vector&, const char*, int); + int forgotchar(std::vector&, const char*, int); + int swapchar(std::vector&, const char*, int); + int longswapchar(std::vector&, const char*, int); + int movechar(std::vector&, const char*, int); + int extrachar(std::vector&, const char*, int); + int badcharkey(std::vector&, const char*, int); + int badchar(std::vector&, const char*, int); + int twowords(std::vector&, const char*, int); - int capchars_utf(char**, const w_char*, int wl, int, int); - int doubletwochars_utf(char**, const w_char*, int wl, int, int); - int forgotchar_utf(char**, const w_char*, int wl, int, int); - int extrachar_utf(char**, const w_char*, int wl, int, int); - int badcharkey_utf(char**, const w_char*, int wl, int, int); - int badchar_utf(char**, const w_char*, int wl, int, int); - int swapchar_utf(char**, const w_char*, int wl, int, int); - int longswapchar_utf(char**, const w_char*, int, int, int); - int movechar_utf(char**, const w_char*, int, int, int); + void capchars_utf(std::vector&, const w_char*, int wl, int); + int doubletwochars_utf(std::vector&, const w_char*, int wl, int); + int forgotchar_utf(std::vector&, const w_char*, int wl, int); + int extrachar_utf(std::vector&, const w_char*, int wl, int); + int badcharkey_utf(std::vector&, const w_char*, int wl, int); + int badchar_utf(std::vector&, const w_char*, int wl, int); + int swapchar_utf(std::vector&, const w_char*, int wl, int); + int longswapchar_utf(std::vector&, const w_char*, int, int); + int movechar_utf(std::vector&, const w_char*, int, int); - int mapchars(char**, const char*, int, int); + int mapchars(std::vector&, const char*, int); int map_related(const char*, std::string&, int, - char** wlst, - int, - int, - const mapentry*, + std::vector& wlst, int, + const std::vector&, int*, clock_t*); int ngram(int n, const std::string& s1, const std::string& s2, int opt); @@ -192,7 +181,7 @@ class LIBHUNSPELL_DLL_EXPORTED SuggestMgr { void lcs(const char* s, const char* s2, int* l1, int* l2, char** result); int lcslen(const char* s, const char* s2); int lcslen(const std::string& s, const std::string& s2); - char* suggest_hentry_gen(hentry* rv, const char* pattern); + std::string suggest_hentry_gen(hentry* rv, const char* pattern); }; #endif diff --git a/extensions/spellcheck/hunspell/src/w_char.hxx b/extensions/spellcheck/hunspell/src/w_char.hxx index 336c454f7969..c561ffc45fdc 100644 --- a/extensions/spellcheck/hunspell/src/w_char.hxx +++ b/extensions/spellcheck/hunspell/src/w_char.hxx @@ -38,8 +38,10 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef __WCHARHXX__ -#define __WCHARHXX__ +#ifndef W_CHAR_HXX_ +#define W_CHAR_HXX_ + +#include #ifndef GCC struct w_char { @@ -66,10 +68,8 @@ struct __attribute__((packed)) w_char { // two character arrays struct replentry { - char* pattern; - char* pattern2; - bool start; - bool end; + std::string pattern; + std::string outstrings[4]; // med, ini, fin, isol }; #endif diff --git a/extensions/spellcheck/src/moz.build b/extensions/spellcheck/src/moz.build index 8d6cef588a48..b36b1fad45a0 100644 --- a/extensions/spellcheck/src/moz.build +++ b/extensions/spellcheck/src/moz.build @@ -5,7 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. include('/ipc/chromium/chromium-config.mozbuild') -SOURCES += [ +UNIFIED_SOURCES += [ 'mozEnglishWordUtils.cpp', 'mozInlineSpellChecker.cpp', 'mozInlineSpellWordUtil.cpp', diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index 234e36e6a50c..c0a406593ac2 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -1608,6 +1608,11 @@ js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto, uint8_t* resul if (!GetGetterPure(cx, proto, NameToId(cx->names().flags), &flagsGetter)) return false; + if (!flagsGetter) { + *result = false; + return true; + } + if (!IsSelfHostedFunctionWithName(flagsGetter, cx->names().RegExpFlagsGetter)) { *result = false; return true; diff --git a/js/src/builtin/SelfHostingDefines.h b/js/src/builtin/SelfHostingDefines.h index b57c172691d5..b51be1ffcd05 100644 --- a/js/src/builtin/SelfHostingDefines.h +++ b/js/src/builtin/SelfHostingDefines.h @@ -100,4 +100,30 @@ #define MODULE_STATE_INSTANTIATED 2 #define MODULE_STATE_EVALUATED 3 +#define STRING_GENERICS_CHAR_AT 0 +#define STRING_GENERICS_CHAR_CODE_AT 1 +#define STRING_GENERICS_CONCAT 2 +#define STRING_GENERICS_ENDS_WITH 3 +#define STRING_GENERICS_INCLUDES 4 +#define STRING_GENERICS_INDEX_OF 5 +#define STRING_GENERICS_LAST_INDEX_OF 6 +#define STRING_GENERICS_LOCALE_COMPARE 7 +#define STRING_GENERICS_MATCH 8 +#define STRING_GENERICS_NORMALIZE 9 +#define STRING_GENERICS_REPLACE 10 +#define STRING_GENERICS_SEARCH 11 +#define STRING_GENERICS_SLICE 12 +#define STRING_GENERICS_SPLIT 13 +#define STRING_GENERICS_STARTS_WITH 14 +#define STRING_GENERICS_SUBSTR 15 +#define STRING_GENERICS_SUBSTRING 16 +#define STRING_GENERICS_TO_LOWER_CASE 17 +#define STRING_GENERICS_TO_LOCALE_LOWER_CASE 18 +#define STRING_GENERICS_TO_LOCALE_UPPER_CASE 19 +#define STRING_GENERICS_TO_UPPER_CASE 20 +#define STRING_GENERICS_TRIM 21 +#define STRING_GENERICS_TRIM_LEFT 22 +#define STRING_GENERICS_TRIM_RIGHT 23 +#define STRING_GENERICS_METHODS_LIMIT 24 + #endif diff --git a/js/src/builtin/String.js b/js/src/builtin/String.js index 4202d0de63cd..623d8da1adee 100644 --- a/js/src/builtin/String.js +++ b/js/src/builtin/String.js @@ -58,6 +58,7 @@ function String_match(regexp) { } function String_generic_match(thisValue, regexp) { + WarnDeprecatedStringMethod(STRING_GENERICS_MATCH, 'match'); if (thisValue === undefined) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.match'); return callFunction(String_match, thisValue, regexp); @@ -192,6 +193,7 @@ function String_replace(searchValue, replaceValue) { } function String_generic_replace(thisValue, searchValue, replaceValue) { + WarnDeprecatedStringMethod(STRING_GENERICS_REPLACE, 'replace'); if (thisValue === undefined) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.replace'); return callFunction(String_replace, thisValue, searchValue, replaceValue); @@ -247,6 +249,7 @@ function String_search(regexp) { } function String_generic_search(thisValue, regexp) { + WarnDeprecatedStringMethod(STRING_GENERICS_SEARCH, 'search'); if (thisValue === undefined) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.search'); return callFunction(String_search, thisValue, regexp); @@ -328,6 +331,7 @@ function String_split(separator, limit) { } function String_generic_split(thisValue, separator, limit) { + WarnDeprecatedStringMethod(STRING_GENERICS_SPLIT, 'split'); if (thisValue === undefined) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.split'); return callFunction(String_split, thisValue, separator, limit); @@ -372,6 +376,7 @@ function String_substring(start, end) { } function String_static_substring(string, start, end) { + WarnDeprecatedStringMethod(STRING_GENERICS_SUBSTRING, 'substring'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.substring'); return callFunction(String_substring, string, start, end); @@ -411,6 +416,7 @@ function String_substr(start, length) { } function String_static_substr(string, start, length) { + WarnDeprecatedStringMethod(STRING_GENERICS_SUBSTR, 'substr'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.substr'); return callFunction(String_substr, string, start, length); @@ -448,6 +454,7 @@ function String_slice(start, end) { } function String_static_slice(string, start, end) { + WarnDeprecatedStringMethod(STRING_GENERICS_SLICE, 'slice'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.slice'); return callFunction(String_slice, string, start, end); @@ -655,6 +662,7 @@ function String_static_raw(callSite, ...substitutions) { * Spec: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String#String_generic_methods */ function String_static_localeCompare(str1, str2) { + WarnDeprecatedStringMethod(STRING_GENERICS_LOCALE_COMPARE, 'localeCompare'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "String.localeCompare"); var locales = arguments.length > 2 ? arguments[2] : undefined; @@ -767,30 +775,35 @@ function String_link(url) { } function String_static_toLowerCase(string) { + WarnDeprecatedStringMethod(STRING_GENERICS_TO_LOWER_CASE, 'toLowerCase'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.toLowerCase'); return callFunction(std_String_toLowerCase, string); } function String_static_toUpperCase(string) { + WarnDeprecatedStringMethod(STRING_GENERICS_TO_UPPER_CASE, 'toUpperCase'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.toUpperCase'); return callFunction(std_String_toUpperCase, string); } function String_static_charAt(string, pos) { + WarnDeprecatedStringMethod(STRING_GENERICS_CHAR_AT, 'charAt'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.charAt'); return callFunction(std_String_charAt, string, pos); } function String_static_charCodeAt(string, pos) { + WarnDeprecatedStringMethod(STRING_GENERICS_CHAR_CODE_AT, 'charCodeAt'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.charCodeAt'); return callFunction(std_String_charCodeAt, string, pos); } function String_static_includes(string, searchString) { + WarnDeprecatedStringMethod(STRING_GENERICS_INCLUDES, 'includes'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.includes'); var position = arguments.length > 2 ? arguments[2] : undefined; @@ -798,6 +811,7 @@ function String_static_includes(string, searchString) { } function String_static_indexOf(string, searchString) { + WarnDeprecatedStringMethod(STRING_GENERICS_INDEX_OF, 'indexOf'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.indexOf'); var position = arguments.length > 2 ? arguments[2] : undefined; @@ -805,6 +819,7 @@ function String_static_indexOf(string, searchString) { } function String_static_lastIndexOf(string, searchString) { + WarnDeprecatedStringMethod(STRING_GENERICS_LAST_INDEX_OF, 'lastIndexOf'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.lastIndexOf'); var position = arguments.length > 2 ? arguments[2] : undefined; @@ -812,6 +827,7 @@ function String_static_lastIndexOf(string, searchString) { } function String_static_startsWith(string, searchString) { + WarnDeprecatedStringMethod(STRING_GENERICS_STARTS_WITH, 'startsWith'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.startsWith'); var position = arguments.length > 2 ? arguments[2] : undefined; @@ -819,6 +835,7 @@ function String_static_startsWith(string, searchString) { } function String_static_endsWith(string, searchString) { + WarnDeprecatedStringMethod(STRING_GENERICS_ENDS_WITH, 'endsWith'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.endsWith'); var endPosition = arguments.length > 2 ? arguments[2] : undefined; @@ -826,30 +843,35 @@ function String_static_endsWith(string, searchString) { } function String_static_trim(string) { + WarnDeprecatedStringMethod(STRING_GENERICS_TRIM, 'trim'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trim'); return callFunction(std_String_trim, string); } function String_static_trimLeft(string) { + WarnDeprecatedStringMethod(STRING_GENERICS_TRIM_LEFT, 'trimLeft'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimLeft'); return callFunction(std_String_trimLeft, string); } function String_static_trimRight(string) { + WarnDeprecatedStringMethod(STRING_GENERICS_TRIM_RIGHT, 'trimRight'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimRight'); return callFunction(std_String_trimRight, string); } function String_static_toLocaleLowerCase(string) { + WarnDeprecatedStringMethod(STRING_GENERICS_TO_LOCALE_LOWER_CASE, 'toLocaleLowerCase'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.toLocaleLowerCase'); return callFunction(std_String_toLocaleLowerCase, string); } function String_static_toLocaleUpperCase(string) { + WarnDeprecatedStringMethod(STRING_GENERICS_TO_LOCALE_UPPER_CASE, 'toLocaleUpperCase'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.toLocaleUpperCase'); return callFunction(std_String_toLocaleUpperCase, string); @@ -857,6 +879,7 @@ function String_static_toLocaleUpperCase(string) { #if EXPOSE_INTL_API function String_static_normalize(string) { + WarnDeprecatedStringMethod(STRING_GENERICS_NORMALIZE, "normalize"); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.normalize'); var form = arguments.length > 1 ? arguments[1] : undefined; @@ -865,6 +888,7 @@ function String_static_normalize(string) { #endif function String_static_concat(string, arg1) { + WarnDeprecatedStringMethod(STRING_GENERICS_CONCAT, 'concat'); if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.concat'); var args = callFunction(std_Array_slice, arguments, 1); diff --git a/js/src/jit/BaselineCacheIR.cpp b/js/src/jit/BaselineCacheIR.cpp index 8567a9f5f5f8..ad76b3a6e8ce 100644 --- a/js/src/jit/BaselineCacheIR.cpp +++ b/js/src/jit/BaselineCacheIR.cpp @@ -944,6 +944,17 @@ BaselineCacheIRCompiler::emitGuardIsSymbol() return true; } +bool +BaselineCacheIRCompiler::emitGuardIsInt32() +{ + ValueOperand input = allocator.useValueRegister(masm, reader.valOperandId()); + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + masm.branchTestInt32(Assembler::NotEqual, input, failure->label()); + return true; +} + bool BaselineCacheIRCompiler::emitGuardType() { @@ -1561,13 +1572,101 @@ bool BaselineCacheIRCompiler::emitLoadStringLengthResult() { Register str = allocator.useRegister(masm, reader.stringOperandId()); - AutoScratchRegister scratch(allocator, masm); - masm.loadStringLength(str, R0.scratchReg()); masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0); return true; } +bool +BaselineCacheIRCompiler::emitLoadStringCharResult() +{ + Register str = allocator.useRegister(masm, reader.stringOperandId()); + Register index = allocator.useRegister(masm, reader.int32OperandId()); + AutoScratchRegister scratch(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + masm.branchIfRope(str, failure->label()); + + // Bounds check, load string char. + masm.branch32(Assembler::BelowOrEqual, Address(str, JSString::offsetOfLength()), + index, failure->label()); + masm.loadStringChar(str, index, scratch); + + // Load StaticString for this char. + masm.branch32(Assembler::AboveOrEqual, scratch, Imm32(StaticStrings::UNIT_STATIC_LIMIT), + failure->label()); + masm.movePtr(ImmPtr(&cx_->staticStrings().unitStaticTable), R0.scratchReg()); + masm.loadPtr(BaseIndex(R0.scratchReg(), scratch, ScalePointer), R0.scratchReg()); + + masm.tagValue(JSVAL_TYPE_STRING, R0.scratchReg(), R0); + return true; +} + +bool +BaselineCacheIRCompiler::emitLoadFrameArgumentResult() +{ + Register index = allocator.useRegister(masm, reader.int32OperandId()); + AutoScratchRegister scratch(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + // Bounds check. + masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs()), scratch); + masm.branch32(Assembler::AboveOrEqual, index, scratch, failure->label()); + + // Load the argument. + masm.loadValue(BaseValueIndex(BaselineFrameReg, index, BaselineFrame::offsetOfArg(0)), R0); + return true; +} + +bool +BaselineCacheIRCompiler::emitLoadArgumentsObjectArgResult() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + Register index = allocator.useRegister(masm, reader.int32OperandId()); + AutoScratchRegister scratch(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + // Get initial length value. + masm.unboxInt32(Address(obj, ArgumentsObject::getInitialLengthSlotOffset()), scratch); + + // Ensure no overridden length/element. + masm.branchTest32(Assembler::NonZero, + scratch, + Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT | + ArgumentsObject::ELEMENT_OVERRIDDEN_BIT), + failure->label()); + + // Bounds check. + masm.rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratch); + masm.branch32(Assembler::AboveOrEqual, index, scratch, failure->label()); + + // Load ArgumentsData. + masm.loadPrivate(Address(obj, ArgumentsObject::getDataSlotOffset()), scratch); + + // Fail if we have a RareArgumentsData (elements were deleted). + masm.branchPtr(Assembler::NotEqual, + Address(scratch, offsetof(ArgumentsData, rareData)), + ImmWord(0), + failure->label()); + + // Guard the argument is not a FORWARD_TO_CALL_SLOT MagicValue. Note that + // the order here matters: we should only clobber R0 after emitting the last + // guard. + BaseValueIndex argValue(scratch, index, ArgumentsData::offsetOfArgs()); + masm.branchTestMagic(Assembler::Equal, argValue, failure->label()); + masm.loadValue(argValue, R0); + return true; +} + bool BaselineCacheIRCompiler::emitTypeMonitorResult() { diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 591fcb3c5d2c..8b0885a98274 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -865,18 +865,6 @@ TypedArrayGetElemStubExists(ICGetElem_Fallback* stub, HandleObject obj) return false; } -static bool -ArgumentsGetElemStubExists(ICGetElem_Fallback* stub, ICGetElem_Arguments::Which which) -{ - for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) { - if (!iter->isGetElem_Arguments()) - continue; - if (iter->toGetElem_Arguments()->which() == which) - return true; - } - return false; -} - static bool IsOptimizableElementPropertyName(JSContext* cx, HandleValue key, MutableHandleId idp) { @@ -951,64 +939,10 @@ static bool TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_Fallback* stub, HandleValue lhs, HandleValue rhs, HandleValue res, bool* attached) { - // Check for String[i] => Char accesses. - if (lhs.isString() && rhs.isInt32() && res.isString() && - !stub->hasStub(ICStub::GetElem_String)) - { - // NoSuchMethod handling doesn't apply to string targets. - - JitSpew(JitSpew_BaselineIC, " Generating GetElem(String[Int32]) stub"); - ICGetElem_String::Compiler compiler(cx); - ICStub* stringStub = compiler.getStub(compiler.getStubSpace(script)); - if (!stringStub) - return false; - - stub->addNewStub(stringStub); - *attached = true; - return true; - } - - if (lhs.isMagic(JS_OPTIMIZED_ARGUMENTS) && rhs.isInt32() && - !ArgumentsGetElemStubExists(stub, ICGetElem_Arguments::Magic)) - { - JitSpew(JitSpew_BaselineIC, " Generating GetElem(MagicArgs[Int32]) stub"); - ICGetElem_Arguments::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(), - ICGetElem_Arguments::Magic); - ICStub* argsStub = compiler.getStub(compiler.getStubSpace(script)); - if (!argsStub) - return false; - - stub->addNewStub(argsStub); - *attached = true; - return true; - } - - // Otherwise, GetElem is only optimized on objects. if (!lhs.isObject()) return true; RootedObject obj(cx, &lhs.toObject()); - // Check for ArgumentsObj[int] accesses - if (obj->is() && rhs.isInt32() && - !obj->as().hasOverriddenElement()) - { - ICGetElem_Arguments::Which which = ICGetElem_Arguments::Mapped; - if (obj->is()) - which = ICGetElem_Arguments::Unmapped; - if (!ArgumentsGetElemStubExists(stub, which)) { - JitSpew(JitSpew_BaselineIC, " Generating GetElem(ArgsObj[Int32]) stub"); - ICGetElem_Arguments::Compiler compiler( - cx, stub->fallbackMonitorStub()->firstMonitorStub(), which); - ICStub* argsStub = compiler.getStub(compiler.getStubSpace(script)); - if (!argsStub) - return false; - - stub->addNewStub(argsStub); - *attached = true; - return true; - } - } - // Check for NativeObject[int] dense accesses. if (IsNativeDenseElementAccess(obj, rhs)) { JitSpew(JitSpew_BaselineIC, " Generating GetElem(Native[Int32] dense) stub"); @@ -1192,56 +1126,6 @@ ICGetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm) return tailCallVM(DoGetElemFallbackInfo, masm); } -// -// GetElem_String -// - -bool -ICGetElem_String::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - masm.branchTestString(Assembler::NotEqual, R0, &failure); - masm.branchTestInt32(Assembler::NotEqual, R1, &failure); - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); - Register scratchReg = regs.takeAny(); - - // Unbox string in R0. - Register str = masm.extractString(R0, ExtractTemp0); - - // Check for non-linear strings. - masm.branchIfRope(str, &failure); - - // Unbox key. - Register key = masm.extractInt32(R1, ExtractTemp1); - - // Bounds check. - masm.branch32(Assembler::BelowOrEqual, Address(str, JSString::offsetOfLength()), - key, &failure); - - // Get char code. - masm.loadStringChar(str, key, scratchReg); - - // Check if char code >= UNIT_STATIC_LIMIT. - masm.branch32(Assembler::AboveOrEqual, scratchReg, Imm32(StaticStrings::UNIT_STATIC_LIMIT), - &failure); - - // Load static string. - masm.movePtr(ImmPtr(&cx->staticStrings().unitStaticTable), str); - masm.loadPtr(BaseIndex(str, scratchReg, ScalePointer), str); - - // Return. - masm.tagValue(JSVAL_TYPE_STRING, str, R0); - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - // // GetElem_Dense // @@ -1422,131 +1306,6 @@ ICGetElem_TypedArray::Compiler::generateStubCode(MacroAssembler& masm) return true; } -// -// GetElem_Arguments -// -bool -ICGetElem_Arguments::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - if (which_ == ICGetElem_Arguments::Magic) { - // Ensure that this is a magic arguments value. - masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure); - - // Ensure that frame has not loaded different arguments object since. - masm.branchTest32(Assembler::NonZero, - Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()), - Imm32(BaselineFrame::HAS_ARGS_OBJ), - &failure); - - // Ensure that index is an integer. - masm.branchTestInt32(Assembler::NotEqual, R1, &failure); - Register idx = masm.extractInt32(R1, ExtractTemp1); - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); - Register scratch = regs.takeAny(); - - // Load num actual arguments - Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs()); - masm.loadPtr(actualArgs, scratch); - - // Ensure idx < argc - masm.branch32(Assembler::AboveOrEqual, idx, scratch, &failure); - - // Load argval - masm.movePtr(BaselineFrameReg, scratch); - masm.addPtr(Imm32(BaselineFrame::offsetOfArg(0)), scratch); - BaseValueIndex element(scratch, idx); - masm.loadValue(element, R0); - - // Enter type monitor IC to type-check result. - EmitEnterTypeMonitorIC(masm); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; - } - - MOZ_ASSERT(which_ == ICGetElem_Arguments::Mapped || - which_ == ICGetElem_Arguments::Unmapped); - - const Class* clasp = (which_ == ICGetElem_Arguments::Mapped) - ? &MappedArgumentsObject::class_ - : &UnmappedArgumentsObject::class_; - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); - Register scratchReg = regs.takeAny(); - - // Guard on input being an arguments object. - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - Register objReg = masm.extractObject(R0, ExtractTemp0); - masm.branchTestObjClass(Assembler::NotEqual, objReg, scratchReg, clasp, &failure); - - // Guard on index being int32 - masm.branchTestInt32(Assembler::NotEqual, R1, &failure); - Register idxReg = masm.extractInt32(R1, ExtractTemp1); - - // Get initial ArgsObj length value. - masm.unboxInt32(Address(objReg, ArgumentsObject::getInitialLengthSlotOffset()), scratchReg); - - // Test if length or any element have been overridden. - masm.branchTest32(Assembler::NonZero, - scratchReg, - Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT | - ArgumentsObject::ELEMENT_OVERRIDDEN_BIT), - &failure); - - // Length has not been overridden, ensure that R1 is an integer and is <= length. - masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratchReg); - masm.branch32(Assembler::AboveOrEqual, idxReg, scratchReg, &failure); - - // Length check succeeded, now check the correct bit. We clobber potential type regs - // now. Inputs will have to be reconstructed if we fail after this point, but that's - // unlikely. - Label failureReconstructInputs; - regs = availableGeneralRegs(0); - regs.takeUnchecked(objReg); - regs.takeUnchecked(idxReg); - regs.take(scratchReg); - Register argData = regs.takeAny(); - - // Load ArgumentsData - masm.loadPrivate(Address(objReg, ArgumentsObject::getDataSlotOffset()), argData); - - // Fail if we have a RareArgumentsData (elements were deleted). - masm.branchPtr(Assembler::NotEqual, - Address(argData, offsetof(ArgumentsData, rareData)), - ImmWord(0), - &failureReconstructInputs); - - // Load the value. Use scratchReg to form a ValueOperand to load into. - masm.addPtr(Imm32(ArgumentsData::offsetOfArgs()), argData); - regs.add(scratchReg); - ValueOperand tempVal = regs.takeAnyValue(); - masm.loadValue(BaseValueIndex(argData, idxReg), tempVal); - - // Makesure that this is not a FORWARD_TO_CALL_SLOT magic value. - masm.branchTestMagic(Assembler::Equal, tempVal, &failureReconstructInputs); - - // Copy value from temp to R0. - masm.moveValue(tempVal, R0); - - // Type-check result - EmitEnterTypeMonitorIC(masm); - - // Failed, but inputs are deconstructed into object and int, and need to be - // reconstructed into values. - masm.bind(&failureReconstructInputs); - masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0); - masm.tagValue(JSVAL_TYPE_INT32, idxReg, R1); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - // // SetElem_Fallback // @@ -7420,13 +7179,6 @@ ICGetElem_TypedArray::ICGetElem_TypedArray(JitCode* stubCode, Shape* shape, Scal MOZ_ASSERT(extra_ == type); } -/* static */ ICGetElem_Arguments* -ICGetElem_Arguments::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetElem_Arguments& other) -{ - return New(cx, space, other.jitCode(), firstMonitorStub, other.which()); -} - ICSetElem_DenseOrUnboxedArray::ICSetElem_DenseOrUnboxedArray(JitCode* stubCode, Shape* shape, ObjectGroup* group) : ICUpdatedStub(SetElem_DenseOrUnboxedArray, stubCode), shape_(shape), diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index ed019e376419..b3119e2bc920 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -419,29 +419,6 @@ class ICGetElem_Fallback : public ICMonitoredFallbackStub }; }; -class ICGetElem_String : public ICStub -{ - friend class ICStubSpace; - - explicit ICGetElem_String(JitCode* stubCode) - : ICStub(ICStub::GetElem_String, stubCode) {} - - public: - // Compiler for this stub kind. - class Compiler : public ICStubCompiler { - protected: - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm); - - public: - explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::GetElem_String, Engine::Baseline) {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode()); - } - }; -}; - class ICGetElem_Dense : public ICMonitoredStub { friend class ICStubSpace; @@ -583,53 +560,6 @@ class ICGetElem_TypedArray : public ICStub }; }; -class ICGetElem_Arguments : public ICMonitoredStub -{ - friend class ICStubSpace; - public: - enum Which { Mapped, Unmapped, Magic }; - - private: - ICGetElem_Arguments(JitCode* stubCode, ICStub* firstMonitorStub, Which which) - : ICMonitoredStub(ICStub::GetElem_Arguments, stubCode, firstMonitorStub) - { - extra_ = static_cast(which); - } - - public: - static ICGetElem_Arguments* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetElem_Arguments& other); - - Which which() const { - return static_cast(extra_); - } - - class Compiler : public ICStubCompiler { - ICStub* firstMonitorStub_; - Which which_; - - protected: - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm); - - virtual int32_t getKey() const { - return static_cast(engine_) | - (static_cast(kind) << 1) | - (static_cast(which_) << 17); - } - - public: - Compiler(JSContext* cx, ICStub* firstMonitorStub, Which which) - : ICStubCompiler(cx, ICStub::GetElem_Arguments, Engine::Baseline), - firstMonitorStub_(firstMonitorStub), - which_(which) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode(), firstMonitorStub_, which_); - } - }; -}; - // SetElem // JSOP_SETELEM // JSOP_INITELEM diff --git a/js/src/jit/BaselineICList.h b/js/src/jit/BaselineICList.h index 42c677de07bd..44685e245160 100644 --- a/js/src/jit/BaselineICList.h +++ b/js/src/jit/BaselineICList.h @@ -49,11 +49,9 @@ namespace jit { _(Call_IsSuspendedStarGenerator) \ \ _(GetElem_Fallback) \ - _(GetElem_String) \ _(GetElem_Dense) \ _(GetElem_UnboxedArray) \ _(GetElem_TypedArray) \ - _(GetElem_Arguments) \ \ _(SetElem_Fallback) \ _(SetElem_DenseOrUnboxedArray) \ diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index dcb56cc27506..fdc92df0bc34 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -1016,11 +1016,6 @@ BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc) case ICStub::GetProp_Generic: return MIRType::Value; - case ICStub::GetElem_Arguments: - // Either an object or magic arguments. - return MIRType::Value; - - case ICStub::GetElem_String: case ICStub::GetElem_Dense: case ICStub::GetElem_TypedArray: case ICStub::GetElem_UnboxedArray: diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index dc7d486adaa6..d5b26f2299ab 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -85,6 +85,13 @@ GetPropIRGenerator::tryAttachStub() return true; if (tryAttachProxy(obj, objId, id)) return true; + return false; + } + if (idVal_.isInt32()) { + ValOperandId indexId = getElemKeyValueId(); + if (tryAttachArgumentsObjectArg(obj, objId, indexId)) + return true; + return false; } return false; } @@ -94,8 +101,18 @@ GetPropIRGenerator::tryAttachStub() return true; if (tryAttachStringLength(valId, id)) return true; - if (tryAttachMagicArguments(valId, id)) + if (tryAttachMagicArgumentsName(valId, id)) return true; + return false; + } + + if (idVal_.isInt32()) { + ValOperandId indexId = getElemKeyValueId(); + if (tryAttachStringChar(valId, indexId)) + return true; + if (tryAttachMagicArgument(valId, indexId)) + return true; + return false; } return false; @@ -775,7 +792,31 @@ GetPropIRGenerator::tryAttachStringLength(ValOperandId valId, HandleId id) } bool -GetPropIRGenerator::tryAttachMagicArguments(ValOperandId valId, HandleId id) +GetPropIRGenerator::tryAttachStringChar(ValOperandId valId, ValOperandId indexId) +{ + MOZ_ASSERT(idVal_.isInt32()); + + if (!val_.isString()) + return false; + + JSString* str = val_.toString(); + int32_t index = idVal_.toInt32(); + if (size_t(index) >= str->length() || + !str->isLinear() || + str->asLinear().latin1OrTwoByteChar(index) >= StaticStrings::UNIT_STATIC_LIMIT) + { + return false; + } + + StringOperandId strId = writer.guardIsString(valId); + Int32OperandId int32IndexId = writer.guardIsInt32(indexId); + writer.loadStringCharResult(strId, int32IndexId); + writer.returnFromIC(); + return true; +} + +bool +GetPropIRGenerator::tryAttachMagicArgumentsName(ValOperandId valId, HandleId id) { if (!val_.isMagic(JS_OPTIMIZED_ARGUMENTS)) return false; @@ -799,6 +840,45 @@ GetPropIRGenerator::tryAttachMagicArguments(ValOperandId valId, HandleId id) return true; } +bool +GetPropIRGenerator::tryAttachMagicArgument(ValOperandId valId, ValOperandId indexId) +{ + MOZ_ASSERT(idVal_.isInt32()); + + if (!val_.isMagic(JS_OPTIMIZED_ARGUMENTS)) + return false; + + writer.guardMagicValue(valId, JS_OPTIMIZED_ARGUMENTS); + writer.guardFrameHasNoArgumentsObject(); + + Int32OperandId int32IndexId = writer.guardIsInt32(indexId); + writer.loadFrameArgumentResult(int32IndexId); + writer.typeMonitorResult(); + return true; +} + +bool +GetPropIRGenerator::tryAttachArgumentsObjectArg(HandleObject obj, ObjOperandId objId, + ValOperandId indexId) +{ + MOZ_ASSERT(idVal_.isInt32()); + + if (!obj->is() || obj->as().hasOverriddenElement()) + return false; + + if (obj->is()) { + writer.guardClass(objId, GuardClassKind::MappedArguments); + } else { + MOZ_ASSERT(obj->is()); + writer.guardClass(objId, GuardClassKind::UnmappedArguments); + } + + Int32OperandId int32IndexId = writer.guardIsInt32(indexId); + writer.loadArgumentsObjectArgResult(objId, int32IndexId); + writer.typeMonitorResult(); + return true; +} + void GetPropIRGenerator::maybeEmitIdGuard(jsid id) { diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index 8e054452c83f..1fdca0ad79d1 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -93,6 +93,13 @@ class SymbolOperandId : public OperandId explicit SymbolOperandId(uint16_t id) : OperandId(id) {} }; +class Int32OperandId : public OperandId +{ + public: + Int32OperandId() = default; + explicit Int32OperandId(uint16_t id) : OperandId(id) {} +}; + class TypedOperandId : public OperandId { JSValueType type_; @@ -107,6 +114,9 @@ class TypedOperandId : public OperandId MOZ_IMPLICIT TypedOperandId(SymbolOperandId id) : OperandId(id.id()), type_(JSVAL_TYPE_SYMBOL) {} + MOZ_IMPLICIT TypedOperandId(Int32OperandId id) + : OperandId(id.id()), type_(JSVAL_TYPE_INT32) + {} JSValueType type() const { return type_; } }; @@ -121,6 +131,7 @@ enum class CacheKind : uint8_t _(GuardIsObject) \ _(GuardIsString) \ _(GuardIsSymbol) \ + _(GuardIsInt32) \ _(GuardType) \ _(GuardShape) \ _(GuardGroup) \ @@ -150,10 +161,13 @@ enum class CacheKind : uint8_t _(LoadTypedObjectResult) \ _(LoadInt32ArrayLengthResult) \ _(LoadUnboxedArrayLengthResult) \ + _(LoadArgumentsObjectArgResult) \ _(LoadArgumentsObjectLengthResult) \ + _(LoadStringCharResult) \ _(LoadStringLengthResult) \ _(LoadFrameCalleeResult) \ _(LoadFrameNumActualArgsResult) \ + _(LoadFrameArgumentResult) \ _(CallScriptedGetterResult) \ _(CallNativeGetterResult) \ _(CallProxyGetResult) \ @@ -368,6 +382,10 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter writeOpWithOperandId(CacheOp::GuardIsSymbol, val); return SymbolOperandId(val.id()); } + Int32OperandId guardIsInt32(ValOperandId val) { + writeOpWithOperandId(CacheOp::GuardIsInt32, val); + return Int32OperandId(val.id()); + } void guardType(ValOperandId val, JSValueType type) { writeOpWithOperandId(CacheOp::GuardType, val); static_assert(sizeof(type) == sizeof(uint8_t), "JSValueType should fit in a byte"); @@ -425,6 +443,9 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter void loadFrameNumActualArgsResult() { writeOp(CacheOp::LoadFrameNumActualArgsResult); } + void loadFrameArgumentResult(Int32OperandId index) { + writeOpWithOperandId(CacheOp::LoadFrameArgumentResult, index); + } void guardNoUnboxedExpando(ObjOperandId obj) { writeOpWithOperandId(CacheOp::GuardNoUnboxedExpando, obj); } @@ -501,12 +522,20 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter void loadUnboxedArrayLengthResult(ObjOperandId obj) { writeOpWithOperandId(CacheOp::LoadUnboxedArrayLengthResult, obj); } + void loadArgumentsObjectArgResult(ObjOperandId obj, Int32OperandId index) { + writeOpWithOperandId(CacheOp::LoadArgumentsObjectArgResult, obj); + writeOperandId(index); + } void loadArgumentsObjectLengthResult(ObjOperandId obj) { writeOpWithOperandId(CacheOp::LoadArgumentsObjectLengthResult, obj); } void loadStringLengthResult(StringOperandId str) { writeOpWithOperandId(CacheOp::LoadStringLengthResult, str); } + void loadStringCharResult(StringOperandId str, Int32OperandId index) { + writeOpWithOperandId(CacheOp::LoadStringCharResult, str); + writeOperandId(index); + } void callScriptedGetterResult(ObjOperandId obj, JSFunction* getter) { writeOpWithOperandId(CacheOp::CallScriptedGetterResult, obj); addStubField(uintptr_t(getter), StubField::Type::JSObject); @@ -561,6 +590,7 @@ class MOZ_RAII CacheIRReader ObjOperandId objOperandId() { return ObjOperandId(buffer_.readByte()); } StringOperandId stringOperandId() { return StringOperandId(buffer_.readByte()); } SymbolOperandId symbolOperandId() { return SymbolOperandId(buffer_.readByte()); } + Int32OperandId int32OperandId() { return Int32OperandId(buffer_.readByte()); } uint32_t stubOffset() { return buffer_.readByte() * sizeof(uintptr_t); } GuardClassKind guardClassKind() { return GuardClassKind(buffer_.readByte()); } @@ -623,8 +653,12 @@ class MOZ_RAII GetPropIRGenerator bool tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id); bool tryAttachPrimitive(ValOperandId valId, HandleId id); + bool tryAttachStringChar(ValOperandId valId, ValOperandId indexId); bool tryAttachStringLength(ValOperandId valId, HandleId id); - bool tryAttachMagicArguments(ValOperandId valId, HandleId id); + bool tryAttachMagicArgumentsName(ValOperandId valId, HandleId id); + + bool tryAttachMagicArgument(ValOperandId valId, ValOperandId indexId); + bool tryAttachArgumentsObjectArg(HandleObject obj, ObjOperandId objId, ValOperandId indexId); ValOperandId getElemKeyValueId() const { MOZ_ASSERT(cacheKind_ == CacheKind::GetElem); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 294fc3ba6995..a73be45cff30 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -1505,6 +1505,11 @@ IonBuilder::visitBlock(const CFGBlock* cfgblock, MBasicBlock* mblock) if (mblock->pc() && script()->hasScriptCounts()) mblock->setHitCount(script()->getHitCount(mblock->pc())); + // Optimization to move a predecessor that only has this block as successor + // just before this block. + if (mblock->numPredecessors() == 1 && mblock->getPredecessor(0)->numSuccessors() == 1) + graph().moveBlockToEnd(mblock->getPredecessor(0)); + if (!setCurrentAndSpecializePhis(mblock)) return false; graph().addBlock(mblock); @@ -2846,6 +2851,9 @@ IonBuilder::visitTest(CFGTest* test) return false; filterBlock->end(MGoto::New(alloc(), ifFalse)); + if (filterBlock->pc() && script()->hasScriptCounts()) + filterBlock->setHitCount(script()->getHitCount(filterBlock->pc())); + blockWorklist[test->falseBranch()->id()] = ifFalse; current = nullptr; @@ -3079,6 +3087,9 @@ IonBuilder::visitTableSwitch(CFGTableSwitch* cfgIns) } graph().addBlock(caseBlock); + if (caseBlock->pc() && script()->hasScriptCounts()) + caseBlock->setHitCount(script()->getHitCount(caseBlock->pc())); + MBasicBlock* merge = newBlock(caseBlock, cfgblock->startPc()); if (!merge) return false; diff --git a/js/src/js.msg b/js/src/js.msg index 1e3abcc5df1d..61eb9fd2982d 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -133,6 +133,7 @@ MSG_DEF(JSMSG_INVALID_NORMALIZE_FORM, 0, JSEXN_RANGEERR, "form must be one of ' MSG_DEF(JSMSG_NEGATIVE_REPETITION_COUNT, 0, JSEXN_RANGEERR, "repeat count must be non-negative") MSG_DEF(JSMSG_NOT_A_CODEPOINT, 1, JSEXN_RANGEERR, "{0} is not a valid code point") MSG_DEF(JSMSG_RESULTING_STRING_TOO_LARGE, 0, JSEXN_RANGEERR, "repeat count must be less than infinity and not overflow maximum string size") +MSG_DEF(JSMSG_DEPRECATED_STRING_METHOD, 2, JSEXN_WARN, "String.{0} is deprecated; use String.prototype.{1} instead") // Number MSG_DEF(JSMSG_BAD_RADIX, 0, JSEXN_RANGEERR, "radix must be an integer at least 2 and no greater than 36") diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 310f9029cf58..3f4ca8999b1d 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -55,6 +55,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = marked(true), warnedAboutExprClosure(false), warnedAboutForEach(false), + warnedAboutStringGenericsMethods(0), #ifdef DEBUG firedOnNewGlobalObject(false), #endif diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index b6da7bf36f5c..e65c5e3c7aa4 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -361,6 +361,7 @@ struct JSCompartment bool marked; bool warnedAboutExprClosure; bool warnedAboutForEach; + uint32_t warnedAboutStringGenericsMethods; #ifdef DEBUG bool firedOnNewGlobalObject; @@ -882,6 +883,7 @@ struct JSCompartment // NO LONGER USING 8 // NO LONGER USING 9 DeprecatedBlockScopeFunRedecl = 10, + DeprecatedStringGenerics = 11, DeprecatedLanguageExtensionCount }; diff --git a/js/src/tests/ecma_6/RegExp/split-deleted-flags.js b/js/src/tests/ecma_6/RegExp/split-deleted-flags.js new file mode 100644 index 000000000000..d2ccc852d18b --- /dev/null +++ b/js/src/tests/ecma_6/RegExp/split-deleted-flags.js @@ -0,0 +1,11 @@ +var BUGNUMBER = 1322319; +var summary = "RegExp.prototype.split should throw if RegRxp.prototype.flags is deleted." + +print(BUGNUMBER + ": " + summary); + +delete RegExp.prototype.flags; + +assertThrowsInstanceOf(() => "aaaaa".split(/a/), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/js1_6/String/generics-deprecated.js b/js/src/tests/js1_6/String/generics-deprecated.js new file mode 100644 index 000000000000..f29b81de3c8d --- /dev/null +++ b/js/src/tests/js1_6/String/generics-deprecated.js @@ -0,0 +1,63 @@ +// |reftest| skip-if(!xulRuntime.shell) +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Warn once for each String generics method. + +var methods = { + charAt: ["", 0], + charCodeAt: ["", 0], + concat: ["", ""], + endsWith: ["", ""], + includes: ["", ""], + indexOf: ["", ""], + lastIndexOf: ["", ""], + localeCompare: ["", ""], + match: ["", ""], + normalize: [""], + replace: ["", ""], + search: ["", ""], + slice: ["", 0], + split: ["", ""], + startsWith: ["", ""], + substr: ["", 0], + substring: ["", 0], + toLowerCase: [""], + toLocaleLowerCase: [""], + toLocaleUpperCase: [""], + toUpperCase: [""], + trim: [""], + trimLeft: [""], + trimRight: [""] +}; + +for (var name in methods) { + var args = methods[name]; + + // String.normalize not available on every platform. + if (name === "normalize" && !(name in String.prototype)) + continue; + + enableLastWarning(); + + String[name].apply(null, args); + + var warning = getLastWarning(); + assertEq(warning !== null, true, "warning should be emitted for " + name); + assertEq(warning.name, "Warning"); + assertEq(warning.message.indexOf(name) !== -1, true, + "warning should mention " + name); + + clearLastWarning(); + + String[name].apply(null, args); + + warning = getLastWarning(); + assertEq(warning, null, "warning shouldn't be emitted for 2nd call to " + name); + + disableLastWarning(); +} + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index b15bda1d2a90..15615fd225c6 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -1904,6 +1904,46 @@ intrinsic_AddContentTelemetry(JSContext* cx, unsigned argc, Value* vp) return true; } +static bool +intrinsic_WarnDeprecatedStringMethod(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(args[0].isInt32()); + MOZ_ASSERT(args[1].isString()); + + uint32_t id = uint32_t(args[0].toInt32()); + MOZ_ASSERT(id < STRING_GENERICS_METHODS_LIMIT); + + NonBuiltinScriptFrameIter iter(cx); + if (!iter.done()) { + const char* filename = iter.filename(); + iter.compartment()->addTelemetry(filename, JSCompartment::DeprecatedStringGenerics); + } + + uint32_t mask = (1 << id); + if (!(cx->compartment()->warnedAboutStringGenericsMethods & mask)) { + JSFlatString* name = args[1].toString()->ensureFlat(cx); + if (!name) + return false; + + AutoStableStringChars stableChars(cx); + if (!stableChars.initTwoByte(cx, name)) + return false; + const char16_t* nameChars = stableChars.twoByteRange().begin().get(); + + if (!JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, GetErrorMessage, nullptr, + JSMSG_DEPRECATED_STRING_METHOD, nameChars, nameChars)) + { + return false; + } + cx->compartment()->warnedAboutStringGenericsMethods |= mask; + } + + args.rval().setUndefined(); + return true; +} + static bool intrinsic_ConstructFunction(JSContext* cx, unsigned argc, Value* vp) { @@ -2513,6 +2553,7 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_INLINABLE_FN("StringSplitString", intrinsic_StringSplitString, 2, 0, IntrinsicStringSplitString), JS_FN("StringSplitStringLimit", intrinsic_StringSplitStringLimit, 3, 0), + JS_FN("WarnDeprecatedStringMethod", intrinsic_WarnDeprecatedStringMethod, 2, 0), // See builtin/RegExp.h for descriptions of the regexp_* functions. JS_FN("regexp_exec_no_statics", regexp_exec_no_statics, 2,0), diff --git a/js/src/vm/TraceLogging.cpp b/js/src/vm/TraceLogging.cpp index 77c35aa2c9a0..b1991066b0cd 100644 --- a/js/src/vm/TraceLogging.cpp +++ b/js/src/vm/TraceLogging.cpp @@ -278,13 +278,14 @@ TraceLoggerThread::disable(bool force, const char* error) } const char* -TraceLoggerThread::eventText(uint32_t id) +TraceLoggerThread::maybeEventText(uint32_t id) { if (id < TraceLogger_Last) return TLTextIdString(static_cast(id)); TextIdHashMap::Ptr p = textIdPayloads.lookup(id); - MOZ_ASSERT(p); + if (!p) + return nullptr; return p->value()->string(); } @@ -570,7 +571,10 @@ TraceLoggerThread::stopEvent(uint32_t id) MOZ_ASSERT(prev >= TraceLogger_Last); } else if (id >= TraceLogger_Last) { MOZ_ASSERT(prev >= TraceLogger_Last); - MOZ_ASSERT_IF(prev != id, strcmp(eventText(id), eventText(prev)) == 0); + if (prev != id) { + // Ignore if the text has been flushed already. + MOZ_ASSERT_IF(maybeEventText(prev), strcmp(eventText(id), eventText(prev)) == 0); + } } else { MOZ_ASSERT(id == prev); } diff --git a/js/src/vm/TraceLogging.h b/js/src/vm/TraceLogging.h index 94aec77cb4bc..4a258cf3882c 100644 --- a/js/src/vm/TraceLogging.h +++ b/js/src/vm/TraceLogging.h @@ -260,7 +260,14 @@ class TraceLoggerThread return true; } - const char* eventText(uint32_t id); + private: + const char* maybeEventText(uint32_t id); + public: + const char* eventText(uint32_t id) { + const char* text = maybeEventText(id); + MOZ_ASSERT(text); + return text; + }; bool textIdIsScriptEvent(uint32_t id); // The createTextId functions map a unique input to a logger ID. diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 4f61e612640f..c77683c9fcd3 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -3173,6 +3173,8 @@ nsLayoutUtils::GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect, builder.SetDescendIntoSubdocuments(false); } + builder.SetHitTestShouldStopAtFirstOpaque(aFlags & ONLY_VISIBLE); + builder.EnterPresShell(aFrame); aFrame->BuildDisplayListForStackingContext(&builder, aRect, &list); builder.LeavePresShell(aFrame, nullptr); @@ -3188,7 +3190,6 @@ nsLayoutUtils::GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect, #endif nsDisplayItem::HitTestState hitTestState; - builder.SetHitTestShouldStopAtFirstOpaque(aFlags & ONLY_VISIBLE); list.HitTest(&builder, aRect, &hitTestState, &aOutFrames); list.DeleteAll(); return NS_OK; diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 40385d255e97..39a566d23384 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -825,7 +825,7 @@ public: * frames under the area of a rectangle that receives a mouse event, * or nullptr if there is no such frame. * @param aRect the rect, relative to the frame origin - * @param aOutFrames an array to add all the frames found + * @param aOutFrames an array to append all the frames found * @param aFlags some combination of FrameForPointFlags */ static nsresult GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect, diff --git a/layout/forms/nsHTMLButtonControlFrame.cpp b/layout/forms/nsHTMLButtonControlFrame.cpp index 37d9a5cbbabd..0f3da4a9634d 100644 --- a/layout/forms/nsHTMLButtonControlFrame.cpp +++ b/layout/forms/nsHTMLButtonControlFrame.cpp @@ -119,7 +119,7 @@ nsHTMLButtonControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, nsDisplayListCollection set; // Do not allow the child subtree to receive events. - if (!isForEventDelivery) { + if (!isForEventDelivery || aBuilder->HitTestShouldStopAtFirstOpaque()) { DisplayListClipState::AutoSaveRestore clipState(aBuilder); if (ShouldClipPaintingToBorderBox()) { diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 708439982336..fdf7ee62421a 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -629,7 +629,7 @@ load text-overflow-form-elements.html load text-overflow-iframe.html asserts-if(Android,2-4) asserts-if(!Android,4) load 1225005.html # bug 682647 and bug 448083 load 1233191.html -asserts(3) load 1271765.html # bug 1319318 +load 1271765.html asserts(2) load 1272983-1.html # bug 586628 asserts(2) load 1272983-2.html # bug 586628 load 1275059.html diff --git a/layout/generic/nsVideoFrame.cpp b/layout/generic/nsVideoFrame.cpp index 35f2b16fe76e..90059d03782c 100644 --- a/layout/generic/nsVideoFrame.cpp +++ b/layout/generic/nsVideoFrame.cpp @@ -377,7 +377,9 @@ nsVideoFrame::Reflow(nsPresContext* aPresContext, borderPadding.left, borderPadding.top, 0, aStatus); if (child->GetContent() == mVideoControls && isBSizeShrinkWrapping) { - contentBoxBSize = kidDesiredSize.BSize(myWM); + // Resolve our own BSize based on the controls' size in the same axis. + contentBoxBSize = myWM.IsOrthogonalTo(wm) ? + kidDesiredSize.ISize(wm) : kidDesiredSize.BSize(wm); } FinishReflowChild(child, aPresContext, diff --git a/layout/inspector/inDOMUtils.cpp b/layout/inspector/inDOMUtils.cpp index 3b7abef0b58d..a5aa77a9f9ba 100644 --- a/layout/inspector/inDOMUtils.cpp +++ b/layout/inspector/inDOMUtils.cpp @@ -48,7 +48,8 @@ #include "nsCSSProps.h" #include "nsCSSValue.h" #include "nsColor.h" -#include "nsStyleSet.h" +#include "mozilla/StyleSetHandle.h" +#include "mozilla/StyleSetHandleInlines.h" #include "nsStyleUtil.h" #include "nsQueryObject.h" @@ -77,7 +78,7 @@ inDOMUtils::GetAllStyleSheets(nsIDOMDocument *aDocument, uint32_t *aLength, { NS_ENSURE_ARG_POINTER(aDocument); - nsTArray> sheets; + nsTArray> sheets; nsCOMPtr document = do_QueryInterface(aDocument); MOZ_ASSERT(document); @@ -85,15 +86,8 @@ inDOMUtils::GetAllStyleSheets(nsIDOMDocument *aDocument, uint32_t *aLength, // Get the agent, then user and finally xbl sheets in the style set. nsIPresShell* presShell = document->GetShell(); - if (presShell && presShell->StyleSet()->IsServo()) { - // XXXheycam ServoStyleSets don't have the ability to expose their - // sheets in a script-accessible way yet. - NS_ERROR("stylo: ServoStyleSets cannot expose their sheets to script yet"); - return NS_ERROR_FAILURE; - } - if (presShell) { - nsStyleSet* styleSet = presShell->StyleSet()->AsGecko(); + StyleSetHandle styleSet = presShell->StyleSet(); SheetType sheetType = SheetType::Agent; for (int32_t i = 0; i < styleSet->SheetCount(sheetType); i++) { sheets.AppendElement(styleSet->StyleSheetAt(sheetType, i)); @@ -102,24 +96,26 @@ inDOMUtils::GetAllStyleSheets(nsIDOMDocument *aDocument, uint32_t *aLength, for (int32_t i = 0; i < styleSet->SheetCount(sheetType); i++) { sheets.AppendElement(styleSet->StyleSheetAt(sheetType, i)); } - AutoTArray xblSheetArray; - styleSet->AppendAllXBLStyleSheets(xblSheetArray); + if (styleSet->IsGecko()) { + AutoTArray xblSheetArray; + styleSet->AsGecko()->AppendAllXBLStyleSheets(xblSheetArray); - // The XBL stylesheet array will quite often be full of duplicates. Cope: - nsTHashtable> sheetSet; - for (CSSStyleSheet* sheet : xblSheetArray) { - if (!sheetSet.Contains(sheet)) { - sheetSet.PutEntry(sheet); - sheets.AppendElement(sheet); + // The XBL stylesheet array will quite often be full of duplicates. Cope: + nsTHashtable> sheetSet; + for (CSSStyleSheet* sheet : xblSheetArray) { + if (!sheetSet.Contains(sheet)) { + sheetSet.PutEntry(sheet); + sheets.AppendElement(sheet); + } } + } else { + NS_WARNING("stylo: XBL style sheets not supported yet"); } } // Get the document sheets. for (int32_t i = 0; i < document->GetNumberOfStyleSheets(); i++) { - // XXXheycam ServoStyleSets don't have the ability to expose their - // sheets in a script-accessible way yet. - sheets.AppendElement(document->GetStyleSheetAt(i)->AsGecko()); + sheets.AppendElement(document->GetStyleSheetAt(i)); } nsISupports** ret = static_cast(moz_xmalloc(sheets.Length() * diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index bc2fae19e204..b86b75ed5738 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -2965,6 +2965,7 @@ var gCSSProperties = { "ruby-base-container", "ruby-text", "ruby-text-container", + "contents", "none" ], invalid_values: [] @@ -6725,8 +6726,6 @@ if (IsCSSPropertyPrefEnabled("layout.css.grid.enabled")) { }; } -gCSSProperties["display"].other_values.push("contents"); - if (IsCSSPropertyPrefEnabled("layout.css.contain.enabled")) { gCSSProperties["contain"] = { domProp: "contain", diff --git a/media/webrtc/signaling/test/mediaconduit_unittests.cpp b/media/webrtc/signaling/gtest/mediaconduit_unittests.cpp similarity index 81% rename from media/webrtc/signaling/test/mediaconduit_unittests.cpp rename to media/webrtc/signaling/gtest/mediaconduit_unittests.cpp index c9a3d21f3731..f85b73bb696a 100644 --- a/media/webrtc/signaling/test/mediaconduit_unittests.cpp +++ b/media/webrtc/signaling/gtest/mediaconduit_unittests.cpp @@ -14,27 +14,14 @@ using namespace std; #include "mozilla/SyncRunnable.h" #include "mozilla/UniquePtr.h" #include -#include "GmpVideoCodec.h" #include "nsIEventTarget.h" -#include "FakeMediaStreamsImpl.h" -#include "FakeLogging.h" #include "nsThreadUtils.h" #include "runnable_utils.h" #include "signaling/src/common/EncodingConstraints.h" - -#include "FakeIPC.h" -#include "FakeIPC.cpp" +#include "prmem.h" #define GTEST_HAS_RTTI 0 #include "gtest/gtest.h" -#include "gtest_utils.h" - -nsCOMPtr gMainThread; -nsCOMPtr gGtestThread; -bool gTestsComplete = false; - -#include "mtransport_test_utils.h" -MtransportTestUtils *test_utils; //Video Frame Color const int COLOR = 0x80; //Gray @@ -128,7 +115,7 @@ int numFrames; * A Dummy AudioConduit Tester * The test reads PCM samples of a standard test file and * passws to audio-conduit for encoding, RTPfication and - * decoding ebery 10 milliseconds. + * decoding every 10 milliseconds. * This decoded samples are read-off the conduit for writing * into output audio file in PCM format. */ @@ -136,7 +123,7 @@ class AudioSendAndReceive { public: static const unsigned int PLAYOUT_SAMPLE_FREQUENCY; //default is 16000 - static const unsigned int PLAYOUT_SAMPLE_LENGTH; //default is 160000 + static const unsigned int PLAYOUT_SAMPLE_LENGTH; //default is 160 AudioSendAndReceive() { @@ -173,7 +160,7 @@ private: }; const unsigned int AudioSendAndReceive::PLAYOUT_SAMPLE_FREQUENCY = 16000; -const unsigned int AudioSendAndReceive::PLAYOUT_SAMPLE_LENGTH = 160000; +const unsigned int AudioSendAndReceive::PLAYOUT_SAMPLE_LENGTH = 160; int AudioSendAndReceive::WriteWaveHeader(int rate, int channels, FILE* outFile) { @@ -262,12 +249,7 @@ void AudioSendAndReceive::GenerateMusic(short* buf, int len) a1=b1=a2=b2=0; c1=c2=d1=d2=0; j=0; - /*60ms silence */ - for(i=0;i<2880;i++) - { - buf[i*2]=buf[(i*2)+1]=0; - } - for(i=2880;i(PLAYOUT_SAMPLE_LENGTH); short* inbuf; int sampleLengthDecoded = 0; - unsigned int SAMPLES = (PLAYOUT_SAMPLE_FREQUENCY * 10); //10 seconds + unsigned int SAMPLES = (PLAYOUT_SAMPLE_FREQUENCY / 100); //10 milliseconds int CHANNELS = 1; //mono audio int sampleLengthInBytes = sizeof(int16_t) * PLAYOUT_SAMPLE_LENGTH; //generated audio buffer @@ -337,7 +319,6 @@ void AudioSendAndReceive::GenerateAndReadSamples() } numSamplesReadFromInput += PLAYOUT_SAMPLE_LENGTH; - inbuf += PLAYOUT_SAMPLE_LENGTH; mSession->SendAudioFrame(audioInput.get(), PLAYOUT_SAMPLE_LENGTH, @@ -366,7 +347,7 @@ void AudioSendAndReceive::GenerateAndReadSamples() /** * Dummy Video Target for the conduit - * This class acts as renderer attached to the video conuit + * This class acts as renderer attached to the video conduit * As of today we just verify if the frames rendered are exactly * the same as frame inserted at the first place */ @@ -509,8 +490,9 @@ private: bool mAudio, mVideo; }; +using namespace mozilla; -namespace { +namespace test { class TransportConduitTest : public ::testing::Test { @@ -525,13 +507,6 @@ class TransportConduitTest : public ::testing::Test ~TransportConduitTest() { - mozilla::SyncRunnable::DispatchToThread(gMainThread, - mozilla::WrapRunnable( - this, - &TransportConduitTest::SelfDestruct)); - } - - void SelfDestruct() { mAudioSession = nullptr; mAudioSession2 = nullptr; mAudioTransport = nullptr; @@ -547,19 +522,13 @@ class TransportConduitTest : public ::testing::Test { //get pointer to AudioSessionConduit int err=0; - mozilla::SyncRunnable::DispatchToThread(gMainThread, - WrapRunnableNMRet(&mAudioSession, - &mozilla::AudioSessionConduit::Create)); - if( !mAudioSession ) { + mAudioSession = mozilla::AudioSessionConduit::Create(); + if( !mAudioSession ) ASSERT_NE(mAudioSession, (void*)nullptr); - } - mozilla::SyncRunnable::DispatchToThread(gMainThread, - WrapRunnableNMRet(&mAudioSession2, - &mozilla::AudioSessionConduit::Create)); - if( !mAudioSession2 ) { + mAudioSession2 = mozilla::AudioSessionConduit::Create(); + if( !mAudioSession2 ) ASSERT_NE(mAudioSession2, (void*)nullptr); - } WebrtcMediaTransport* xport = new WebrtcMediaTransport(); ASSERT_NE(xport, (void*)nullptr); @@ -600,9 +569,7 @@ class TransportConduitTest : public ::testing::Test cerr << " ******************************************************** " << endl; cerr << " Generating Audio Samples " << endl; cerr << " ******************************************************** " << endl; - PR_Sleep(PR_SecondsToInterval(2)); audioTester.GenerateAndReadSamples(); - PR_Sleep(PR_SecondsToInterval(2)); cerr << " ******************************************************** " << endl; cerr << " Input Audio File " << iAudiofilename << endl; cerr << " Output Audio File " << oAudiofilename << endl; @@ -610,28 +577,18 @@ class TransportConduitTest : public ::testing::Test } //2. Dump audio samples to dummy external transport - void TestDummyVideoAndTransport(bool send_vp8 = true, const char *source_file = nullptr) + void TestDummyVideoAndTransport(const char *source_file = nullptr) { int err = 0; //get pointer to VideoSessionConduit - mozilla::SyncRunnable::DispatchToThread(gMainThread, - WrapRunnableNMRet(&mVideoSession, - &mozilla::VideoSessionConduit::Create)); - if( !mVideoSession ) { + mVideoSession = VideoSessionConduit::Create(); + if( !mVideoSession ) ASSERT_NE(mVideoSession, (void*)nullptr); - } - // This session is for other one - mozilla::SyncRunnable::DispatchToThread(gMainThread, - WrapRunnableNMRet(&mVideoSession2, - &mozilla::VideoSessionConduit::Create)); - if( !mVideoSession2 ) { + // This session is for other one + mVideoSession2 = VideoSessionConduit::Create(); + if( !mVideoSession2 ) ASSERT_NE(mVideoSession2,(void*)nullptr); - } - - if (!send_vp8) { - SetGmpCodecs(); - } mVideoRenderer = new DummyVideoTarget(); ASSERT_NE(mVideoRenderer, (void*)nullptr); @@ -659,15 +616,13 @@ class TransportConduitTest : public ::testing::Test rcvCodecList.push_back(&cinst1); rcvCodecList.push_back(&cinst2); - err = mVideoSession->ConfigureSendMediaCodec( - send_vp8 ? &cinst1 : &cinst2); + err = mVideoSession->ConfigureSendMediaCodec(&cinst1); ASSERT_EQ(mozilla::kMediaConduitNoError, err); err = mVideoSession->StartTransmitting(); ASSERT_EQ(mozilla::kMediaConduitNoError, err); - err = mVideoSession2->ConfigureSendMediaCodec( - send_vp8 ? &cinst1 : &cinst2); + err = mVideoSession2->ConfigureSendMediaCodec(&cinst1); err = mVideoSession2->StartTransmitting(); ASSERT_EQ(mozilla::kMediaConduitNoError, err); @@ -679,10 +634,8 @@ class TransportConduitTest : public ::testing::Test cerr << " *************************************************" << endl; cerr << " Starting the Video Sample Generation " << endl; cerr << " *************************************************" << endl; - PR_Sleep(PR_SecondsToInterval(2)); videoTester.Init(mVideoSession); videoTester.GenerateAndReadSamples(); - PR_Sleep(PR_SecondsToInterval(2)); cerr << " **************************************************" << endl; cerr << " Done With The Testing " << endl; @@ -697,19 +650,10 @@ class TransportConduitTest : public ::testing::Test cerr << " Done With The Testing " << endl; cerr << " **************************************************" << endl; + ASSERT_EQ(0, vidStatsGlobal.numFramesRenderedWrongly); - if (send_vp8) { - ASSERT_EQ(vidStatsGlobal.numRawFramesInserted, - vidStatsGlobal.numFramesRenderedSuccessfully); - } - else { - // Allow some fudge because there seems to be some buffering. - // TODO(ekr@rtfm.com): Fix this. - ASSERT_GE(vidStatsGlobal.numRawFramesInserted, - vidStatsGlobal.numFramesRenderedSuccessfully); - ASSERT_LE(vidStatsGlobal.numRawFramesInserted, - vidStatsGlobal.numFramesRenderedSuccessfully + 2); - } + ASSERT_EQ(vidStatsGlobal.numRawFramesInserted, + vidStatsGlobal.numFramesRenderedSuccessfully); } void TestVideoConduitCodecAPI() @@ -717,12 +661,9 @@ class TransportConduitTest : public ::testing::Test int err = 0; RefPtr videoSession; //get pointer to VideoSessionConduit - mozilla::SyncRunnable::DispatchToThread(gMainThread, - WrapRunnableNMRet(&videoSession, - &mozilla::VideoSessionConduit::Create)); - if( !videoSession ) { + videoSession = VideoSessionConduit::Create(); + if( !videoSession ) ASSERT_NE(videoSession, (void*)nullptr); - } //Test Configure Recv Codec APIS cerr << " *************************************************" << endl; @@ -742,12 +683,11 @@ class TransportConduitTest : public ::testing::Test rcvCodecList.push_back(&cinst1); rcvCodecList.push_back(&cinst2); err = videoSession->ConfigureRecvMediaCodecs(rcvCodecList); - EXPECT_NE(err,mozilla::kMediaConduitNoError); + EXPECT_EQ(err, mozilla::kMediaConduitNoError); rcvCodecList.pop_back(); rcvCodecList.pop_back(); - PR_Sleep(PR_SecondsToInterval(2)); cerr << " *************************************************" << endl; cerr << " 2. Codec With Invalid Payload Names " << endl; cerr << " *************************************************" << endl; @@ -766,7 +706,6 @@ class TransportConduitTest : public ::testing::Test rcvCodecList.pop_back(); - PR_Sleep(PR_SecondsToInterval(2)); cerr << " *************************************************" << endl; cerr << " 3. Null Codec Parameter " << endl; cerr << " *************************************************" << endl; @@ -791,7 +730,7 @@ class TransportConduitTest : public ::testing::Test err = videoSession->StartTransmitting(); ASSERT_EQ(mozilla::kMediaConduitNoError, err); err = videoSession->ConfigureSendMediaCodec(&cinst1); - EXPECT_EQ(mozilla::kMediaConduitCodecInUse, err); + EXPECT_EQ(mozilla::kMediaConduitNoError, err); err = videoSession->StartTransmitting(); ASSERT_EQ(mozilla::kMediaConduitNoError, err); @@ -810,11 +749,6 @@ class TransportConduitTest : public ::testing::Test err = videoSession->ConfigureSendMediaCodec(nullptr); EXPECT_TRUE(err != mozilla::kMediaConduitNoError); - - mozilla::SyncRunnable::DispatchToThread(gMainThread, - WrapRunnable( - videoSession.forget().take(), - &mozilla::VideoSessionConduit::Release)); } void DumpMaxFs(int orig_width, int orig_height, int max_fs, @@ -833,12 +767,9 @@ class TransportConduitTest : public ::testing::Test int err = 0; // Get pointer to VideoSessionConduit. - mozilla::SyncRunnable::DispatchToThread(gMainThread, - WrapRunnableNMRet(&mVideoSession, - &mozilla::VideoSessionConduit::Create)); - if( !mVideoSession ) { + mVideoSession = VideoSessionConduit::Create(); + if( !mVideoSession ) ASSERT_NE(mVideoSession, (void*)nullptr); - } mozilla::EncodingConstraints constraints; constraints.maxFs = max_fs; @@ -902,7 +833,7 @@ class TransportConduitTest : public ::testing::Test GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height); DumpMaxFs(orig_width, orig_height, max_fs, width, height); ASSERT_EQ(width, 768); - ASSERT_EQ(height, 26); + ASSERT_EQ(height, 25); // Small max-fs. cerr << "Test small max-fs (case 1)" << endl; @@ -921,7 +852,7 @@ class TransportConduitTest : public ::testing::Test max_fs = 1; GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height); DumpMaxFs(orig_width, orig_height, max_fs, width, height); - ASSERT_EQ(width, 2); + ASSERT_EQ(width, 1); ASSERT_EQ(height, 16); // Small max-fs. @@ -932,7 +863,7 @@ class TransportConduitTest : public ::testing::Test GetVideoResolutionWithMaxFs(orig_width, orig_height, max_fs, &width, &height); DumpMaxFs(orig_width, orig_height, max_fs, width, height); ASSERT_EQ(width, 48); - ASSERT_EQ(height, 8); + ASSERT_EQ(height, 7); // Small max-fs. cerr << "Test small max-fs (case 4)" << endl; @@ -969,23 +900,10 @@ class TransportConduitTest : public ::testing::Test DumpMaxFs(orig_width, orig_height, max_fs, width, height); ADD_FAILURE(); } - if ((width & 1) || (height & 1)) { - DumpMaxFs(orig_width, orig_height, max_fs, width, height); - ADD_FAILURE(); - } } cerr << endl; } - void SetGmpCodecs() { - mExternalEncoder = mozilla::GmpVideoCodec::CreateEncoder(); - mExternalDecoder = mozilla::GmpVideoCodec::CreateDecoder(); - mozilla::EncodingConstraints constraints; - mozilla::VideoCodecConfig config(124, "H264", constraints); - mVideoSession->SetExternalSendCodec(&config, mExternalEncoder); - mVideoSession2->SetExternalRecvCodec(&config, mExternalDecoder); - } - private: //Audio Conduit Test Objects RefPtr mAudioSession; @@ -1000,9 +918,6 @@ class TransportConduitTest : public ::testing::Test RefPtr mVideoTransport; VideoSendAndReceive videoTester; - mozilla::VideoEncoder* mExternalEncoder; - mozilla::VideoDecoder* mExternalDecoder; - std::string fileToPlay; std::string fileToRecord; std::string iAudiofilename; @@ -1011,19 +926,17 @@ class TransportConduitTest : public ::testing::Test // Test 1: Test Dummy External Xport -TEST_F(TransportConduitTest, TestDummyAudioWithTransport) { +// See Bug 1319121 +TEST_F(TransportConduitTest, DISABLED_TestDummyAudioWithTransport) { TestDummyAudioAndTransport(); } // Test 2: Test Dummy External Xport -TEST_F(TransportConduitTest, TestDummyVideoWithTransport) { +// See Bug 1319121 +TEST_F(TransportConduitTest, DISABLED_TestDummyVideoWithTransport) { TestDummyVideoAndTransport(); } -TEST_F(TransportConduitTest, TestVideoConduitExternalCodec) { - TestDummyVideoAndTransport(false); -} - TEST_F(TransportConduitTest, TestVideoConduitCodecAPI) { TestVideoConduitCodecAPI(); } @@ -1033,65 +946,3 @@ TEST_F(TransportConduitTest, TestVideoConduitMaxFs) { } } // end namespace - -static int test_result; -bool test_finished = false; - - - -// This exists to send as an event to trigger shutdown. -static void tests_complete() { - gTestsComplete = true; -} - -// The GTest thread runs this instead of the main thread so it can -// do things like ASSERT_TRUE_WAIT which you could not do on the main thread. -static int gtest_main(int argc, char **argv) { - MOZ_ASSERT(!NS_IsMainThread()); - - ::testing::InitGoogleTest(&argc, argv); - - int result = RUN_ALL_TESTS(); - - // Set the global shutdown flag and tickle the main thread - // The main thread did not go through Init() so calling Shutdown() - // on it will not work. - gMainThread->Dispatch(mozilla::WrapRunnableNM(tests_complete), NS_DISPATCH_SYNC); - - return result; -} - -int main(int argc, char **argv) -{ - // This test can cause intermittent oranges on the builders - CHECK_ENVIRONMENT_FLAG("MOZ_WEBRTC_MEDIACONDUIT_TESTS") - - test_utils = new MtransportTestUtils(); - - // Set the main thread global which is this thread. - nsIThread *thread; - NS_GetMainThread(&thread); - gMainThread = thread; - - // Now create the GTest thread and run all of the tests on it - // When it is complete it will set gTestsComplete - NS_NewNamedThread("gtest_thread", &thread); - gGtestThread = thread; - - int result; - gGtestThread->Dispatch( - mozilla::WrapRunnableNMRet(&result, gtest_main, argc, argv), NS_DISPATCH_NORMAL); - - // Here we handle the event queue for dispatches to the main thread - // When the GTest thread is complete it will send one more dispatch - // with gTestsComplete == true. - while (!gTestsComplete && NS_ProcessNextEvent()); - - gGtestThread->Shutdown(); - - delete test_utils; - return test_result; -} - - - diff --git a/media/webrtc/signaling/gtest/moz.build b/media/webrtc/signaling/gtest/moz.build index 155664924ced..aec3ce48b5e3 100644 --- a/media/webrtc/signaling/gtest/moz.build +++ b/media/webrtc/signaling/gtest/moz.build @@ -13,12 +13,15 @@ if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk' and '/media/mtransport', '/media/webrtc/', '/media/webrtc/signaling/src/common/time_profiling', + '/media/webrtc/signaling/src/media-conduit', '/media/webrtc/signaling/src/peerconnection', + '/media/webrtc/trunk/', ] SOURCES += [ 'jsep_session_unittest.cpp', 'jsep_track_unittest.cpp', + 'mediaconduit_unittests.cpp', 'sdp_unittests.cpp', ] diff --git a/media/webrtc/signaling/test/moz.build b/media/webrtc/signaling/test/moz.build index 79a29abafab4..71778fbec3c6 100644 --- a/media/webrtc/signaling/test/moz.build +++ b/media/webrtc/signaling/test/moz.build @@ -7,7 +7,6 @@ # TODO: bug 1172551 - get these tests working on iOS if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'uikit': GeckoCppUnitTests([ - 'mediaconduit_unittests', 'mediapipeline_unittest', 'signaling_unittests', ]) diff --git a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java index 27f1fa67fbee..1096afc56e01 100644 --- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java @@ -103,12 +103,9 @@ import org.mozilla.gecko.util.EventCallback; import org.mozilla.gecko.util.FloatUtils; import org.mozilla.gecko.util.GamepadUtils; import org.mozilla.gecko.util.GeckoBundle; -import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.HardwareUtils; import org.mozilla.gecko.util.IntentUtils; import org.mozilla.gecko.util.MenuUtils; -import org.mozilla.gecko.util.NativeEventListener; -import org.mozilla.gecko.util.NativeJSObject; import org.mozilla.gecko.util.PrefUtils; import org.mozilla.gecko.util.StringUtils; import org.mozilla.gecko.util.ThreadUtils; @@ -202,8 +199,7 @@ public class BrowserApp extends GeckoApp OnUrlOpenInBackgroundListener, AnchoredPopup.OnVisibilityChangeListener, ActionModeCompat.Presenter, - LayoutInflater.Factory, - BundleEventListener { + LayoutInflater.Factory { private static final String LOGTAG = "GeckoBrowserApp"; private static final int TABS_ANIMATION_DURATION = 450; @@ -726,32 +722,36 @@ public class BrowserApp extends GeckoApp mDoorhangerOverlay = findViewById(R.id.doorhanger_overlay); - EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener)this, - "Gecko:DelayedStartup", + EventDispatcher.getInstance().registerGeckoThreadListener(this, + "Search:Keyword", + "Favicon:CacheLoad", + null); + + EventDispatcher.getInstance().registerUiThreadListener(this, "Menu:Open", "Menu:Update", + "Menu:Add", + "Menu:Remove", "LightweightTheme:Update", - "Search:Keyword", "Tab:Added", - "Video:Play"); - - EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener)this, + "Video:Play", "CharEncoding:Data", "CharEncoding:State", - "Download:AndroidDownloadManager", + "Settings:Show", + "Updater:Launch", + null); + + EventDispatcher.getInstance().registerBackgroundThreadListener(this, "Experiments:GetActive", "Experiments:SetOverride", "Experiments:ClearOverride", - "Favicon:CacheLoad", "Feedback:MaybeLater", - "Menu:Add", - "Menu:Remove", "Sanitize:ClearHistory", "Sanitize:ClearSyncedTabs", - "Settings:Show", "Telemetry:Gather", - "Updater:Launch", - "Website:Metadata"); + "Download:AndroidDownloadManager", + "Website:Metadata", + null); getAppEventDispatcher().registerUiThreadListener(this, "Prompt:ShowTop"); @@ -1430,32 +1430,36 @@ public class BrowserApp extends GeckoApp mSearchEngineManager.unregisterListeners(); - EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener) this, - "Gecko:DelayedStartup", + EventDispatcher.getInstance().unregisterGeckoThreadListener(this, + "Search:Keyword", + "Favicon:CacheLoad", + null); + + EventDispatcher.getInstance().unregisterUiThreadListener(this, "Menu:Open", "Menu:Update", + "Menu:Add", + "Menu:Remove", "LightweightTheme:Update", - "Search:Keyword", "Tab:Added", - "Video:Play"); - - EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener) this, + "Video:Play", "CharEncoding:Data", "CharEncoding:State", - "Download:AndroidDownloadManager", + "Settings:Show", + "Updater:Launch", + null); + + EventDispatcher.getInstance().unregisterBackgroundThreadListener(this, "Experiments:GetActive", "Experiments:SetOverride", "Experiments:ClearOverride", - "Favicon:CacheLoad", "Feedback:MaybeLater", - "Menu:Add", - "Menu:Remove", "Sanitize:ClearHistory", "Sanitize:ClearSyncedTabs", - "Settings:Show", "Telemetry:Gather", - "Updater:Launch", - "Website:Metadata"); + "Download:AndroidDownloadManager", + "Website:Metadata", + null); getAppEventDispatcher().unregisterUiThreadListener(this, "Prompt:ShowTop"); @@ -1548,25 +1552,6 @@ public class BrowserApp extends GeckoApp alphaAnimator.start(); } - private void handleClearHistory(final boolean clearSearchHistory) { - final BrowserDB db = BrowserDB.from(getProfile()); - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - db.clearHistory(getContentResolver(), clearSearchHistory); - } - }); - } - - private void handleClearSyncedTabs() { - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - FennecTabsRepository.deleteNonLocalClientsAndTabs(getContext()); - } - }); - } - private void setToolbarMargin(int margin) { ((RelativeLayout.LayoutParams) mGeckoLayout.getLayoutParams()).topMargin = margin; mGeckoLayout.requestLayout(); @@ -1663,29 +1648,21 @@ public class BrowserApp extends GeckoApp @Override void toggleChrome(final boolean aShow) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - if (aShow) { - mBrowserChrome.setVisibility(View.VISIBLE); - } else { - mBrowserChrome.setVisibility(View.GONE); - } - } - }); + if (aShow) { + mBrowserChrome.setVisibility(View.VISIBLE); + } else { + mBrowserChrome.setVisibility(View.GONE); + } super.toggleChrome(aShow); } @Override void focusChrome() { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - mBrowserChrome.setVisibility(View.VISIBLE); - mActionBarFlipper.requestFocusFromTouch(); - } - }); + mBrowserChrome.setVisibility(View.VISIBLE); + mActionBarFlipper.requestFocusFromTouch(); + + super.focusChrome(); } @Override @@ -1707,6 +1684,124 @@ public class BrowserApp extends GeckoApp public void handleMessage(final String event, final GeckoBundle message, final EventCallback callback) { switch (event) { + case "Gecko:Ready": + EventDispatcher.getInstance().registerUiThreadListener(this, "Gecko:DelayedStartup"); + + // Handle this message in GeckoApp, but also enable the Settings + // menuitem, which is specific to BrowserApp. + super.handleMessage(event, message, callback); + + final Menu menu = mMenu; + ThreadUtils.postToUiThread(new Runnable() { + @Override + public void run() { + if (menu != null) { + menu.findItem(R.id.settings).setEnabled(true); + menu.findItem(R.id.help).setEnabled(true); + } + } + }); + + // Display notification for Mozilla data reporting, if data should be collected. + if (AppConstants.MOZ_DATA_REPORTING && + Restrictions.isAllowed(this, Restrictable.DATA_CHOICES)) { + DataReportingNotification.checkAndNotifyPolicy(GeckoAppShell.getContext()); + } + break; + + case "Gecko:DelayedStartup": + EventDispatcher.getInstance().unregisterUiThreadListener(this, "Gecko:DelayedStartup"); + + // Force tabs panel inflation once the initial pageload is finished. + ensureTabsPanelExists(); + + if (AppConstants.NIGHTLY_BUILD && mZoomedView == null) { + ViewStub stub = (ViewStub) findViewById(R.id.zoomed_view_stub); + mZoomedView = (ZoomedView) stub.inflate(); + } + + if (AppConstants.MOZ_MEDIA_PLAYER) { + // Check if the fragment is already added. This should never be true + // here, but this is a nice safety check. If casting is disabled, + // these classes aren't built. We use reflection to initialize them. + final Class mediaManagerClass = getMediaPlayerManager(); + + if (mediaManagerClass != null) { + try { + final String tag = ""; + mediaManagerClass.getDeclaredField("MEDIA_PLAYER_TAG").get(tag); + Log.i(LOGTAG, "Found tag " + tag); + final Fragment frag = getSupportFragmentManager().findFragmentByTag(tag); + if (frag == null) { + final Method getInstance = mediaManagerClass.getMethod( + "getInstance", (Class[]) null); + final Fragment mpm = (Fragment) getInstance.invoke(null); + getSupportFragmentManager().beginTransaction() + .disallowAddToBackStack().add(mpm, tag).commit(); + } + } catch (Exception ex) { + Log.e(LOGTAG, "Error initializing media manager", ex); + } + } + } + + if (AppConstants.MOZ_STUMBLER_BUILD_TIME_ENABLED && + Restrictions.isAllowed(this, Restrictable.DATA_CHOICES)) { + // Start (this acts as ping if started already) the stumbler lib; if + // the stumbler has queued data it will upload it. Stumbler operates + // on its own thread, and startup impact is further minimized by + // delaying work (such as upload) a few seconds. Avoid any potential + // startup CPU/thread contention by delaying the pref broadcast. + GeckoPreferences.broadcastStumblerPref(BrowserApp.this); + } + + if (AppConstants.MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE) { + // TODO: Better scheduling of sync action (Bug 1257492) + DownloadContentService.startSync(this); + DownloadContentService.startVerification(this); + } + + FeedService.setup(this); + break; + + case "Menu:Open": + if (mBrowserToolbar.isEditing()) { + mBrowserToolbar.cancelEdit(); + } + openOptionsMenu(); + break; + + case "Menu:Update": + updateAddonMenuItem(message.getInt("id"), message.getBundle("options")); + break; + + case "Menu:Add": + final MenuItemInfo info = new MenuItemInfo(); + info.label = message.getString("name"); + info.id = message.getInt("id") + ADDON_MENU_OFFSET; + info.checked = message.getBoolean("checked", false); + info.enabled = message.getBoolean("enabled", true); + info.visible = message.getBoolean("visible", true); + info.checkable = message.getBoolean("checkable", false); + final int parent = message.getInt("parent", 0); + info.parent = parent <= 0 ? parent : parent + ADDON_MENU_OFFSET; + addAddonMenuItem(info); + break; + + case "Menu:Remove": + removeAddonMenuItem(message.getInt("id") + ADDON_MENU_OFFSET); + break; + + case "LightweightTheme:Update": + mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE); + break; + + case "Search:Keyword": + storeSearchQuery(message.getString("query")); + recordSearch(GeckoSharedPrefs.forProfile(this), message.getString("identifier"), + TelemetryContract.Method.ACTIONBAR); + break; + case "Prompt:ShowTop": // Bring this activity to front so the prompt is visible.. Intent bringToFrontIntent = new Intent(); @@ -1715,21 +1810,32 @@ public class BrowserApp extends GeckoApp bringToFrontIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); startActivity(bringToFrontIntent); break; - } - } - @Override - public void handleMessage(final String event, final NativeJSObject message, - final EventCallback callback) { - switch (event) { + case "Tab:Added": + if (message.getBoolean("cancelEditMode")) { + // Set the target tab to null so it does not get selected (on editing + // mode exit) in lieu of the tab that we're going to open and select. + mTargetTabForEditingMode = null; + mBrowserToolbar.cancelEdit(); + } + break; + + case "Video:Play": + if (SwitchBoard.isInExperiment(this, Experiments.HLS_VIDEO_PLAYBACK)) { + mVideoPlayer.start(Uri.parse(message.getString("uri"))); + Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, + TelemetryContract.Method.CONTENT, "playhls"); + } + break; + case "CharEncoding:Data": - final NativeJSObject[] charsets = message.getObjectArray("charsets"); + final GeckoBundle[] charsets = message.getBundleArray("charsets"); final int selected = message.getInt("selected"); final String[] titleArray = new String[charsets.length]; final String[] codeArray = new String[charsets.length]; for (int i = 0; i < charsets.length; i++) { - final NativeJSObject charset = charsets[i]; + final GeckoBundle charset = charsets[i]; titleArray[i] = charset.getString("title"); codeArray[i] = charset.getString("code"); } @@ -1750,26 +1856,15 @@ public class BrowserApp extends GeckoApp dialog.dismiss(); } }); - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - dialogBuilder.show(); - } - }); + dialogBuilder.show(); break; case "CharEncoding:State": - final boolean visible = message.getString("visible").equals("true"); + final boolean visible = "true".equals(message.getString("visible")); GeckoPreferences.setCharEncodingState(visible); - final Menu menu = mMenu; - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - if (menu != null) { - menu.findItem(R.id.char_encoding).setVisible(visible); - } - } - }); + if (mMenu != null) { + mMenu.findItem(R.id.char_encoding).setVisible(visible); + } break; case "Experiments:GetActive": @@ -1779,7 +1874,8 @@ public class BrowserApp extends GeckoApp break; case "Experiments:SetOverride": - Experiments.setOverride(getContext(), message.getString("name"), message.getBoolean("isEnabled")); + Experiments.setOverride(getContext(), message.getString("name"), + message.getBoolean("isEnabled")); break; case "Experiments:ClearOverride": @@ -1792,52 +1888,25 @@ public class BrowserApp extends GeckoApp break; case "Feedback:MaybeLater": - resetFeedbackLaunchCount(); - break; - - case "Menu:Add": - final MenuItemInfo info = new MenuItemInfo(); - info.label = message.getString("name"); - info.id = message.getInt("id") + ADDON_MENU_OFFSET; - info.checked = message.optBoolean("checked", false); - info.enabled = message.optBoolean("enabled", true); - info.visible = message.optBoolean("visible", true); - info.checkable = message.optBoolean("checkable", false); - final int parent = message.optInt("parent", 0); - info.parent = parent <= 0 ? parent : parent + ADDON_MENU_OFFSET; - final MenuItemInfo menuItemInfo = info; - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - addAddonMenuItem(menuItemInfo); - } - }); - break; - - case "Menu:Remove": - final int id = message.getInt("id") + ADDON_MENU_OFFSET; - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - removeAddonMenuItem(id); - } - }); + SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE); + settings.edit().putInt(getPackageName() + ".feedback_launch_count", 0).apply(); break; case "Sanitize:ClearHistory": - handleClearHistory(message.optBoolean("clearSearchHistory", false)); - callback.sendSuccess(true); + BrowserDB.from(getProfile()).clearHistory( + getContentResolver(), message.getBoolean("clearSearchHistory", false)); + callback.sendSuccess(null); break; case "Sanitize:ClearSyncedTabs": - handleClearSyncedTabs(); - callback.sendSuccess(true); + FennecTabsRepository.deleteNonLocalClientsAndTabs(getContext()); + callback.sendSuccess(null); break; case "Settings:Show": - final String resource = - message.optString(GeckoPreferences.INTENT_EXTRA_RESOURCES, null); final Intent settingsIntent = new Intent(this, GeckoPreferences.class); + final String resource = message.getString(GeckoPreferences.INTENT_EXTRA_RESOURCES); + GeckoPreferences.setResourceToOpen(settingsIntent, resource); startActivityForResult(settingsIntent, ACTIVITY_REQUEST_PREFERENCES); @@ -1851,30 +1920,58 @@ public class BrowserApp extends GeckoApp case "Telemetry:Gather": final BrowserDB db = BrowserDB.from(getProfile()); final ContentResolver cr = getContentResolver(); + Telemetry.addToHistogram("PLACES_PAGES_COUNT", db.getCount(cr, "history")); Telemetry.addToHistogram("FENNEC_BOOKMARKS_COUNT", db.getCount(cr, "bookmarks")); - Telemetry.addToHistogram("BROWSER_IS_USER_DEFAULT", (isDefaultBrowser(Intent.ACTION_VIEW) ? 1 : 0)); - Telemetry.addToHistogram("FENNEC_CUSTOM_HOMEPAGE", (TextUtils.isEmpty(getHomepage()) ? 0 : 1)); + Telemetry.addToHistogram("BROWSER_IS_USER_DEFAULT", + (isDefaultBrowser(Intent.ACTION_VIEW) ? 1 : 0)); + Telemetry.addToHistogram("FENNEC_CUSTOM_HOMEPAGE", + (TextUtils.isEmpty(getHomepage()) ? 0 : 1)); + final SharedPreferences prefs = GeckoSharedPrefs.forProfile(getContext()); final boolean hasCustomHomepanels = - prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY) || prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY_OLD); + prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY) || + prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY_OLD); + Telemetry.addToHistogram("FENNEC_HOMEPANELS_CUSTOM", hasCustomHomepanels ? 1 : 0); Telemetry.addToHistogram("FENNEC_READER_VIEW_CACHE_SIZE", - SavedReaderViewHelper.getSavedReaderViewHelper(getContext()).getDiskSpacedUsedKB()); + SavedReaderViewHelper.getSavedReaderViewHelper(getContext()) + .getDiskSpacedUsedKB()); if (Versions.feature16Plus) { - Telemetry.addToHistogram("BROWSER_IS_ASSIST_DEFAULT", (isDefaultBrowser(Intent.ACTION_ASSIST) ? 1 : 0)); + Telemetry.addToHistogram("BROWSER_IS_ASSIST_DEFAULT", + (isDefaultBrowser(Intent.ACTION_ASSIST) ? 1 : 0)); } break; case "Updater:Launch": - handleUpdaterLaunch(); + /** + * Launch UI that lets the user update Firefox. + * + * This depends on the current channel: Release and Beta both direct to + * the Google Play Store. If updating is enabled, Aurora, Nightly, and + * custom builds open about:, which provides an update interface. + * + * If updating is not enabled, this simply logs an error. + */ + if (AppConstants.RELEASE_OR_BETA) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse("market://details?id=" + getPackageName())); + startActivity(intent); + break; + } + + if (AppConstants.MOZ_UPDATER) { + Tabs.getInstance().loadUrlInTab(AboutPages.UPDATER); + break; + } + + Log.w(LOGTAG, "No candidate updater found; ignoring launch request."); break; case "Download:AndroidDownloadManager": // Downloading via Android's download manager - final String uri = message.getString("uri"); final String filename = message.getString("filename"); final String mimeType = message.getString("mimeType"); @@ -1883,54 +1980,49 @@ public class BrowserApp extends GeckoApp request.setMimeType(mimeType); try { - request.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS, filename); + request.setDestinationInExternalFilesDir( + this, Environment.DIRECTORY_DOWNLOADS, filename); } catch (IllegalStateException e) { Log.e(LOGTAG, "Cannot create download directory"); - return; + break; } request.allowScanningByMediaScanner(); - request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + request.setNotificationVisibility( + DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); request.addRequestHeader("User-Agent", HardwareUtils.isTablet() ? AppConstants.USER_AGENT_FENNEC_TABLET : AppConstants.USER_AGENT_FENNEC_MOBILE); try { - DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); + DownloadManager manager = (DownloadManager) + getSystemService(Context.DOWNLOAD_SERVICE); manager.enqueue(request); - - Log.d(LOGTAG, "Enqueued download (Download Manager)"); } catch (RuntimeException e) { Log.e(LOGTAG, "Download failed: " + e); } break; case "Website:Metadata": - final NativeJSObject metadata = message.getObject("metadata"); final String location = message.getString("location"); + final boolean hasImage = message.getBoolean("hasImage"); + final String metadata = message.getString("metadata"); - final boolean hasImage = !TextUtils.isEmpty(metadata.optString("image_url", null)); - final String metadataJSON = metadata.toString(); - - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - final ContentProviderClient contentProviderClient = getContentResolver() - .acquireContentProviderClient(BrowserContract.PageMetadata.CONTENT_URI); - if (contentProviderClient == null) { - Log.w(LOGTAG, "Failed to obtain content provider client for: " + BrowserContract.PageMetadata.CONTENT_URI); - return; - } - try { - GlobalPageMetadata.getInstance().add( - BrowserDB.from(getProfile()), - contentProviderClient, - location, hasImage, metadataJSON); - } finally { - contentProviderClient.release(); - } - } - }); + final ContentProviderClient contentProviderClient = getContentResolver() + .acquireContentProviderClient(BrowserContract.PageMetadata.CONTENT_URI); + if (contentProviderClient == null) { + Log.w(LOGTAG, "Failed to obtain content provider client for: " + + BrowserContract.PageMetadata.CONTENT_URI); + return; + } + try { + GlobalPageMetadata.getInstance().add( + BrowserDB.from(getProfile()), + contentProviderClient, + location, hasImage, metadata); + } finally { + contentProviderClient.release(); + } break; @@ -1994,164 +2086,6 @@ public class BrowserApp extends GeckoApp return (TextUtils.equals(packageName, getPackageName())); } - @Override - public void handleMessage(String event, JSONObject message) { - try { - switch (event) { - case "Menu:Open": - if (mBrowserToolbar.isEditing()) { - mBrowserToolbar.cancelEdit(); - } - - openOptionsMenu(); - break; - - case "Menu:Update": - final int id = message.getInt("id") + ADDON_MENU_OFFSET; - final JSONObject options = message.getJSONObject("options"); - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - updateAddonMenuItem(id, options); - } - }); - break; - - case "Gecko:DelayedStartup": - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - // Force tabs panel inflation once the initial - // pageload is finished. - ensureTabsPanelExists(); - if (AppConstants.NIGHTLY_BUILD && mZoomedView == null) { - ViewStub stub = (ViewStub) findViewById(R.id.zoomed_view_stub); - mZoomedView = (ZoomedView) stub.inflate(); - } - } - }); - - if (AppConstants.MOZ_MEDIA_PLAYER) { - // Check if the fragment is already added. This should never be true here, but this is - // a nice safety check. - // If casting is disabled, these classes aren't built. We use reflection to initialize them. - final Class mediaManagerClass = getMediaPlayerManager(); - - if (mediaManagerClass != null) { - try { - final String tag = ""; - mediaManagerClass.getDeclaredField("MEDIA_PLAYER_TAG").get(tag); - Log.i(LOGTAG, "Found tag " + tag); - final Fragment frag = getSupportFragmentManager().findFragmentByTag(tag); - if (frag == null) { - final Method getInstance = mediaManagerClass.getMethod("getInstance", (Class[]) null); - final Fragment mpm = (Fragment) getInstance.invoke(null); - getSupportFragmentManager().beginTransaction().disallowAddToBackStack().add(mpm, tag).commit(); - } - } catch (Exception ex) { - Log.e(LOGTAG, "Error initializing media manager", ex); - } - } - } - - if (AppConstants.MOZ_STUMBLER_BUILD_TIME_ENABLED && Restrictions.isAllowed(this, Restrictable.DATA_CHOICES)) { - // Start (this acts as ping if started already) the stumbler lib; if the stumbler has queued data it will upload it. - // Stumbler operates on its own thread, and startup impact is further minimized by delaying work (such as upload) a few seconds. - // Avoid any potential startup CPU/thread contention by delaying the pref broadcast. - final long oneSecondInMillis = 1000; - ThreadUtils.getBackgroundHandler().postDelayed(new Runnable() { - @Override - public void run() { - GeckoPreferences.broadcastStumblerPref(BrowserApp.this); - } - }, oneSecondInMillis); - } - - if (AppConstants.MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE) { - // TODO: Better scheduling of sync action (Bug 1257492) - DownloadContentService.startSync(this); - - DownloadContentService.startVerification(this); - } - - FeedService.setup(this); - - super.handleMessage(event, message); - break; - - case "Gecko:Ready": - // Handle this message in GeckoApp, but also enable the Settings - // menuitem, which is specific to BrowserApp. - super.handleMessage(event, message); - final Menu menu = mMenu; - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - if (menu != null) { - menu.findItem(R.id.settings).setEnabled(true); - menu.findItem(R.id.help).setEnabled(true); - } - } - }); - - // Display notification for Mozilla data reporting, if data should be collected. - if (AppConstants.MOZ_DATA_REPORTING && Restrictions.isAllowed(this, Restrictable.DATA_CHOICES)) { - DataReportingNotification.checkAndNotifyPolicy(GeckoAppShell.getContext()); - } - break; - - case "Search:Keyword": - storeSearchQuery(message.getString("query")); - recordSearch(GeckoSharedPrefs.forProfile(this), message.getString("identifier"), - TelemetryContract.Method.ACTIONBAR); - break; - - case "LightweightTheme:Update": - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE); - } - }); - break; - - case "Video:Play": - if (SwitchBoard.isInExperiment(this, Experiments.HLS_VIDEO_PLAYBACK)) { - final String uri = message.getString("uri"); - final String uuid = message.getString("uuid"); - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - mVideoPlayer.start(Uri.parse(uri)); - Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.CONTENT, "playhls"); - } - }); - } - break; - - case "Tab:Added": - if (message.getBoolean("cancelEditMode")) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - // Set the target tab to null so it does not get selected (on editing - // mode exit) in lieu of the tab that we're going to open and select. - mTargetTabForEditingMode = null; - mBrowserToolbar.cancelEdit(); - } - }); - } - break; - - default: - super.handleMessage(event, message); - break; - } - } catch (Exception e) { - Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); - } - } - @Override public void addTab() { Tabs.getInstance().addTab(); @@ -3248,16 +3182,16 @@ public class BrowserApp extends GeckoApp mMenu.removeItem(id); } - private void updateAddonMenuItem(int id, JSONObject options) { + private void updateAddonMenuItem(int id, final GeckoBundle options) { // Set attribute for the menu item in cache, if available if (mAddonMenuItemsCache != null && !mAddonMenuItemsCache.isEmpty()) { for (MenuItemInfo item : mAddonMenuItemsCache) { if (item.id == id) { - item.label = options.optString("name", item.label); - item.checkable = options.optBoolean("checkable", item.checkable); - item.checked = options.optBoolean("checked", item.checked); - item.enabled = options.optBoolean("enabled", item.enabled); - item.visible = options.optBoolean("visible", item.visible); + item.label = options.getString("name", item.label); + item.checkable = options.getBoolean("checkable", item.checkable); + item.checked = options.getBoolean("checked", item.checked); + item.enabled = options.getBoolean("enabled", item.enabled); + item.visible = options.getBoolean("visible", item.visible); item.added = (mMenu != null); break; } @@ -3270,11 +3204,11 @@ public class BrowserApp extends GeckoApp final MenuItem menuItem = mMenu.findItem(id); if (menuItem != null) { - menuItem.setTitle(options.optString("name", menuItem.getTitle().toString())); - menuItem.setCheckable(options.optBoolean("checkable", menuItem.isCheckable())); - menuItem.setChecked(options.optBoolean("checked", menuItem.isChecked())); - menuItem.setEnabled(options.optBoolean("enabled", menuItem.isEnabled())); - menuItem.setVisible(options.optBoolean("visible", menuItem.isVisible())); + menuItem.setTitle(options.getString("name", menuItem.getTitle().toString())); + menuItem.setCheckable(options.getBoolean("checkable", menuItem.isCheckable())); + menuItem.setChecked(options.getBoolean("checked", menuItem.isChecked())); + menuItem.setEnabled(options.getBoolean("enabled", menuItem.isEnabled())); + menuItem.setVisible(options.getBoolean("visible", menuItem.isVisible())); } } @@ -4029,11 +3963,6 @@ public class BrowserApp extends GeckoApp }); } - private void resetFeedbackLaunchCount() { - SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE); - settings.edit().putInt(getPackageName() + ".feedback_launch_count", 0).apply(); - } - // HomePager.OnUrlOpenListener @Override public void onUrlOpen(String url, EnumSet flags) { @@ -4147,34 +4076,6 @@ public class BrowserApp extends GeckoApp return mReadingListHelper; } - /** - * Launch UI that lets the user update Firefox. - * - * This depends on the current channel: Release and Beta both direct to the - * Google Play Store. If updating is enabled, Aurora, Nightly, and custom - * builds open about:, which provides an update interface. - * - * If updating is not enabled, this simply logs an error. - * - * @return true if update UI was launched. - */ - protected boolean handleUpdaterLaunch() { - if (AppConstants.RELEASE_OR_BETA) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse("market://details?id=" + getPackageName())); - startActivity(intent); - return true; - } - - if (AppConstants.MOZ_UPDATER) { - Tabs.getInstance().loadUrlInTab(AboutPages.UPDATER); - return true; - } - - Log.w(LOGTAG, "No candidate updater found; ignoring launch request."); - return false; - } - /* Implementing ActionModeCompat.Presenter */ @Override public void startActionModeCompat(final ActionModeCompat.Callback callback) { diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java index 9100433a8ecb..3c76ac6042e9 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java @@ -39,12 +39,12 @@ import org.mozilla.gecko.text.TextSelection; import org.mozilla.gecko.updater.UpdateServiceHelper; import org.mozilla.gecko.util.ActivityResultHandler; import org.mozilla.gecko.util.ActivityUtils; +import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.EventCallback; import org.mozilla.gecko.util.FileUtils; -import org.mozilla.gecko.util.GeckoEventListener; +import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.GeckoRequest; import org.mozilla.gecko.util.HardwareUtils; -import org.mozilla.gecko.util.NativeEventListener; import org.mozilla.gecko.util.NativeJSObject; import org.mozilla.gecko.util.PrefUtils; import org.mozilla.gecko.util.ThreadUtils; @@ -130,12 +130,11 @@ import java.util.concurrent.TimeUnit; public abstract class GeckoApp extends GeckoActivity implements + BundleEventListener, ContextGetter, GeckoAppShell.GeckoInterface, - GeckoEventListener, GeckoMenu.Callback, GeckoMenu.MenuPresenter, - NativeEventListener, Tabs.OnTabsChangedListener, ViewTreeObserver.OnGlobalLayoutListener { @@ -616,36 +615,52 @@ public abstract class GeckoApp public boolean areTabsShown() { return false; } @Override - public void handleMessage(final String event, final NativeJSObject message, + public void handleMessage(final String event, final GeckoBundle message, final EventCallback callback) { - if ("Accessibility:Ready".equals(event)) { + if (event.equals("Gecko:Ready")) { + mGeckoReadyStartupTimer.stop(); + geckoConnected(); + + // This method is already running on the background thread, so we + // know that mHealthRecorder will exist. That doesn't stop us being + // paranoid. + // This method is cheap, so don't spawn a new runnable. + final HealthRecorder rec = mHealthRecorder; + if (rec != null) { + rec.recordGeckoStartupTime(mGeckoReadyStartupTimer.getElapsed()); + } + + GeckoApplication.get().onDelayedStartup(); + + } else if (event.equals("Gecko:Exited")) { + // Gecko thread exited first; let GeckoApp die too. + doShutdown(); + + } else if ("Accessibility:Ready".equals(event)) { GeckoAccessibility.updateAccessibilitySettings(this); + } else if ("Accessibility:Event".equals(event)) { + GeckoAccessibility.sendAccessibilityEvent(message); + } else if ("Bookmark:Insert".equals(event)) { - final String url = message.getString("url"); - final String title = message.getString("title"); - final Context context = this; final BrowserDB db = BrowserDB.from(getProfile()); - ThreadUtils.postToBackgroundThread(new Runnable() { + final boolean bookmarkAdded = db.addBookmark( + getContentResolver(), message.getString("title"), message.getString("url")); + final int resId = bookmarkAdded ? R.string.bookmark_added + : R.string.bookmark_already_added; + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { - final boolean bookmarkAdded = db.addBookmark(getContentResolver(), title, url); - final int resId = bookmarkAdded ? R.string.bookmark_added : R.string.bookmark_already_added; - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - SnackbarBuilder.builder(GeckoApp.this) - .message(resId) - .duration(Snackbar.LENGTH_LONG) - .buildAndShow(); - } - }); + SnackbarBuilder.builder(GeckoApp.this) + .message(resId) + .duration(Snackbar.LENGTH_LONG) + .buildAndShow(); } }); } else if ("Contact:Add".equals(event)) { - final String email = message.optString("email", null); - final String phone = message.optString("phone", null); + final String email = message.getString("email", null); + final String phone = message.getString("phone", null); if (email != null) { Uri contactUri = Uri.parse(email); Intent i = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT, contactUri); @@ -685,51 +700,12 @@ public abstract class GeckoApp setLocale(message.getString("locale")); } else if ("Permissions:Data".equals(event)) { - final NativeJSObject[] permissions = message.getObjectArray("permissions"); + final GeckoBundle[] permissions = message.getBundleArray("permissions"); showSiteSettingsDialog(permissions); } else if ("PrivateBrowsing:Data".equals(event)) { - mPrivateBrowsingSession = message.optString("session", null); + mPrivateBrowsingSession = message.getString("session", null); - } else if ("Session:StatePurged".equals(event)) { - onStatePurged(); - - } else if ("Share:Text".equals(event)) { - final String text = message.getString("text"); - final Tab tab = Tabs.getInstance().getSelectedTab(); - String title = ""; - if (tab != null) { - title = tab.getDisplayTitle(); - } - IntentHelper.openUriExternal(text, "text/plain", "", "", Intent.ACTION_SEND, title, false); - - // Context: Sharing via chrome list (no explicit session is active) - Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST, "text"); - - } else if ("Snackbar:Show".equals(event)) { - SnackbarBuilder.builder(this) - .fromEvent(message) - .callback(callback) - .buildAndShow(); - - } else if ("SystemUI:Visibility".equals(event)) { - setSystemUiVisible(message.getBoolean("visible")); - - } else if ("ToggleChrome:Focus".equals(event)) { - focusChrome(); - - } else if ("ToggleChrome:Hide".equals(event)) { - toggleChrome(false); - - } else if ("ToggleChrome:Show".equals(event)) { - toggleChrome(true); - - } else if ("Update:Check".equals(event)) { - UpdateServiceHelper.checkForUpdate(this); - } else if ("Update:Download".equals(event)) { - UpdateServiceHelper.downloadUpdate(this); - } else if ("Update:Install".equals(event)) { - UpdateServiceHelper.applyUpdate(this); } else if ("RuntimePermissions:Prompt".equals(event)) { String[] permissions = message.getStringArray("permissions"); if (callback == null || permissions == null) { @@ -750,54 +726,64 @@ public abstract class GeckoApp callback.sendSuccess(true); } }); - } - } - @Override - public void handleMessage(String event, JSONObject message) { - try { - if (event.equals("Gecko:Ready")) { - mGeckoReadyStartupTimer.stop(); - geckoConnected(); - - // This method is already running on the background thread, so we - // know that mHealthRecorder will exist. That doesn't stop us being - // paranoid. - // This method is cheap, so don't spawn a new runnable. - final HealthRecorder rec = mHealthRecorder; - if (rec != null) { - rec.recordGeckoStartupTime(mGeckoReadyStartupTimer.getElapsed()); - } - - GeckoApplication.get().onDelayedStartup(); - - } else if (event.equals("Gecko:Exited")) { - // Gecko thread exited first; let GeckoApp die too. - doShutdown(); - return; - - } else if (event.equals("Accessibility:Event")) { - GeckoAccessibility.sendAccessibilityEvent(message); + } else if ("Share:Text".equals(event)) { + final String text = message.getString("text"); + final Tab tab = Tabs.getInstance().getSelectedTab(); + String title = ""; + if (tab != null) { + title = tab.getDisplayTitle(); } - } catch (Exception e) { - Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); + IntentHelper.openUriExternal(text, "text/plain", "", "", Intent.ACTION_SEND, title, false); + + // Context: Sharing via chrome list (no explicit session is active) + Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST, "text"); + + } else if ("Snackbar:Show".equals(event)) { + SnackbarBuilder.builder(this) + .fromEvent(message) + .callback(callback) + .buildAndShow(); + + } else if ("SystemUI:Visibility".equals(event)) { + if (message.getBoolean("visible", true)) { + mMainLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } else { + mMainLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); + } + + } else if ("ToggleChrome:Focus".equals(event)) { + focusChrome(); + + } else if ("ToggleChrome:Hide".equals(event)) { + toggleChrome(false); + + } else if ("ToggleChrome:Show".equals(event)) { + toggleChrome(true); + + } else if ("Update:Check".equals(event)) { + UpdateServiceHelper.checkForUpdate(this); + + } else if ("Update:Download".equals(event)) { + UpdateServiceHelper.downloadUpdate(this); + + } else if ("Update:Install".equals(event)) { + UpdateServiceHelper.applyUpdate(this); } } - void onStatePurged() { } - /** * @param permissions * Array of JSON objects to represent site permissions. * Example: { type: "offline-app", setting: "Store Offline Data", value: "Allow" } */ - private void showSiteSettingsDialog(final NativeJSObject[] permissions) { + private void showSiteSettingsDialog(final GeckoBundle[] permissions) { final AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.site_settings_title); final ArrayList> itemList = new ArrayList>(); - for (final NativeJSObject permObj : permissions) { + for (final GeckoBundle permObj : permissions) { final HashMap map = new HashMap(); map.put("setting", permObj.getString("setting")); map.put("value", permObj.getString("value")); @@ -840,36 +826,29 @@ public abstract class GeckoApp } }); - ThreadUtils.postToUiThread(new Runnable() { + AlertDialog dialog = builder.create(); + dialog.show(); + + final ListView listView = dialog.getListView(); + if (listView != null) { + listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + } + + final Button clearButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); + clearButton.setEnabled(false); + + dialog.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override - public void run() { - AlertDialog dialog = builder.create(); - dialog.show(); - - final ListView listView = dialog.getListView(); - if (listView != null) { - listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + public void onItemClick(AdapterView adapterView, View view, int i, long l) { + if (listView.getCheckedItemCount() == 0) { + clearButton.setEnabled(false); + } else { + clearButton.setEnabled(true); } - - final Button clearButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); - clearButton.setEnabled(false); - - dialog.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView adapterView, View view, int i, long l) { - if (listView.getCheckedItemCount() == 0) { - clearButton.setEnabled(false); - } else { - clearButton.setEnabled(true); - } - } - }); } }); } - - /* package */ void addFullScreenPluginView(View view) { if (mFullScreenPluginView != null) { Log.w(LOGTAG, "Already have a fullscreen plugin view"); @@ -958,6 +937,35 @@ public abstract class GeckoApp } } + private void showSetImageResult(final boolean success, final int message, final String path) { + ThreadUtils.postToUiThread(new Runnable() { + @Override + public void run() { + if (!success) { + SnackbarBuilder.builder(GeckoApp.this) + .message(message) + .duration(Snackbar.LENGTH_LONG) + .buildAndShow(); + return; + } + + final Intent intent = new Intent(Intent.ACTION_ATTACH_DATA); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setData(Uri.parse(path)); + + // Removes the image from storage once the chooser activity ends. + Intent chooser = Intent.createChooser(intent, getString(message)); + ActivityResultHandler handler = new ActivityResultHandler() { + @Override + public void onActivityResult (int resultCode, Intent data) { + getContentResolver().delete(intent.getData(), null, null); + } + }; + ActivityHandlerHelper.startIntentForActivity(GeckoApp.this, chooser, handler); + } + }); + } + // This method starts downloading an image synchronously and displays the Chooser activity to set the image as wallpaper. private void setImageAs(final String aSrc) { boolean isDataURI = aSrc.startsWith("data:"); @@ -990,38 +998,17 @@ public abstract class GeckoApp File dcimDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); if (!dcimDir.mkdirs() && !dcimDir.isDirectory()) { - SnackbarBuilder.builder(this) - .message(R.string.set_image_path_fail) - .duration(Snackbar.LENGTH_LONG) - .buildAndShow(); + showSetImageResult(/* success */ false, R.string.set_image_path_fail, null); return; } String path = Media.insertImage(getContentResolver(), image, null, null); if (path == null) { - SnackbarBuilder.builder(this) - .message(R.string.set_image_path_fail) - .duration(Snackbar.LENGTH_LONG) - .buildAndShow(); + showSetImageResult(/* success */ false, R.string.set_image_path_fail, null); return; } - final Intent intent = new Intent(Intent.ACTION_ATTACH_DATA); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.setData(Uri.parse(path)); - - // Removes the image from storage once the chooser activity ends. - Intent chooser = Intent.createChooser(intent, getString(R.string.set_image_chooser_title)); - ActivityResultHandler handler = new ActivityResultHandler() { - @Override - public void onActivityResult (int resultCode, Intent data) { - getContentResolver().delete(intent.getData(), null, null); - } - }; - ActivityHandlerHelper.startIntentForActivity(this, chooser, handler); + showSetImageResult(/* success */ true, R.string.set_image_chooser_title, path); } else { - SnackbarBuilder.builder(this) - .message(R.string.set_image_fail) - .duration(Snackbar.LENGTH_LONG) - .buildAndShow(); + showSetImageResult(/* success */ false, R.string.set_image_fail, null); } } catch (OutOfMemoryError ome) { Log.e(LOGTAG, "Out of Memory when converting to byte array", ome); @@ -1193,15 +1180,12 @@ public abstract class GeckoApp } } - // GeckoThread has to register for "Gecko:Ready" first, so GeckoApp registers - // for events after initializing GeckoThread but before launching it. - - EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener)this, + // To prevent races, register startup events before launching the Gecko thread. + EventDispatcher.getInstance().registerGeckoThreadListener(this, + "Accessibility:Ready", + "Gecko:Exited", "Gecko:Ready", - "Gecko:Exited"); - - EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener)this, - "Accessibility:Ready"); + null); GeckoThread.launch(); @@ -1233,30 +1217,33 @@ public abstract class GeckoApp mMainLayout = (RelativeLayout) findViewById(R.id.main_layout); mLayerView = (GeckoView) findViewById(R.id.layer_view); - getAppEventDispatcher().registerGeckoThreadListener((GeckoEventListener)this, - "Accessibility:Event"); + getAppEventDispatcher().registerGeckoThreadListener(this, + "Accessibility:Event", + "Locale:Set", + null); - getAppEventDispatcher().registerGeckoThreadListener((NativeEventListener)this, + getAppEventDispatcher().registerBackgroundThreadListener(this, "Bookmark:Insert", + "Image:SetAs", + null); + + getAppEventDispatcher().registerUiThreadListener(this, "Contact:Add", "DevToolsAuth:Scan", "DOMFullScreen:Start", "DOMFullScreen:Stop", - "Image:SetAs", - "Locale:Set", "Permissions:Data", "PrivateBrowsing:Data", "RuntimePermissions:Prompt", - "Session:StatePurged", "Share:Text", - "Snackbar:Show", "SystemUI:Visibility", "ToggleChrome:Focus", "ToggleChrome:Hide", "ToggleChrome:Show", "Update:Check", "Update:Download", - "Update:Install"); + "Update:Install", + null); Tabs.getInstance().attachToContext(this, mLayerView); @@ -1683,9 +1670,9 @@ public abstract class GeckoApp public void run() { if (TabQueueHelper.TAB_QUEUE_ENABLED && TabQueueHelper.shouldOpenTabQueueUrls(GeckoApp.this)) { - getAppEventDispatcher().registerGeckoThreadListener(new NativeEventListener() { + getAppEventDispatcher().registerGeckoThreadListener(new BundleEventListener() { @Override - public void handleMessage(String event, NativeJSObject message, EventCallback callback) { + public void handleMessage(String event, GeckoBundle message, EventCallback callback) { if ("Tabs:TabsOpened".equals(event)) { getAppEventDispatcher().unregisterGeckoThreadListener(this, "Tabs:TabsOpened"); openTabsRunnable.run(); @@ -2083,6 +2070,9 @@ public abstract class GeckoApp // After an onPause, the activity is back in the foreground. // Undo whatever we did in onPause. super.onResume(); + + EventDispatcher.getInstance().registerUiThreadListener(this, "Snackbar:Show"); + if (mIsAbortingAppLaunch) { return; } @@ -2164,6 +2154,8 @@ public abstract class GeckoApp @Override public void onPause() { + EventDispatcher.getInstance().unregisterUiThreadListener(this, "Snackbar:Show"); + if (mIsAbortingAppLaunch) { super.onPause(); return; @@ -2247,37 +2239,39 @@ public abstract class GeckoApp return; } - EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener)this, + EventDispatcher.getInstance().unregisterGeckoThreadListener(this, + "Accessibility:Ready", + "Gecko:Exited", "Gecko:Ready", - "Gecko:Exited"); + null); - EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener)this, - "Accessibility:Ready"); + getAppEventDispatcher().unregisterGeckoThreadListener(this, + "Accessibility:Event", + "Locale:Set", + null); - getAppEventDispatcher().unregisterGeckoThreadListener((GeckoEventListener)this, - "Accessibility:Event"); - - getAppEventDispatcher().unregisterGeckoThreadListener((NativeEventListener)this, + getAppEventDispatcher().unregisterBackgroundThreadListener(this, "Bookmark:Insert", + "Image:SetAs", + null); + + getAppEventDispatcher().unregisterUiThreadListener(this, "Contact:Add", "DevToolsAuth:Scan", "DOMFullScreen:Start", "DOMFullScreen:Stop", - "Image:SetAs", - "Locale:Set", "Permissions:Data", "PrivateBrowsing:Data", "RuntimePermissions:Prompt", - "Session:StatePurged", "Share:Text", - "Snackbar:Show", "SystemUI:Visibility", "ToggleChrome:Focus", "ToggleChrome:Hide", "ToggleChrome:Show", "Update:Check", "Update:Download", - "Update:Install"); + "Update:Install", + null); if (mPromptService != null) mPromptService.destroy(); @@ -2799,19 +2793,6 @@ public abstract class GeckoApp onLocaleChanged(resultant); } - private void setSystemUiVisible(final boolean visible) { - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - if (visible) { - mMainLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); - } else { - mMainLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); - } - } - }); - } - protected HealthRecorder createHealthRecorder(final Context context, final String profilePath, final EventDispatcher dispatcher, diff --git a/mobile/android/base/java/org/mozilla/gecko/SnackbarBuilder.java b/mobile/android/base/java/org/mozilla/gecko/SnackbarBuilder.java index 07f83a4e1127..48cf3f0c3a60 100644 --- a/mobile/android/base/java/org/mozilla/gecko/SnackbarBuilder.java +++ b/mobile/android/base/java/org/mozilla/gecko/SnackbarBuilder.java @@ -6,7 +6,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.util.EventCallback; -import org.mozilla.gecko.util.NativeJSObject; +import org.mozilla.gecko.util.GeckoBundle; import android.app.Activity; import android.graphics.Color; @@ -173,11 +173,11 @@ public class SnackbarBuilder { /** * @param object Populate the builder with data from a Gecko Snackbar:Show event. */ - public SnackbarBuilder fromEvent(final NativeJSObject object) { + public SnackbarBuilder fromEvent(final GeckoBundle object) { message = object.getString("message"); duration = object.getInt("duration"); - if (object.has("backgroundColor")) { + if (object.containsKey("backgroundColor")) { final String providedColor = object.getString("backgroundColor"); try { backgroundColor = Color.parseColor(providedColor); @@ -186,9 +186,9 @@ public class SnackbarBuilder { } } - NativeJSObject actionObject = object.optObject("action", null); + GeckoBundle actionObject = object.getBundle("action"); if (actionObject != null) { - action = actionObject.optString("label", null); + action = actionObject.getString("label", null); } return this; } diff --git a/mobile/android/base/java/org/mozilla/gecko/Tabs.java b/mobile/android/base/java/org/mozilla/gecko/Tabs.java index 4fc94cfc7742..ac0a0f7c23f9 100644 --- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java +++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java @@ -23,6 +23,9 @@ import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.mozglue.SafeIntent; import org.mozilla.gecko.notifications.WhatsNewReceiver; import org.mozilla.gecko.reader.ReaderModeUtils; +import org.mozilla.gecko.util.BundleEventListener; +import org.mozilla.gecko.util.EventCallback; +import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.ThreadUtils; @@ -40,7 +43,7 @@ import android.provider.Browser; import android.support.v4.content.ContextCompat; import android.util.Log; -public class Tabs implements GeckoEventListener { +public class Tabs implements BundleEventListener, GeckoEventListener { private static final String LOGTAG = "GeckoTabs"; // mOrder and mTabs are always of the same cardinality, and contain the same values. @@ -104,8 +107,11 @@ public class Tabs implements GeckoEventListener { }; private Tabs() { - EventDispatcher.getInstance().registerGeckoThreadListener(this, + EventDispatcher.getInstance().registerUiThreadListener(this, "Tab:Added", + null); + + EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener) this, "Tab:Close", "Tab:Select", "Tab:SelectAndForeground", @@ -473,50 +479,56 @@ public class Tabs implements GeckoEventListener { return Tabs.TabsInstanceHolder.INSTANCE; } + @Override // BundleEventListener + public synchronized void handleMessage(final String event, final GeckoBundle message, + final EventCallback callback) { + // "Tab:Added" is a special case because tab will be null if the tab was just added + if ("Tab:Added".equals(event)) { + int id = message.getInt("tabID"); + Tab tab = getTab(id); + + String url = message.getString("uri"); + + if (message.getBoolean("cancelEditMode")) { + final Tab oldTab = getSelectedTab(); + if (oldTab != null) { + oldTab.setIsEditing(false); + } + } + + if (message.getBoolean("stub")) { + if (tab == null) { + // Tab was already closed; abort + return; + } + } else { + tab = addTab(id, url, message.getBoolean("external"), + message.getInt("parentId"), + message.getString("title"), + message.getBoolean("isPrivate"), + message.getInt("tabIndex")); + // If we added the tab as a stub, we should have already + // selected it, so ignore this flag for stubbed tabs. + if (message.getBoolean("selected")) + selectTab(id); + } + + if (message.getBoolean("delayLoad")) + tab.setState(Tab.STATE_DELAYED); + if (message.getBoolean("desktopMode")) + tab.setDesktopMode(true); + } + } + // GeckoEventListener implementation @Override - public void handleMessage(String event, JSONObject message) { + public synchronized void handleMessage(String event, JSONObject message) { Log.d(LOGTAG, "handleMessage: " + event); try { // All other events handled below should contain a tabID property int id = message.getInt("tabID"); Tab tab = getTab(id); - // "Tab:Added" is a special case because tab will be null if the tab was just added - if (event.equals("Tab:Added")) { - String url = message.isNull("uri") ? null : message.getString("uri"); - - if (message.getBoolean("cancelEditMode")) { - final Tab oldTab = getSelectedTab(); - if (oldTab != null) { - oldTab.setIsEditing(false); - } - } - - if (message.getBoolean("stub")) { - if (tab == null) { - // Tab was already closed; abort - return; - } - } else { - tab = addTab(id, url, message.getBoolean("external"), - message.getInt("parentId"), - message.getString("title"), - message.getBoolean("isPrivate"), - message.getInt("tabIndex")); - // If we added the tab as a stub, we should have already - // selected it, so ignore this flag for stubbed tabs. - if (message.getBoolean("selected")) - selectTab(id); - } - - if (message.getBoolean("delayLoad")) - tab.setState(Tab.STATE_DELAYED); - if (message.getBoolean("desktopMode")) - tab.setDesktopMode(true); - return; - } - // Tab was already closed; abort if (tab == null) return; diff --git a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java index c9afecd636f0..6e82c94474cc 100644 --- a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java +++ b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java @@ -538,14 +538,7 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD @Override public void onClick(View widget) { Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, "hint_private_browsing"); - try { - final JSONObject json = new JSONObject(); - json.put("type", "Menu:Open"); - GeckoApp.getEventDispatcher().dispatchEvent(json, null); - EventDispatcher.getInstance().dispatchEvent(json, null); - } catch (JSONException e) { - Log.e(LOGTAG, "Error forming JSON for Private Browsing contextual hint", e); - } + EventDispatcher.getInstance().dispatch("Menu:Open", null); } }; diff --git a/mobile/android/base/java/org/mozilla/gecko/lwt/LightweightTheme.java b/mobile/android/base/java/org/mozilla/gecko/lwt/LightweightTheme.java index 0f27c1febc15..dc35a162200c 100644 --- a/mobile/android/base/java/org/mozilla/gecko/lwt/LightweightTheme.java +++ b/mobile/android/base/java/org/mozilla/gecko/lwt/LightweightTheme.java @@ -15,7 +15,9 @@ import org.mozilla.gecko.AppConstants.Versions; import org.mozilla.gecko.EventDispatcher; import org.mozilla.gecko.GeckoSharedPrefs; import org.mozilla.gecko.gfx.BitmapUtils; -import org.mozilla.gecko.util.GeckoEventListener; +import org.mozilla.gecko.util.BundleEventListener; +import org.mozilla.gecko.util.EventCallback; +import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.WindowUtils; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.util.ThreadUtils.AssertBehavior; @@ -37,7 +39,7 @@ import android.view.Gravity; import android.view.View; import android.view.ViewParent; -public class LightweightTheme implements GeckoEventListener { +public class LightweightTheme implements BundleEventListener { private static final String LOGTAG = "GeckoLightweightTheme"; private static final String PREFS_URL = "lightweightTheme.headerURL"; @@ -163,7 +165,7 @@ public class LightweightTheme implements GeckoEventListener { mListeners = new ArrayList(); // unregister isn't needed as the lifetime is same as the application. - EventDispatcher.getInstance().registerGeckoThreadListener(this, + EventDispatcher.getInstance().registerUiThreadListener(this, "LightweightTheme:Update", "LightweightTheme:Disable"); @@ -181,28 +183,19 @@ public class LightweightTheme implements GeckoEventListener { } @Override - public void handleMessage(String event, JSONObject message) { - try { - if (event.equals("LightweightTheme:Update")) { - JSONObject lightweightTheme = message.getJSONObject("data"); - final String headerURL = lightweightTheme.getString("headerURL"); - final String color = lightweightTheme.optString("accentcolor"); + public void handleMessage(String event, GeckoBundle message, EventCallback callback) { + if (event.equals("LightweightTheme:Update")) { + GeckoBundle lightweightTheme = message.getBundle("data"); + final String headerURL = lightweightTheme.getString("headerURL"); + final String color = lightweightTheme.getString("accentcolor"); - ThreadUtils.postToBackgroundThread(new LightweightThemeRunnable(headerURL, color)); - } else if (event.equals("LightweightTheme:Disable")) { - // Clear the saved data when a theme is disabled. - // Called on the Gecko thread, but should be very lightweight. - clearPrefs(); + ThreadUtils.postToBackgroundThread(new LightweightThemeRunnable(headerURL, color)); - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - resetLightweightTheme(); - } - }); - } - } catch (Exception e) { - Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); + } else if (event.equals("LightweightTheme:Disable")) { + // Clear the saved data when a theme is disabled. + // Called on the Gecko thread, but should be very lightweight. + clearPrefs(); + resetLightweightTheme(); } } diff --git a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java index dd1786ed914b..2b015bb2de5a 100644 --- a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java +++ b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java @@ -41,8 +41,10 @@ import org.mozilla.gecko.tabqueue.TabQueueHelper; import org.mozilla.gecko.tabqueue.TabQueuePrompt; import org.mozilla.gecko.updater.UpdateService; import org.mozilla.gecko.updater.UpdateServiceHelper; +import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.ContextUtils; import org.mozilla.gecko.util.EventCallback; +import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.HardwareUtils; import org.mozilla.gecko.util.InputOptionsUtils; import org.mozilla.gecko.util.NativeEventListener; @@ -101,12 +103,12 @@ import java.util.Locale; import java.util.Map; public class GeckoPreferences -extends AppCompatPreferenceActivity -implements -GeckoActivityStatus, -NativeEventListener, -OnPreferenceChangeListener, -OnSharedPreferenceChangeListener + extends AppCompatPreferenceActivity + implements BundleEventListener, + GeckoActivityStatus, + NativeEventListener, + OnPreferenceChangeListener, + OnSharedPreferenceChangeListener { private static final String LOGTAG = "GeckoPreferences"; @@ -369,9 +371,8 @@ OnSharedPreferenceChangeListener // Use setResourceToOpen to specify these extras. Bundle intentExtras = getIntent().getExtras(); - EventDispatcher.getInstance().registerGeckoThreadListener(this, - "Sanitize:Finished", - "Snackbar:Show"); + EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener) this, + "Sanitize:Finished"); // Add handling for long-press click. // This is only for Android 3.0 and below (which use the long-press-context-menu paradigm). @@ -506,9 +507,8 @@ OnSharedPreferenceChangeListener protected void onDestroy() { super.onDestroy(); - EventDispatcher.getInstance().unregisterGeckoThreadListener(this, - "Sanitize:Finished", - "Snackbar:Show"); + EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener) this, + "Sanitize:Finished"); if (mPrefsRequest != null) { PrefsHelper.removeObserver(mPrefsRequest); @@ -518,6 +518,8 @@ OnSharedPreferenceChangeListener @Override public void onPause() { + EventDispatcher.getInstance().unregisterUiThreadListener(this, "Snackbar:Show"); + // Symmetric with onResume. if (isMultiPane()) { SharedPreferences prefs = GeckoSharedPrefs.forApp(this); @@ -535,6 +537,8 @@ OnSharedPreferenceChangeListener public void onResume() { super.onResume(); + EventDispatcher.getInstance().registerUiThreadListener(this, "Snackbar:Show"); + if (getApplication() instanceof GeckoApplication) { ((GeckoApplication) getApplication()).onActivityResume(this); } @@ -607,6 +611,17 @@ OnSharedPreferenceChangeListener Permissions.onRequestPermissionsResult(this, permissions, grantResults); } + @Override + public void handleMessage(final String event, final GeckoBundle message, + final EventCallback callback) { + if ("Snackbar:Show".equals(event)) { + SnackbarBuilder.builder(this) + .fromEvent(message) + .callback(callback) + .buildAndShow(); + } + } + @Override public void handleMessage(final String event, final NativeJSObject message, final EventCallback callback) { try { @@ -620,12 +635,6 @@ OnSharedPreferenceChangeListener .duration(Snackbar.LENGTH_LONG) .buildAndShow(); break; - case "Snackbar:Show": - SnackbarBuilder.builder(this) - .fromEvent(message) - .callback(callback) - .buildAndShow(); - break; } } catch (Exception e) { Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); diff --git a/mobile/android/chrome/content/ActionBarHandler.js b/mobile/android/chrome/content/ActionBarHandler.js index 1900210432e0..2b4ff2a1961d 100644 --- a/mobile/android/chrome/content/ActionBarHandler.js +++ b/mobile/android/chrome/content/ActionBarHandler.js @@ -591,7 +591,7 @@ var ActionBarHandler = { }, action: function(element, win) { - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: "Share:Text", text: ActionBarHandler._getSelectedText(), }); diff --git a/mobile/android/chrome/content/Feedback.js b/mobile/android/chrome/content/Feedback.js index 8727c46c3921..7d5b7597e0cf 100644 --- a/mobile/android/chrome/content/Feedback.js +++ b/mobile/android/chrome/content/Feedback.js @@ -48,7 +48,7 @@ var Feedback = { break; case "FeedbackMaybeLater": - Messaging.sendRequest({ type: "Feedback:MaybeLater" }); + GlobalEventDispatcher.sendRequest({ type: "Feedback:MaybeLater" }); break; } diff --git a/mobile/android/chrome/content/PermissionsHelper.js b/mobile/android/chrome/content/PermissionsHelper.js index ad1eb760aaba..52e32096c566 100644 --- a/mobile/android/chrome/content/PermissionsHelper.js +++ b/mobile/android/chrome/content/PermissionsHelper.js @@ -106,7 +106,7 @@ var PermissionsHelper = { // Keep track of permissions, so we know which ones to clear this._currentPermissions = permissions; - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: "Permissions:Data", permissions: permissions }); diff --git a/mobile/android/chrome/content/Reader.js b/mobile/android/chrome/content/Reader.js index d0f3d7801a7a..0b05a06f021e 100644 --- a/mobile/android/chrome/content/Reader.js +++ b/mobile/android/chrome/content/Reader.js @@ -126,7 +126,7 @@ var Reader = { } case "Reader:SystemUIVisibility": - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: "SystemUI:Visibility", visible: message.data.visible }); diff --git a/mobile/android/chrome/content/RemoteDebugger.js b/mobile/android/chrome/content/RemoteDebugger.js index a5a3a43deecf..66a75062acbe 100644 --- a/mobile/android/chrome/content/RemoteDebugger.js +++ b/mobile/android/chrome/content/RemoteDebugger.js @@ -151,7 +151,7 @@ var RemoteDebugger = { return this._receivingOOB; } - this._receivingOOB = Messaging.sendRequestForResult({ + this._receivingOOB = WindowEventDispatcher.sendRequestForResult({ type: "DevToolsAuth:Scan" }).then(data => { return JSON.parse(data); diff --git a/mobile/android/chrome/content/about.js b/mobile/android/chrome/content/about.js index 8c9acdf8ae13..baa6d09affb0 100644 --- a/mobile/android/chrome/content/about.js +++ b/mobile/android/chrome/content/about.js @@ -78,17 +78,20 @@ function init() { function checkForUpdates() { showCheckingMessage(); - Services.androidBridge.handleGeckoMessage({ type: "Update:Check" }); + let window = Services.wm.getMostRecentWindow("navigator:browser"); + window.WindowEventDispatcher.sendRequest({ type: "Update:Check" }); } function downloadUpdate() { - Services.androidBridge.handleGeckoMessage({ type: "Update:Download" }); + let window = Services.wm.getMostRecentWindow("navigator:browser"); + window.WindowEventDispatcher.sendRequest({ type: "Update:Download" }); } function installUpdate() { showCheckAction(); - Services.androidBridge.handleGeckoMessage({ type: "Update:Install" }); + let window = Services.wm.getMostRecentWindow("navigator:browser"); + window.WindowEventDispatcher.sendRequest({ type: "Update:Install" }); } let updateLink = document.getElementById("updateLink"); diff --git a/mobile/android/chrome/content/aboutHealthReport.js b/mobile/android/chrome/content/aboutHealthReport.js index 070eb821df52..9615d5f7471b 100644 --- a/mobile/android/chrome/content/aboutHealthReport.js +++ b/mobile/android/chrome/content/aboutHealthReport.js @@ -8,8 +8,11 @@ var { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); Cu.import("resource://gre/modules/SharedPreferences.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", + "resource://gre/modules/Messaging.jsm"); // Name of Android SharedPreference controlling whether to upload // health reports. @@ -119,7 +122,7 @@ var healthReportWrapper = { showSettings: function () { console.log("AboutHealthReport: showing settings."); - Messaging.sendRequest({ + EventDispatcher.instance.sendRequest({ type: "Settings:Show", resource: "preferences_vendor", }); @@ -127,7 +130,7 @@ var healthReportWrapper = { launchUpdater: function () { console.log("AboutHealthReport: launching updater."); - Messaging.sendRequest({ + EventDispatcher.instance.sendRequest({ type: "Updater:Launch", }); }, diff --git a/mobile/android/chrome/content/aboutLogins.js b/mobile/android/chrome/content/aboutLogins.js index 99e2af841ad0..0770b94b73c1 100644 --- a/mobile/android/chrome/content/aboutLogins.js +++ b/mobile/android/chrome/content/aboutLogins.js @@ -5,7 +5,6 @@ var Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils; Cu.import("resource://services-common/utils.js"); /*global: CommonUtils */ -Cu.import("resource://gre/modules/Messaging.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/TelemetryStopwatch.jsm"); @@ -19,6 +18,8 @@ XPCOMUtils.defineLazyGetter(window, "gChromeWin", () => .getInterface(Ci.nsIDOMWindow) .QueryInterface(Ci.nsIDOMChromeWindow)); +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", + "resource://gre/modules/Messaging.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Prompt", "resource://gre/modules/Prompt.jsm"); @@ -416,7 +417,7 @@ var Logins = { _loadFavicon: function (aImg, aHostname) { // Load favicon from cache. - Messaging.sendRequestForResult({ + EventDispatcher.instance.sendRequestForResult({ type: "Favicon:CacheLoad", url: aHostname, }).then(function(faviconUrl) { diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index e240bc7416bb..97fa8077facb 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -390,14 +390,13 @@ var BrowserApp = { Services.obs.addObserver(this, "android-set-pref", false); Services.obs.addObserver(this, "gather-telemetry", false); Services.obs.addObserver(this, "keyword-search", false); - Services.obs.addObserver(this, "sessionstore-state-purge-complete", false); Services.obs.addObserver(this, "Fonts:Reload", false); Services.obs.addObserver(this, "Vibration:Request", false); Messaging.addListener(this.getHistory.bind(this), "Session:GetHistory"); window.addEventListener("fullscreen", function() { - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: window.fullScreen ? "ToggleChrome:Hide" : "ToggleChrome:Show" }); }, false); @@ -409,7 +408,7 @@ var BrowserApp = { // (per spec). This means the last event on enabling will be for the innermost // document, which will have fullscreenElement set correctly. let doc = e.target; - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: doc.fullscreenElement ? "DOMFullScreen:Start" : "DOMFullScreen:Stop", rootElement: doc.fullscreenElement == doc.documentElement }); @@ -508,7 +507,7 @@ var BrowserApp = { } // Notify Java that Gecko has loaded. - Messaging.sendRequest({ type: "Gecko:Ready" }); + GlobalEventDispatcher.sendRequest({ type: "Gecko:Ready" }); this.deck.addEventListener("DOMContentLoaded", function BrowserApp_delayedStartup() { BrowserApp.deck.removeEventListener("DOMContentLoaded", BrowserApp_delayedStartup, false); @@ -516,7 +515,7 @@ var BrowserApp = { InitLater(() => Cu.import("resource://gre/modules/NotificationDB.jsm")); InitLater(() => Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "")); - InitLater(() => Messaging.sendRequest({ type: "Gecko:DelayedStartup" })); + InitLater(() => GlobalEventDispatcher.sendRequest({ type: "Gecko:DelayedStartup" })); if (!AppConstants.RELEASE_OR_BETA) { InitLater(() => WebcompatReporter.init()); @@ -572,7 +571,7 @@ var BrowserApp = { */ setLocale: function (locale) { console.log("browser.js: requesting locale set: " + locale); - Messaging.sendRequest({ type: "Locale:Set", locale: locale }); + WindowEventDispatcher.sendRequest({ type: "Locale:Set", locale: locale }); }, initContextMenu: function () { @@ -725,7 +724,7 @@ var BrowserApp = { UITelemetry.addEvent("action.1", "contextmenu", null, "web_contact_email"); let url = NativeWindow.contextmenus._getLinkURL(aTarget); - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: "Contact:Add", email: url }); @@ -737,7 +736,7 @@ var BrowserApp = { UITelemetry.addEvent("action.1", "contextmenu", null, "web_contact_phone"); let url = NativeWindow.contextmenus._getLinkURL(aTarget); - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: "Contact:Add", phone: url }); @@ -751,7 +750,7 @@ var BrowserApp = { let url = NativeWindow.contextmenus._getLinkURL(aTarget); let title = aTarget.textContent || aTarget.title || url; - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: "Bookmark:Insert", url: url, title: title @@ -888,7 +887,7 @@ var BrowserApp = { UITelemetry.addEvent("action.1", "contextmenu", null, "web_background_image"); let src = aTarget.src; - Messaging.sendRequest({ + WindowEventDispatcher.sendRequest({ type: "Image:SetAs", url: src }); @@ -1755,7 +1754,7 @@ var BrowserApp = { let query = isPrivate ? "" : aData; let engine = aSubject.QueryInterface(Ci.nsISearchEngine); - Messaging.sendRequest({ + GlobalEventDispatcher.sendRequest({ type: "Search:Keyword", identifier: engine.identifier, name: engine.name, @@ -1901,12 +1900,8 @@ var BrowserApp = { break; } - case "sessionstore-state-purge-complete": - Messaging.sendRequest({ type: "Session:StatePurged" }); - break; - case "gather-telemetry": - Messaging.sendRequest({ type: "Telemetry:Gather" }); + GlobalEventDispatcher.sendRequest({ type: "Telemetry:Gather" }); break; case "Locale:OS": @@ -2178,21 +2173,21 @@ var NativeWindow = { options.type = "Menu:Add"; options.id = this._menuId; - Messaging.sendRequest(options); + GlobalEventDispatcher.sendRequest(options); this._callbacks[this._menuId] = options.callback; this._menuId++; return this._menuId - 1; }, remove: function(aId) { - Messaging.sendRequest({ type: "Menu:Remove", id: aId }); + GlobalEventDispatcher.sendRequest({ type: "Menu:Remove", id: aId }); }, update: function(aId, aOptions) { if (!aOptions) return; - Messaging.sendRequest({ + GlobalEventDispatcher.sendRequest({ type: "Menu:Update", id: aId, options: aOptions @@ -3503,7 +3498,7 @@ Tab.prototype = { isPrivate: isPrivate, stub: stub }; - Messaging.sendRequest(message); + GlobalEventDispatcher.sendRequest(message); } let flags = Ci.nsIWebProgress.NOTIFY_STATE_ALL | @@ -4534,7 +4529,7 @@ var BrowserEventHandler = { case 'OpenMediaWithExternalApp': { let mediaSrc = aEvent.target.currentSrc || aEvent.target.src; let uuid = uuidgen.generateUUID().toString(); - Services.androidBridge.handleGeckoMessage({ + GlobalEventDispatcher.sendRequest({ type: "Video:Play", uri: mediaSrc, uuid: uuid @@ -5631,7 +5626,7 @@ var CharacterEncoding = { showCharEncoding = Services.prefs.getComplexValue("browser.menu.showCharacterEncoding", Ci.nsIPrefLocalizedString).data; } catch (e) { /* Optional */ } - Messaging.sendRequest({ + GlobalEventDispatcher.sendRequest({ type: "CharEncoding:State", visible: showCharEncoding }); @@ -5665,7 +5660,7 @@ var CharacterEncoding = { } } - Messaging.sendRequest({ + GlobalEventDispatcher.sendRequest({ type: "CharEncoding:Data", charsets: this._charsets, selected: selected @@ -6333,7 +6328,7 @@ var Experiments = { OFFLINE_CACHE: "offline-cache", init() { - Messaging.sendRequestForResult({ + GlobalEventDispatcher.sendRequestForResult({ type: "Experiments:GetActive" }).then(experiments => { let names = JSON.parse(experiments); @@ -6360,7 +6355,7 @@ var Experiments = { }, setOverride(name, isEnabled) { - Messaging.sendRequest({ + GlobalEventDispatcher.sendRequest({ type: "Experiments:SetOverride", name: name, isEnabled: isEnabled @@ -6368,7 +6363,7 @@ var Experiments = { }, clearOverride(name) { - Messaging.sendRequest({ + GlobalEventDispatcher.sendRequest({ type: "Experiments:ClearOverride", name: name }); diff --git a/mobile/android/chrome/content/geckoview.js b/mobile/android/chrome/content/geckoview.js index b4685a8d3abd..998427983a07 100644 --- a/mobile/android/chrome/content/geckoview.js +++ b/mobile/android/chrome/content/geckoview.js @@ -14,9 +14,6 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Log", "resource://gre/modules/AndroidLog.jsm", "AndroidLog"); -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", - "resource://gre/modules/Messaging.jsm", "Messaging"); - XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm", "Services"); @@ -25,8 +22,5 @@ function dump(msg) { } function startup() { - dump("zerdatime " + Date.now() + " - geckoivew chrome startup finished."); - - // Notify Java that Gecko has loaded. - Messaging.sendRequest({ type: "Gecko:Ready" }); + dump("zerdatime " + Date.now() + " - geckoview chrome startup finished."); } diff --git a/mobile/android/components/HelperAppDialog.js b/mobile/android/components/HelperAppDialog.js index d1c20dede328..2298f0074bf3 100644 --- a/mobile/android/components/HelperAppDialog.js +++ b/mobile/android/components/HelperAppDialog.js @@ -26,7 +26,7 @@ Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "RuntimePermissions", "resource://gre/modules/RuntimePermissions.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", "resource://gre/modules/Messaging.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm"); // ----------------------------------------------------------------------- @@ -239,7 +239,7 @@ HelperAppLauncherDialog.prototype = { mimeType = ContentAreaUtils.getMIMETypeForURI(aLauncher.source) || ""; } - Messaging.sendRequest({ + EventDispatcher.instance.sendRequest({ 'type': 'Download:AndroidDownloadManager', 'uri': aLauncher.source.spec, 'mimeType': mimeType, diff --git a/mobile/android/components/SessionStore.js b/mobile/android/components/SessionStore.js index b0468ed2861d..8d5bc1fdf01c 100644 --- a/mobile/android/components/SessionStore.js +++ b/mobile/android/components/SessionStore.js @@ -311,7 +311,8 @@ SessionStore.prototype = { this._openTabs(data); if (data.shouldNotifyTabsOpenedToJava) { - Messaging.sendRequest({ + let window = Services.wm.getMostRecentWindow("navigator:browser"); + window.WindowEventDispatcher.sendRequest({ type: "Tabs:TabsOpened" }); } @@ -979,7 +980,8 @@ SessionStore.prototype = { // If we have private data, send it to Java; otherwise, send null to // indicate that there is no private data - Messaging.sendRequest({ + let window = Services.wm.getMostRecentWindow("navigator:browser"); + window.WindowEventDispatcher.sendRequest({ type: "PrivateBrowsing:Data", session: (privateData.windows.length > 0 && privateData.windows[0].tabs.length > 0) ? JSON.stringify(privateData) : null }); diff --git a/mobile/android/geckoview/src/main/AndroidManifest.xml b/mobile/android/geckoview/src/main/AndroidManifest.xml index a933190ee1ed..adf67239f8b3 100644 --- a/mobile/android/geckoview/src/main/AndroidManifest.xml +++ b/mobile/android/geckoview/src/main/AndroidManifest.xml @@ -36,21 +36,23 @@ - - - + + + + - - + + + diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java index b7a209b9351a..47f9a1b181dd 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java @@ -106,6 +106,9 @@ public final class EventDispatcher extends JNIObject { try { synchronized (listenersMap) { for (final String event : events) { + if (event == null) { + continue; + } List listeners = listenersMap.get(event); if (listeners == null) { // Java doesn't let us put Class> as the type for listType. @@ -156,6 +159,9 @@ public final class EventDispatcher extends JNIObject { final String[] events) { synchronized (listenersMap) { for (final String event : events) { + if (event == null) { + continue; + } List listeners = listenersMap.get(event); if ((listeners == null || !listeners.remove(listener)) && !AppConstants.RELEASE_OR_BETA) { diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java index 8d4c0fb2a1be..63bc2e68ba68 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java @@ -5,10 +5,10 @@ package org.mozilla.gecko; -import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.AppConstants.Versions; +import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.util.UIAsyncTask; @@ -34,7 +34,7 @@ public class GeckoAccessibility { private static boolean sEnabled; // Used to store the JSON message and populate the event later in the code path. - private static JSONObject sHoverEnter; + private static GeckoBundle sHoverEnter; private static AccessibilityNodeInfo sVirtualCursorNode; private static int sCurrentNode; @@ -76,32 +76,32 @@ public class GeckoAccessibility { }.execute(); } - private static void populateEventFromJSON (AccessibilityEvent event, JSONObject message) { - final JSONArray textArray = message.optJSONArray("text"); + private static void populateEventFromJSON (AccessibilityEvent event, GeckoBundle message) { + final String[] textArray = message.getStringArray("text"); if (textArray != null) { - for (int i = 0; i < textArray.length(); i++) - event.getText().add(textArray.optString(i)); + for (int i = 0; i < textArray.length; i++) + event.getText().add(textArray[i]); } - event.setContentDescription(message.optString("description")); - event.setEnabled(message.optBoolean("enabled", true)); - event.setChecked(message.optBoolean("checked")); - event.setPassword(message.optBoolean("password")); - event.setAddedCount(message.optInt("addedCount", -1)); - event.setRemovedCount(message.optInt("removedCount", -1)); - event.setFromIndex(message.optInt("fromIndex", -1)); - event.setItemCount(message.optInt("itemCount", -1)); - event.setCurrentItemIndex(message.optInt("currentItemIndex", -1)); - event.setBeforeText(message.optString("beforeText")); - event.setToIndex(message.optInt("toIndex", -1)); - event.setScrollable(message.optBoolean("scrollable")); - event.setScrollX(message.optInt("scrollX", -1)); - event.setScrollY(message.optInt("scrollY", -1)); - event.setMaxScrollX(message.optInt("maxScrollX", -1)); - event.setMaxScrollY(message.optInt("maxScrollY", -1)); + event.setContentDescription(message.getString("description")); + event.setEnabled(message.getBoolean("enabled", true)); + event.setChecked(message.getBoolean("checked")); + event.setPassword(message.getBoolean("password")); + event.setAddedCount(message.getInt("addedCount", -1)); + event.setRemovedCount(message.getInt("removedCount", -1)); + event.setFromIndex(message.getInt("fromIndex", -1)); + event.setItemCount(message.getInt("itemCount", -1)); + event.setCurrentItemIndex(message.getInt("currentItemIndex", -1)); + event.setBeforeText(message.getString("beforeText")); + event.setToIndex(message.getInt("toIndex", -1)); + event.setScrollable(message.getBoolean("scrollable")); + event.setScrollX(message.getInt("scrollX", -1)); + event.setScrollY(message.getInt("scrollY", -1)); + event.setMaxScrollX(message.getInt("maxScrollX", -1)); + event.setMaxScrollY(message.getInt("maxScrollY", -1)); } - private static void sendDirectAccessibilityEvent(int eventType, JSONObject message) { + private static void sendDirectAccessibilityEvent(int eventType, GeckoBundle message) { final Context context = GeckoAppShell.getApplicationContext(); final AccessibilityEvent accEvent = AccessibilityEvent.obtain(eventType); accEvent.setClassName(GeckoAccessibility.class.getName()); @@ -120,11 +120,11 @@ public class GeckoAccessibility { return sEnabled; } - public static void sendAccessibilityEvent (final JSONObject message) { + public static void sendAccessibilityEvent(final GeckoBundle message) { if (!sEnabled) return; - final int eventType = message.optInt("eventType", -1); + final int eventType = message.getInt("eventType", -1); if (eventType < 0) { Log.e(LOGTAG, "No accessibility event type provided"); return; @@ -133,11 +133,11 @@ public class GeckoAccessibility { sendAccessibilityEvent(message, eventType); } - public static void sendAccessibilityEvent (final JSONObject message, final int eventType) { + public static void sendAccessibilityEvent(final GeckoBundle message, final int eventType) { if (!sEnabled) return; - final String exitView = message.optString("exitView"); + final String exitView = message.getString("exitView"); if (exitView.equals("moveNext")) { sCurrentNode = VIRTUAL_ENTRY_POINT_AFTER; } else if (exitView.equals("movePrevious")) { @@ -162,29 +162,30 @@ public class GeckoAccessibility { if (view == null) return; - if (sVirtualCursorNode == null) + if (sVirtualCursorNode == null) { sVirtualCursorNode = AccessibilityNodeInfo.obtain(view, VIRTUAL_CURSOR_POSITION); - sVirtualCursorNode.setEnabled(message.optBoolean("enabled", true)); - sVirtualCursorNode.setClickable(message.optBoolean("clickable")); - sVirtualCursorNode.setCheckable(message.optBoolean("checkable")); - sVirtualCursorNode.setChecked(message.optBoolean("checked")); - sVirtualCursorNode.setPassword(message.optBoolean("password")); + } + sVirtualCursorNode.setEnabled(message.getBoolean("enabled", true)); + sVirtualCursorNode.setClickable(message.getBoolean("clickable")); + sVirtualCursorNode.setCheckable(message.getBoolean("checkable")); + sVirtualCursorNode.setChecked(message.getBoolean("checked")); + sVirtualCursorNode.setPassword(message.getBoolean("password")); - final JSONArray textArray = message.optJSONArray("text"); + final String[] textArray = message.getStringArray("text"); StringBuilder sb = new StringBuilder(); - if (textArray != null && textArray.length() > 0) { - sb.append(textArray.optString(0)); - for (int i = 1; i < textArray.length(); i++) { - sb.append(" ").append(textArray.optString(i)); + if (textArray != null && textArray.length > 0) { + sb.append(textArray[0]); + for (int i = 1; i < textArray.length; i++) { + sb.append(" ").append(textArray[i]); } sVirtualCursorNode.setText(sb.toString()); } - sVirtualCursorNode.setContentDescription(message.optString("description")); + sVirtualCursorNode.setContentDescription(message.getString("description")); - JSONObject bounds = message.optJSONObject("bounds"); + final GeckoBundle bounds = message.getBundle("bounds"); if (bounds != null) { - Rect relativeBounds = new Rect(bounds.optInt("left"), bounds.optInt("top"), - bounds.optInt("right"), bounds.optInt("bottom")); + Rect relativeBounds = new Rect(bounds.getInt("left"), bounds.getInt("top"), + bounds.getInt("right"), bounds.getInt("bottom")); sVirtualCursorNode.setBoundsInParent(relativeBounds); int[] locationOnScreen = new int[2]; view.getLocationOnScreen(locationOnScreen); @@ -193,10 +194,10 @@ public class GeckoAccessibility { sVirtualCursorNode.setBoundsInScreen(screenBounds); } - final JSONObject braille = message.optJSONObject("brailleOutput"); + final GeckoBundle braille = message.getBundle("brailleOutput"); if (braille != null) { - sendBrailleText(view, braille.optString("text"), - braille.optInt("selectionStart"), braille.optInt("selectionEnd")); + sendBrailleText(view, braille.getString("text"), + braille.getInt("selectionStart"), braille.getInt("selectionEnd")); } if (eventType == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER) { diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java index c566098e0e52..14efc005c1bc 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java @@ -551,13 +551,7 @@ public class GeckoThread extends Thread { // And... we're done. setState(State.EXITED); - try { - final JSONObject msg = new JSONObject(); - msg.put("type", "Gecko:Exited"); - EventDispatcher.getInstance().dispatchEvent(msg, null); - } catch (final JSONException e) { - Log.e(LOGTAG, "unable to dispatch event", e); - } + EventDispatcher.getInstance().dispatch("Gecko:Exited", null); // Remove pumpMessageLoop() idle handler Looper.myQueue().removeIdleHandler(idleHandler); diff --git a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java index 071f7ed25e9c..61dd052eb10e 100644 --- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java +++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java @@ -29,7 +29,7 @@ public class GeckoViewActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setGeckoInterface(new BaseGeckoInterface(getApplicationContext())); + setGeckoInterface(new BaseGeckoInterface(this)); setContentView(R.layout.geckoview_activity); diff --git a/mobile/android/modules/LightweightThemeConsumer.jsm b/mobile/android/modules/LightweightThemeConsumer.jsm index 3d3ca4c0b7ed..4b2c683edb1f 100644 --- a/mobile/android/modules/LightweightThemeConsumer.jsm +++ b/mobile/android/modules/LightweightThemeConsumer.jsm @@ -8,6 +8,10 @@ var Ci = Components.interfaces; Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm"); +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", + "resource://gre/modules/Messaging.jsm"); function LightweightThemeConsumer(aDocument) { this._doc = aDocument; @@ -39,6 +43,6 @@ LightweightThemeConsumer.prototype = { let msg = active ? { type: "LightweightTheme:Update", data: aData } : { type: "LightweightTheme:Disable" }; - Services.androidBridge.handleGeckoMessage(msg); + EventDispatcher.instance.sendRequest(msg); } } diff --git a/mobile/android/modules/RuntimePermissions.jsm b/mobile/android/modules/RuntimePermissions.jsm index 42d8024b1a60..7df65ebadfef 100644 --- a/mobile/android/modules/RuntimePermissions.jsm +++ b/mobile/android/modules/RuntimePermissions.jsm @@ -8,6 +8,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components; this.EXPORTED_SYMBOLS = ["RuntimePermissions"]; +Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm"); @@ -36,6 +37,7 @@ var RuntimePermissions = { permissions: permissions }; - return Messaging.sendRequestForResult(msg); + let window = Services.wm.getMostRecentWindow("navigator:browser"); + return window.WindowEventDispatcher.sendRequestForResult(msg); } -}; \ No newline at end of file +}; diff --git a/mobile/android/modules/Sanitizer.jsm b/mobile/android/modules/Sanitizer.jsm index 014a8968839c..126707754db3 100644 --- a/mobile/android/modules/Sanitizer.jsm +++ b/mobile/android/modules/Sanitizer.jsm @@ -13,7 +13,6 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/LoadContextInfo.jsm"); Cu.import("resource://gre/modules/FormHistory.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/Downloads.jsm"); Cu.import("resource://gre/modules/osfile.jsm"); @@ -21,6 +20,8 @@ Cu.import("resource://gre/modules/Accounts.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DownloadIntegration", "resource://gre/modules/DownloadIntegration.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", + "resource://gre/modules/Messaging.jsm"); function dump(a) { Services.console.logStringMessage(a); @@ -144,7 +145,7 @@ Sanitizer.prototype = { history: { clear: function () { - return Messaging.sendRequestForResult({ type: "Sanitize:ClearHistory" }) + return EventDispatcher.instance.sendRequestForResult({ type: "Sanitize:ClearHistory" }) .catch(e => Cu.reportError("Java-side history clearing failed: " + e)) .then(function() { try { @@ -170,7 +171,7 @@ Sanitizer.prototype = { searchHistory: { clear: function () { - return Messaging.sendRequestForResult({ type: "Sanitize:ClearHistory", clearSearchHistory: true }) + return EventDispatcher.instance.sendRequestForResult({ type: "Sanitize:ClearHistory", clearSearchHistory: true }) .catch(e => Cu.reportError("Java-side search history clearing failed: " + e)) }, @@ -283,7 +284,7 @@ Sanitizer.prototype = { syncedTabs: { clear: function () { - return Messaging.sendRequestForResult({ type: "Sanitize:ClearSyncedTabs" }) + return EventDispatcher.instance.sendRequestForResult({ type: "Sanitize:ClearSyncedTabs" }) .catch(e => Cu.reportError("Java-side synced tabs clearing failed: " + e)); }, diff --git a/mobile/android/modules/Snackbars.jsm b/mobile/android/modules/Snackbars.jsm index 066a28c56789..aa02b86afa11 100644 --- a/mobile/android/modules/Snackbars.jsm +++ b/mobile/android/modules/Snackbars.jsm @@ -10,7 +10,7 @@ this.EXPORTED_SYMBOLS = ["Snackbars"]; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", "resource://gre/modules/Messaging.jsm"); const LENGTH_INDEFINITE = -2; const LENGTH_LONG = 0; @@ -45,9 +45,9 @@ var Snackbars = { msg.action.label = aOptions.action.label; } - Messaging.sendRequestForResult(msg).then(result => aOptions.action.callback()); + EventDispatcher.instance.sendRequestForResult(msg).then(result => aOptions.action.callback()); } else { - Messaging.sendRequest(msg); + EventDispatcher.instance.sendRequest(msg); } } }; @@ -69,4 +69,4 @@ function migrateToastIfNeeded(aDuration, aOptions) { }; } return [duration, options]; -} \ No newline at end of file +} diff --git a/mobile/android/modules/WebsiteMetadata.jsm b/mobile/android/modules/WebsiteMetadata.jsm index 39af9ddebebb..ad39c0094b27 100644 --- a/mobile/android/modules/WebsiteMetadata.jsm +++ b/mobile/android/modules/WebsiteMetadata.jsm @@ -10,7 +10,7 @@ this.EXPORTED_SYMBOLS = ["WebsiteMetadata"]; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", "resource://gre/modules/Messaging.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); var WebsiteMetadata = { @@ -32,10 +32,11 @@ var WebsiteMetadata = { let msg = { type: 'Website:Metadata', location: doc.location.href, - metadata: metadata, + hasImage: metadata.image_url && metadata.image_url !== "", + metadata: JSON.stringify(metadata), }; - Messaging.sendRequest(msg); + EventDispatcher.instance.sendRequest(msg); }); } }; diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/Actions.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/Actions.java index 05e6bfa52a37..bcdf6530aad6 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/Actions.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/Actions.java @@ -3,6 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko; + +import org.mozilla.gecko.util.GeckoBundle; + import android.database.Cursor; public interface Actions { @@ -26,12 +29,21 @@ public interface Actions { /** Blocks until the event has been received and returns data associated with the event. */ public String blockForEventData(); + /** Blocks until the event has been received and returns data associated with the event. */ + public GeckoBundle blockForBundle(); + /** * Blocks until the event has been received, or until the timeout has been exceeded. * Returns the data associated with the event, if applicable. */ public String blockForEventDataWithTimeout(long millis); + /** + * Blocks until the event has been received, or until the timeout has been exceeded. + * Returns the data associated with the event, if applicable. + */ + public GeckoBundle blockForBundleWithTimeout(long millis); + /** Polls to see if the event has been received. Once this returns true, subsequent calls will also return true. */ public boolean eventReceived(); @@ -44,6 +56,13 @@ public interface Actions { public void blockUntilClear(long millis); } + public enum EventType { + JSON, + GECKO, + UI, + BACKGROUND + } + /** * Sends an event to Gecko. * @@ -51,6 +70,22 @@ public interface Actions { */ void sendGeckoEvent(String geckoEvent, String data); + /** + * Sends an event to the global EventDispatcher instance. + * + * @param event The event type + * @param data Data associated with the event + */ + void sendGlobalEvent(String event, GeckoBundle data); + + /** + * Sends an event to the GeckoApp-specific EventDispatcher instance. + * + * @param event The event type + * @param data Data associated with the event + */ + void sendWindowEvent(String event, GeckoBundle data); + public interface PrefWaiter { boolean isFinished(); void waitForFinish(); @@ -94,6 +129,26 @@ public interface Actions { */ RepeatedEventExpecter expectGeckoEvent(String geckoEvent); + /** + * Listens for an event on the global EventDispatcher instance. + * The returned object can be used to test if the event has been + * received. Note that only one event is listened for. + * + * @param type The thread type for the event + * @param event The name for the event + */ + RepeatedEventExpecter expectGlobalEvent(EventType type, String event); + + /** + * Listens for an event on the global EventDispatcher instance. + * The returned object can be used to test if the event has been + * received. Note that only one event is listened for. + * + * @param type The thread type for the event + * @param event The name for the event + */ + RepeatedEventExpecter expectWindowEvent(EventType type, String event); + /** * Listens for a paint event. Note that calling expectPaint() will * invalidate the event expecters returned from any previous calls diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/FennecNativeActions.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/FennecNativeActions.java index 7faccdf43b2f..1a7726617d43 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/FennecNativeActions.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/FennecNativeActions.java @@ -15,6 +15,9 @@ import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.LayerView.DrawListener; import org.mozilla.gecko.mozglue.GeckoLoader; import org.mozilla.gecko.sqlite.SQLiteBridge; +import org.mozilla.gecko.util.BundleEventListener; +import org.mozilla.gecko.util.EventCallback; +import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.GeckoEventListener; import android.app.Activity; @@ -46,35 +49,70 @@ public class FennecNativeActions implements Actions { private volatile boolean mIsRegistered; + private final EventDispatcher mDispatcher; + private final EventType mType; private final String mGeckoEvent; + private final BlockingQueue mEventDataQueue; private final GeckoEventListener mListener; + private final BundleEventListener mBundleListener; private volatile boolean mEventEverReceived; - private String mEventData; - private BlockingQueue mEventDataQueue; + private Object mEventData; - GeckoEventExpecter(final String geckoEvent) { + GeckoEventExpecter(final EventDispatcher dispatcher, final EventType type, + final String geckoEvent) { if (TextUtils.isEmpty(geckoEvent)) { throw new IllegalArgumentException("geckoEvent must not be empty"); } + mDispatcher = dispatcher; + mType = type; mGeckoEvent = geckoEvent; - mEventDataQueue = new LinkedBlockingQueue(); + mEventDataQueue = new LinkedBlockingQueue<>(); + mIsRegistered = true; - final GeckoEventExpecter expecter = this; - mListener = new GeckoEventListener() { + if (type == EventType.JSON) { + mListener = new GeckoEventListener() { + @Override + public void handleMessage(final String event, final JSONObject message) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, + "handleMessage called for: " + event + "; expecting: " + mGeckoEvent); + mAsserter.is(event, mGeckoEvent, + "Given message occurred for registered event: " + message); + + notifyOfEvent(message.toString()); + } + }; + mBundleListener = null; + dispatcher.registerGeckoThreadListener(mListener, geckoEvent); + return; + } + + mListener = null; + mBundleListener = new BundleEventListener() { @Override - public void handleMessage(final String event, final JSONObject message) { + public void handleMessage(final String event, final GeckoBundle message, + final EventCallback callback) { FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, "handleMessage called for: " + event + "; expecting: " + mGeckoEvent); - mAsserter.is(event, mGeckoEvent, "Given message occurred for registered event: " + message); + mAsserter.is(event, mGeckoEvent, + "Given message occurred for registered event: " + message); - expecter.notifyOfEvent(message); + // Because we cannot put null into the queue, we have to use an empty + // bundle if we don't have a message. + notifyOfEvent(message != null ? message : new GeckoBundle(0)); } }; - EventDispatcher.getInstance().registerGeckoThreadListener(mListener, mGeckoEvent); - mIsRegistered = true; + if (type == EventType.GECKO) { + dispatcher.registerGeckoThreadListener(mBundleListener, geckoEvent); + } else if (type == EventType.UI) { + dispatcher.registerUiThreadListener(mBundleListener, geckoEvent); + } else if (type == EventType.BACKGROUND) { + dispatcher.registerBackgroundThreadListener(mBundleListener, geckoEvent); + } else { + throw new IllegalArgumentException("Unsupported thread type"); + } } public void blockForEvent() { @@ -143,12 +181,22 @@ public class FennecNativeActions implements Actions { public String blockForEventData() { blockForEvent(); - return mEventData; + return (String) mEventData; } public String blockForEventDataWithTimeout(long millis) { blockForEvent(millis, false); - return mEventData; + return (String) mEventData; + } + + public GeckoBundle blockForBundle() { + blockForEvent(); + return (GeckoBundle) mEventData; + } + + public GeckoBundle blockForBundleWithTimeout(long millis) { + blockForEvent(millis, false); + return (GeckoBundle) mEventData; } public void unregisterListener() { @@ -159,7 +207,17 @@ public class FennecNativeActions implements Actions { FennecNativeDriver.log(LogLevel.INFO, "EventExpecter: no longer listening for " + mGeckoEvent); - EventDispatcher.getInstance().unregisterGeckoThreadListener(mListener, mGeckoEvent); + if (mType == EventType.JSON) { + mDispatcher.unregisterGeckoThreadListener(mListener, mGeckoEvent); + } else if (mType == EventType.GECKO) { + mDispatcher.unregisterGeckoThreadListener(mBundleListener, mGeckoEvent); + } else if (mType == EventType.UI) { + mDispatcher.unregisterUiThreadListener(mBundleListener, mGeckoEvent); + } else if (mType == EventType.BACKGROUND) { + mDispatcher.unregisterBackgroundThreadListener(mBundleListener, mGeckoEvent); + } else { + throw new IllegalArgumentException("Unsupported thread type"); + } mIsRegistered = false; } @@ -167,30 +225,48 @@ public class FennecNativeActions implements Actions { return mEventEverReceived; } - void notifyOfEvent(final JSONObject message) { + /* package */ void notifyOfEvent(final Object data) { FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, "received event " + mGeckoEvent); mEventEverReceived = true; try { - mEventDataQueue.put(message.toString()); + mEventDataQueue.put(data); } catch (InterruptedException e) { FennecNativeDriver.log(LogLevel.ERROR, - "EventExpecter dropped event: " + message.toString(), e); + "EventExpecter dropped event: " + data, e); } } } public RepeatedEventExpecter expectGeckoEvent(final String geckoEvent) { FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, "waiting for " + geckoEvent); - return new GeckoEventExpecter(geckoEvent); + return new GeckoEventExpecter(EventDispatcher.getInstance(), EventType.JSON, geckoEvent); + } + + public RepeatedEventExpecter expectGlobalEvent(final EventType type, final String geckoEvent) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, "waiting for " + geckoEvent); + return new GeckoEventExpecter(EventDispatcher.getInstance(), type, geckoEvent); + } + + public RepeatedEventExpecter expectWindowEvent(final EventType type, final String geckoEvent) { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, "waiting for " + geckoEvent); + return new GeckoEventExpecter(GeckoApp.getEventDispatcher(), type, geckoEvent); } public void sendGeckoEvent(final String geckoEvent, final String data) { GeckoAppShell.notifyObservers(geckoEvent, data); } + public void sendGlobalEvent(final String event, final GeckoBundle data) { + EventDispatcher.getInstance().dispatch(event, data); + } + + public void sendWindowEvent(final String event, final GeckoBundle data) { + GeckoApp.getEventDispatcher().dispatch(event, data); + } + public static final class PrefProxy implements PrefsHelper.PrefHandler, PrefWaiter { public static final int MAX_WAIT_MS = 180000; @@ -366,6 +442,14 @@ public class FennecNativeActions implements Actions { return null; } + public GeckoBundle blockForBundle() { + throw new UnsupportedOperationException(); + } + + public GeckoBundle blockForBundleWithTimeout(long millis) { + throw new UnsupportedOperationException(); + } + public synchronized boolean eventReceived() { return mPaintDone; } diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java index 79eda6426e2d..e46b4d63df75 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java @@ -68,7 +68,8 @@ abstract class BaseTest extends BaseRobocopTest { protected void blockForDelayedStartup() { try { - Actions.EventExpecter delayedStartupExpector = mActions.expectGeckoEvent("Gecko:DelayedStartup"); + Actions.EventExpecter delayedStartupExpector = + mActions.expectGlobalEvent(Actions.EventType.UI, "Gecko:DelayedStartup"); delayedStartupExpector.blockForEvent(GECKO_READY_WAIT_MS, true); delayedStartupExpector.unregisterListener(); } catch (Exception e) { @@ -78,7 +79,8 @@ abstract class BaseTest extends BaseRobocopTest { protected void blockForGeckoReady() { try { - Actions.EventExpecter geckoReadyExpector = mActions.expectGeckoEvent("Gecko:Ready"); + Actions.EventExpecter geckoReadyExpector = + mActions.expectGlobalEvent(Actions.EventType.GECKO, "Gecko:Ready"); if (!GeckoThread.isRunning()) { geckoReadyExpector.blockForEvent(GECKO_READY_WAIT_MS, true); } diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/ContentContextMenuTest.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/ContentContextMenuTest.java index 5a1d09f8c921..a50cc68dfb5b 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/ContentContextMenuTest.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/ContentContextMenuTest.java @@ -48,7 +48,7 @@ abstract class ContentContextMenuTest extends PixelTest { if (!mSolo.searchText(contextMenuOption)) { openWebContentContextMenu(contextMenuOption); // Open the context menu if it is not already } - Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + Actions.EventExpecter tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); mSolo.clickOnText(contextMenuOption); tabEventExpecter.blockForEvent(); tabEventExpecter.unregisterListener(); diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/helpers/GeckoHelper.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/helpers/GeckoHelper.java index cd75b7255d47..cfca653001bb 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/helpers/GeckoHelper.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/helpers/GeckoHelper.java @@ -26,7 +26,7 @@ public final class GeckoHelper { } public static void blockForReady() { - blockForEvent("Gecko:Ready"); + blockForEvent(Actions.EventType.GECKO, "Gecko:Ready"); } /** @@ -34,11 +34,11 @@ public final class GeckoHelper { * first page load. */ public static void blockForDelayedStartup() { - blockForEvent("Gecko:DelayedStartup"); + blockForEvent(Actions.EventType.UI, "Gecko:DelayedStartup"); } - private static void blockForEvent(final String eventName) { - final EventExpecter eventExpecter = sActions.expectGeckoEvent(eventName); + private static void blockForEvent(final Actions.EventType type, final String eventName) { + final EventExpecter eventExpecter = sActions.expectGlobalEvent(type, eventName); if (!GeckoThread.isRunning()) { eventExpecter.blockForEvent(); diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAboutPage.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAboutPage.java index 6a00acd96585..3f037ee9a638 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAboutPage.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAboutPage.java @@ -29,7 +29,7 @@ public class testAboutPage extends PixelTest { verifyUrlInContentDescription(url); // Set up listeners to catch the page load we're about to do. - Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + Actions.EventExpecter tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded"); selectSettingsItem(mStringHelper.MOZILLA_SECTION_LABEL, mStringHelper.ABOUT_LABEL); diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAddonManager.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAddonManager.java index 4256d93c4d24..8be9d8960bbf 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAddonManager.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAddonManager.java @@ -29,7 +29,7 @@ public class testAddonManager extends PixelTest { selectMenuItem(mStringHelper.ADDONS_LABEL); // Set up listeners to catch the page load we're about to do - tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded"); // Wait for the new tab and page to load @@ -50,7 +50,7 @@ public class testAddonManager extends PixelTest { verifyUrlBarTitle(aboutAddonsURL); // Setup wait for tab to spawn and load - tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded"); // Open a new tab diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testBookmarksPanel.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testBookmarksPanel.java index a7e9505da452..24f7b4a05b61 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testBookmarksPanel.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testBookmarksPanel.java @@ -4,17 +4,16 @@ package org.mozilla.gecko.tests; -import org.json.JSONException; -import org.json.JSONObject; import org.mozilla.gecko.Actions; import org.mozilla.gecko.Element; import org.mozilla.gecko.R; +import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.StringUtils; public class testBookmarksPanel extends AboutHomeTest { public void testBookmarksPanel() { final String BOOKMARK_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_BLANK_PAGE_01_URL); - JSONObject data = null; + GeckoBundle data = null; // Make sure our default bookmarks are loaded. // Technically this will race with the check below. @@ -40,43 +39,27 @@ public class testBookmarksPanel extends AboutHomeTest { // Test that "Open in New Tab" works final Element tabCount = mDriver.findElement(getActivity(), R.id.tabs_counter); final int tabCountInt = Integer.parseInt(tabCount.getText()); - Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + Actions.EventExpecter tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); mSolo.clickOnText(mStringHelper.BOOKMARK_CONTEXT_MENU_ITEMS[0]); - try { - data = new JSONObject(tabEventExpecter.blockForEventData()); - } catch (JSONException e) { - mAsserter.ok(false, "exception getting event data", e.toString()); - } + + data = tabEventExpecter.blockForBundle(); tabEventExpecter.unregisterListener(); mAsserter.ok(mSolo.searchText(mStringHelper.TITLE_PLACE_HOLDER), "Checking that the tab is not changed", "The tab was not changed"); // extra check here on the Tab:Added message to be sure the right tab opened - int tabID = 0; - try { - mAsserter.is(mStringHelper.ABOUT_FIREFOX_URL, data.getString("uri"), "Checking tab uri"); - tabID = data.getInt("tabID"); - } catch (JSONException e) { - mAsserter.ok(false, "exception accessing event data", e.toString()); - } + mAsserter.is(mStringHelper.ABOUT_FIREFOX_URL, data.getString("uri"), "Checking tab uri"); // close tab so about:firefox can be selected again - closeTab(tabID); + closeTab(data.getInt("tabID")); // Test that "Open in Private Tab" works openBookmarkContextMenu(mStringHelper.DEFAULT_BOOKMARKS_URLS[0]); - tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); mSolo.clickOnText(mStringHelper.BOOKMARK_CONTEXT_MENU_ITEMS[1]); - try { - data = new JSONObject(tabEventExpecter.blockForEventData()); - } catch (JSONException e) { - mAsserter.ok(false, "exception getting event data", e.toString()); - } + + data = tabEventExpecter.blockForBundle(); tabEventExpecter.unregisterListener(); mAsserter.ok(mSolo.searchText(mStringHelper.TITLE_PLACE_HOLDER), "Checking that the tab is not changed", "The tab was not changed"); // extra check here on the Tab:Added message to be sure the right tab opened, again - try { - mAsserter.is(mStringHelper.ABOUT_FIREFOX_URL, data.getString("uri"), "Checking tab uri"); - } catch (JSONException e) { - mAsserter.ok(false, "exception accessing event data", e.toString()); - } + mAsserter.is(mStringHelper.ABOUT_FIREFOX_URL, data.getString("uri"), "Checking tab uri"); // Test that "Edit" works String[] editedBookmarkValues = new String[] { "New bookmark title", "www.NewBookmark.url", "newBookmarkKeyword" }; diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDoorHanger.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDoorHanger.java index 2c3feb3a8862..7c52779602ca 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDoorHanger.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDoorHanger.java @@ -7,10 +7,8 @@ package org.mozilla.gecko.tests; import android.widget.CheckBox; import android.view.View; import com.robotium.solo.Condition; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; import org.mozilla.gecko.Actions; +import org.mozilla.gecko.util.GeckoBundle; /* This test will test if doorhangers are displayed and dismissed The test will test: @@ -133,7 +131,7 @@ public class testDoorHanger extends BaseTest { mAsserter.is(mSolo.searchText(mStringHelper.POPUP_MESSAGE), true, "Popup blocker is displayed"); // Wait for the popup to be shown. - Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); + Actions.EventExpecter tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); waitForCheckBox(); mSolo.clickOnCheckBox(0); @@ -141,18 +139,14 @@ public class testDoorHanger extends BaseTest { waitForTextDismissed(mStringHelper.POPUP_MESSAGE); mAsserter.is(mSolo.searchText(mStringHelper.POPUP_MESSAGE), false, "Popup blocker is hidden when popup allowed"); - try { - final JSONObject data = new JSONObject(tabEventExpecter.blockForEventData()); + final GeckoBundle data = tabEventExpecter.blockForBundle(); - // Check to make sure the popup window was opened. - mAsserter.is("data:text/plain;charset=utf-8,a", data.getString("uri"), "Checking popup URL"); + // Check to make sure the popup window was opened. + mAsserter.is("data:text/plain;charset=utf-8,a", data.getString("uri"), "Checking popup URL"); - // Close the popup window. - closeTab(data.getInt("tabID")); + // Close the popup window. + closeTab(data.getInt("tabID")); - } catch (JSONException e) { - mAsserter.ok(false, "exception getting event data", e.toString()); - } tabEventExpecter.unregisterListener(); // Load page with popup diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPrivateBrowsing.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPrivateBrowsing.java index 461e95aa7242..1707d9b493f9 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPrivateBrowsing.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPrivateBrowsing.java @@ -6,10 +6,9 @@ package org.mozilla.gecko.tests; import java.util.ArrayList; -import org.json.JSONException; -import org.json.JSONObject; import org.mozilla.gecko.Actions; import org.mozilla.gecko.Tabs; +import org.mozilla.gecko.util.GeckoBundle; /** * The test loads a new private tab and loads a page with a big link on it @@ -27,7 +26,7 @@ public class testPrivateBrowsing extends ContentContextMenuTest { blockForGeckoReady(); - Actions.EventExpecter tabExpecter = mActions.expectGeckoEvent("Tab:Added"); + Actions.EventExpecter tabExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); Actions.EventExpecter contentExpecter = mActions.expectGeckoEvent("Content:PageShow"); tabs.loadUrl(bigLinkUrl, Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_PRIVATE); tabExpecter.blockForEvent(); @@ -46,18 +45,19 @@ public class testPrivateBrowsing extends ContentContextMenuTest { mAsserter.ok(!mSolo.searchText(mStringHelper.CONTEXT_MENU_ITEMS_IN_NORMAL_TAB[0]), "Checking that 'Open Link in New Tab' is not displayed in the context menu", "'Open Link in New Tab' is not displayed in the context menu"); // Open the link in a new private tab and check that it is private - tabExpecter = mActions.expectGeckoEvent("Tab:Added"); + tabExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); contentExpecter = mActions.expectGeckoEvent("Content:PageShow"); mSolo.clickOnText(mStringHelper.CONTEXT_MENU_ITEMS_IN_PRIVATE_TAB[0]); - String eventData = tabExpecter.blockForEventData(); + + final GeckoBundle eventData = tabExpecter.blockForBundle(); tabExpecter.unregisterListener(); contentExpecter.blockForEvent(); contentExpecter.unregisterListener(); - mAsserter.ok(isTabPrivate(eventData), "Checking if the new tab opened from the context menu was a private tab", "The tab was a private tab"); + mAsserter.ok(eventData.getBoolean("isPrivate"), "Checking if the new tab opened from the context menu was a private tab", "The tab was a private tab"); verifyTabCount(2); // Open a normal tab to check later that it was registered in the Firefox Browser History - tabExpecter = mActions.expectGeckoEvent("Tab:Added"); + tabExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added"); contentExpecter = mActions.expectGeckoEvent("Content:PageShow"); tabs.loadUrl(blank2Url, Tabs.LOADURL_NEW_TAB); tabExpecter.blockForEvent(); @@ -76,14 +76,4 @@ public class testPrivateBrowsing extends ContentContextMenuTest { mAsserter.ok(!firefoxHistory.contains(blank1Url), "Check that the link opened in the private tab from the context menu was not saved", blank1Url + " was not added to history"); mAsserter.ok(firefoxHistory.contains(blank2Url), "Check that the link opened in the normal tab was saved", blank2Url + " was added to history"); } - - private boolean isTabPrivate(String eventData) { - try { - JSONObject data = new JSONObject(eventData); - return data.getBoolean("isPrivate"); - } catch (JSONException e) { - mAsserter.ok(false, "Error parsing the event data", e.toString()); - return false; - } - } } diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testRuntimePermissionsAPI.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testRuntimePermissionsAPI.java index df192fc430fc..3cb44df69f14 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testRuntimePermissionsAPI.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testRuntimePermissionsAPI.java @@ -5,13 +5,14 @@ package org.mozilla.gecko.tests; import org.mozilla.gecko.EventDispatcher; +import org.mozilla.gecko.GeckoApp; +import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.EventCallback; -import org.mozilla.gecko.util.NativeEventListener; -import org.mozilla.gecko.util.NativeJSObject; +import org.mozilla.gecko.util.GeckoBundle; import static org.mozilla.gecko.tests.helpers.AssertionHelper.fFail; -public class testRuntimePermissionsAPI extends JavascriptTest implements NativeEventListener { +public class testRuntimePermissionsAPI extends JavascriptTest implements BundleEventListener { public testRuntimePermissionsAPI() { super("testRuntimePermissionsAPI.js"); } @@ -20,18 +21,18 @@ public class testRuntimePermissionsAPI extends JavascriptTest implements NativeE public void setUp() throws Exception { super.setUp(); - EventDispatcher.getInstance().registerGeckoThreadListener(this, "RuntimePermissions:Prompt"); + GeckoApp.getEventDispatcher().registerUiThreadListener(this, "RuntimePermissions:Prompt"); } @Override public void tearDown() throws Exception { super.tearDown(); - EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "RuntimePermissions:Prompt"); + GeckoApp.getEventDispatcher().unregisterUiThreadListener(this, "RuntimePermissions:Prompt"); } @Override - public void handleMessage(String event, NativeJSObject message, EventCallback callback) { + public void handleMessage(String event, GeckoBundle message, EventCallback callback) { mAsserter.is(event, "RuntimePermissions:Prompt", "Received RuntimePermissions:Prompt event"); try { diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testSnackbarAPI.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testSnackbarAPI.java index 893f98a518c9..3533ba05aa32 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testSnackbarAPI.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testSnackbarAPI.java @@ -7,11 +7,11 @@ package org.mozilla.gecko.tests; import static org.mozilla.gecko.tests.helpers.AssertionHelper.fFail; import org.mozilla.gecko.EventDispatcher; +import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.EventCallback; -import org.mozilla.gecko.util.NativeEventListener; -import org.mozilla.gecko.util.NativeJSObject; +import org.mozilla.gecko.util.GeckoBundle; -public class testSnackbarAPI extends JavascriptTest implements NativeEventListener { +public class testSnackbarAPI extends JavascriptTest implements BundleEventListener { // Snackbar.LENGTH_INDEFINITE: To avoid tests depending on the android design support library private static final int SNACKBAR_LENGTH_INDEFINITE = -2; @@ -20,14 +20,19 @@ public class testSnackbarAPI extends JavascriptTest implements NativeEventListen } @Override - public void handleMessage(String event, NativeJSObject message, EventCallback callback) { + public void handleMessage(String event, GeckoBundle message, EventCallback callback) { + if ("Robocop:WaitOnUI".equals(event)) { + callback.sendSuccess(null); + return; + } + mAsserter.is(event, "Snackbar:Show", "Received Snackbar:Show event"); try { mAsserter.is(message.getString("message"), "This is a Snackbar", "Snackbar message"); mAsserter.is(message.getInt("duration"), SNACKBAR_LENGTH_INDEFINITE, "Snackbar duration"); - NativeJSObject action = message.getObject("action"); + GeckoBundle action = message.getBundle("action"); mAsserter.is(action.getString("label"), "Click me", "Snackbar action label"); @@ -40,13 +45,15 @@ public class testSnackbarAPI extends JavascriptTest implements NativeEventListen public void setUp() throws Exception { super.setUp(); - EventDispatcher.getInstance().registerGeckoThreadListener(this, "Snackbar:Show"); + EventDispatcher.getInstance().registerUiThreadListener(this, "Snackbar:Show"); + EventDispatcher.getInstance().registerUiThreadListener(this, "Robocop:WaitOnUI"); } @Override public void tearDown() throws Exception { super.tearDown(); - EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "Snackbar:Show"); + EventDispatcher.getInstance().unregisterUiThreadListener(this, "Snackbar:Show"); + EventDispatcher.getInstance().unregisterUiThreadListener(this, "Robocop:WaitOnUI"); } } diff --git a/mobile/android/tests/browser/robocop/testSnackbarAPI.js b/mobile/android/tests/browser/robocop/testSnackbarAPI.js index 1031528dfe9b..28e6417f45fe 100644 --- a/mobile/android/tests/browser/robocop/testSnackbarAPI.js +++ b/mobile/android/tests/browser/robocop/testSnackbarAPI.js @@ -8,6 +8,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", "resource://gre/modules/Messaging.jsm"); add_task(function* test_snackbar_api() { Snackbars.show("This is a Snackbar", Snackbars.LENGTH_INDEFINITE, { @@ -16,6 +17,10 @@ add_task(function* test_snackbar_api() { callback: function () {} } }); + + yield EventDispatcher.instance.sendRequestForResult({ + type: "Robocop:WaitOnUI" + }); }); run_next_test(); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 4ab8a0801a08..5b0dff2be175 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2125,7 +2125,7 @@ pref("intl.hyphenation-alias.nb-*", "nb"); pref("intl.hyphenation-alias.nn-*", "nn"); pref("font.name.serif.x-math", "Latin Modern Math"); -pref("font.name-list.serif.x-math", "Latin Modern Math, XITS Math, Cambria Math, Libertinus Math, DejaVu Math TeX Gyre, TeX Gyre Bonum Math, TeX Gyre Pagella Math, TeX Gyre Schola, TeX Gyre Termes Math, STIX Math, Asana Math, STIXGeneral, DejaVu Serif, DejaVu Sans, serif"); +pref("font.name-list.serif.x-math", "Latin Modern Math, STIX Two Math, XITS Math, Cambria Math, Libertinus Math, DejaVu Math TeX Gyre, TeX Gyre Bonum Math, TeX Gyre Pagella Math, TeX Gyre Schola, TeX Gyre Termes Math, STIX Math, Asana Math, STIXGeneral, DejaVu Serif, DejaVu Sans, serif"); pref("font.name.sans-serif.x-math", "sans-serif"); pref("font.name.monospace.x-math", "monospace"); @@ -3379,7 +3379,7 @@ pref("font.minimum-size.th", 10); pref("font.default.x-devanagari", "sans-serif"); pref("font.name.serif.x-math", "Latin Modern Math"); // We have special support for Monotype Symbol on Windows. -pref("font.name-list.serif.x-math", "Latin Modern Math, XITS Math, Cambria Math, Libertinus Math, DejaVu Math TeX Gyre, TeX Gyre Bonum Math, TeX Gyre Pagella Math, TeX Gyre Schola, TeX Gyre Termes Math, STIX Math, Asana Math, STIXGeneral, DejaVu Serif, DejaVu Sans, Symbol, Times New Roman"); +pref("font.name-list.serif.x-math", "Latin Modern Math, STIX Two Math, XITS Math, Cambria Math, Libertinus Math, DejaVu Math TeX Gyre, TeX Gyre Bonum Math, TeX Gyre Pagella Math, TeX Gyre Schola, TeX Gyre Termes Math, STIX Math, Asana Math, STIXGeneral, DejaVu Serif, DejaVu Sans, Symbol, Times New Roman"); pref("font.name.sans-serif.x-math", "Arial"); pref("font.name.monospace.x-math", "Courier New"); pref("font.name.cursive.x-math", "Comic Sans MS"); @@ -3841,7 +3841,7 @@ pref("font.size.variable.zh-TW", 15); pref("font.name.serif.x-math", "Latin Modern Math"); // Apple's Symbol is Unicode so use it -pref("font.name-list.serif.x-math", "Latin Modern Math, XITS Math, Cambria Math, Libertinus Math, DejaVu Math TeX Gyre, TeX Gyre Bonum Math, TeX Gyre Pagella Math, TeX Gyre Schola, TeX Gyre Termes Math, STIX Math, Asana Math, STIXGeneral, DejaVu Serif, DejaVu Sans, Symbol, Times"); +pref("font.name-list.serif.x-math", "Latin Modern Math, STIX Two Math, XITS Math, Cambria Math, Libertinus Math, DejaVu Math TeX Gyre, TeX Gyre Bonum Math, TeX Gyre Pagella Math, TeX Gyre Schola, TeX Gyre Termes Math, STIX Math, Asana Math, STIXGeneral, DejaVu Serif, DejaVu Sans, Symbol, Times"); pref("font.name.sans-serif.x-math", "Helvetica"); pref("font.name.monospace.x-math", "Courier"); pref("font.name.cursive.x-math", "Apple Chancery"); @@ -4255,7 +4255,7 @@ pref("font.name.monospace.zh-TW", "Fira Mono"); pref("font.name-list.sans-serif.zh-TW", "Fira Sans,Droid Sans Fallback"); pref("font.name.serif.x-math", "Latin Modern Math"); -pref("font.name-list.serif.x-math", "Latin Modern Math, XITS Math, Cambria Math, Libertinus Math, DejaVu Math TeX Gyre, TeX Gyre Bonum Math, TeX Gyre Pagella Math, TeX Gyre Schola, TeX Gyre Termes Math, STIX Math, Asana Math, STIXGeneral, DejaVu Serif, DejaVu Sans, Charis SIL Compact"); +pref("font.name-list.serif.x-math", "Latin Modern Math, STIX Two Math, XITS Math, Cambria Math, Libertinus Math, DejaVu Math TeX Gyre, TeX Gyre Bonum Math, TeX Gyre Pagella Math, TeX Gyre Schola, TeX Gyre Termes Math, STIX Math, Asana Math, STIXGeneral, DejaVu Serif, DejaVu Sans, Charis SIL Compact"); pref("font.name.sans-serif.x-math", "Fira Sans"); pref("font.name.monospace.x-math", "Fira Mono"); @@ -4336,7 +4336,7 @@ pref("font.name-list.sans-serif.zh-TW", "Roboto, Droid Sans, Noto Sans TC, Noto pref("font.name-list.monospace.zh-TW", "Noto Sans Mono CJK TC, Droid Sans Fallback"); pref("font.name.serif.x-math", "Latin Modern Math"); -pref("font.name-list.serif.x-math", "Latin Modern Math, XITS Math, Cambria Math, Libertinus Math, DejaVu Math TeX Gyre, TeX Gyre Bonum Math, TeX Gyre Pagella Math, TeX Gyre Schola, TeX Gyre Termes Math, STIX Math, Asana Math, STIXGeneral, DejaVu Serif, DejaVu Sans, Charis SIL Compact"); +pref("font.name-list.serif.x-math", "Latin Modern Math, STIX Two Math, XITS Math, Cambria Math, Libertinus Math, DejaVu Math TeX Gyre, TeX Gyre Bonum Math, TeX Gyre Pagella Math, TeX Gyre Schola, TeX Gyre Termes Math, STIX Math, Asana Math, STIXGeneral, DejaVu Serif, DejaVu Sans, Charis SIL Compact"); pref("font.name.sans-serif.x-math", "Clear Sans"); pref("font.name.monospace.x-math", "Droid Sans Mono"); diff --git a/netwerk/base/nsSocketTransportService2.cpp b/netwerk/base/nsSocketTransportService2.cpp index 5cfac40a9447..4705a6055d94 100644 --- a/netwerk/base/nsSocketTransportService2.cpp +++ b/netwerk/base/nsSocketTransportService2.cpp @@ -32,6 +32,10 @@ #include "mozilla/WindowsVersion.h" #endif +#ifdef MOZ_TASK_TRACER +#include "GeckoTaskTracer.h" +#endif + namespace mozilla { namespace net { @@ -236,8 +240,13 @@ nsSocketTransportService::DetachSocket(SocketContext *listHead, SocketContext *s MOZ_ASSERT((listHead == mActiveList) || (listHead == mIdleList), "DetachSocket invalid head"); - // inform the handler that this socket is going away - sock->mHandler->OnSocketDetached(sock->mFD); + { +#ifdef MOZ_TASK_TRACER + tasktracer::AutoSourceEvent taskTracerEvent(tasktracer::SourceEventType::SocketIO); +#endif + // inform the handler that this socket is going away + sock->mHandler->OnSocketDetached(sock->mFD); + } mSentBytesCount += sock->mHandler->ByteCountSent(); mReceivedBytesCount += sock->mHandler->ByteCountReceived(); @@ -1131,6 +1140,9 @@ nsSocketTransportService::DoPollIteration(TimeDuration *pollDuration) PRPollDesc &desc = mPollList[i+1]; SocketContext &s = mActiveList[i]; if (n > 0 && desc.out_flags != 0) { +#ifdef MOZ_TASK_TRACER + tasktracer::AutoSourceEvent taskTracerEvent(tasktracer::SourceEventType::SocketIO); +#endif s.mElapsedTime = 0; s.mHandler->OnSocketReady(desc.fd, desc.out_flags); numberOfOnSocketReadyCalls++; @@ -1150,6 +1162,9 @@ nsSocketTransportService::DoPollIteration(TimeDuration *pollDuration) s.mElapsedTime += uint16_t(pollInterval); // check for timeout expiration if (s.mElapsedTime >= s.mHandler->mPollTimeout) { +#ifdef MOZ_TASK_TRACER + tasktracer::AutoSourceEvent taskTracerEvent(tasktracer::SourceEventType::SocketIO); +#endif s.mElapsedTime = 0; s.mHandler->OnSocketReady(desc.fd, -1); numberOfOnSocketReadyCalls++; @@ -1320,6 +1335,9 @@ nsSocketTransportService::NotifyKeepaliveEnabledPrefChange(SocketContext *sock) return; } +#ifdef MOZ_TASK_TRACER + tasktracer::AutoSourceEvent taskTracerEvent(tasktracer::SourceEventType::SocketIO); +#endif sock->mHandler->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref); } diff --git a/netwerk/cache2/CacheIOThread.cpp b/netwerk/cache2/CacheIOThread.cpp index b96f03216c1d..66742679c9ac 100644 --- a/netwerk/cache2/CacheIOThread.cpp +++ b/netwerk/cache2/CacheIOThread.cpp @@ -15,6 +15,11 @@ #include #endif +#ifdef MOZ_TASK_TRACER +#include "GeckoTaskTracer.h" +#include "TracedTaskCommon.h" +#endif + namespace mozilla { namespace net { @@ -320,6 +325,10 @@ nsresult CacheIOThread::DispatchInternal(already_AddRefed aRunnable uint32_t aLevel) { nsCOMPtr runnable(aRunnable); +#ifdef MOZ_TASK_TRACER + runnable = tasktracer::CreateTracedRunnable(runnable.forget()); + (static_cast(runnable.get()))->DispatchTask(); +#endif if (NS_WARN_IF(!runnable)) return NS_ERROR_NULL_POINTER; diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 360a301e291a..7a01753e0755 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -44,6 +44,10 @@ #include "chrome/common/file_descriptor_set_posix.h" #endif +#ifdef MOZ_TASK_TRACER +#include "GeckoTaskTracer.h" +#endif + using namespace mozilla::dom; using namespace mozilla::ipc; @@ -1915,6 +1919,14 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); mAsyncOpenTime = TimeStamp::Now(); +#ifdef MOZ_TASK_TRACER + nsCOMPtr uri; + GetURI(getter_AddRefs(uri)); + nsAutoCString urispec; + uri->GetSpec(urispec); + tasktracer::AddLabel("HttpChannelChild::AsyncOpen %s", urispec.get()); +#endif + // Port checked in parent, but duplicate here so we can return with error // immediately diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 2b053efa4bcd..f69078f2de66 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -103,6 +103,10 @@ #include "HSTSPrimerListener.h" #include "CacheStorageService.h" +#ifdef MOZ_TASK_TRACER +#include "GeckoTaskTracer.h" +#endif + namespace mozilla { namespace net { namespace { @@ -5690,6 +5694,18 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) "security flags in loadInfo but asyncOpen2() not called"); LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this)); +#ifdef MOZ_TASK_TRACER + { + uint64_t sourceEventId, parentTaskId; + tasktracer::SourceEventType sourceEventType; + GetCurTraceInfo(&sourceEventId, &parentTaskId, &sourceEventType); + nsCOMPtr uri; + GetURI(getter_AddRefs(uri)); + nsAutoCString urispec; + uri->GetSpec(urispec); + tasktracer::AddLabel("nsHttpChannel::AsyncOpen %s", urispec.get()); + } +#endif NS_CompareLoadInfoAndLoadContext(this); diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 49d3dfdd6eb5..580db67516f1 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -76,6 +76,10 @@ #include "nsCocoaFeatures.h" #endif +#ifdef MOZ_TASK_TRACER +#include "GeckoTaskTracer.h" +#endif + //----------------------------------------------------------------------------- #include "mozilla/net/HttpChannelChild.h" @@ -1997,6 +2001,14 @@ nsHttpHandler::NewProxiedChannel2(nsIURI *uri, LOG(("nsHttpHandler::NewProxiedChannel [proxyInfo=%p]\n", givenProxyInfo)); +#ifdef MOZ_TASK_TRACER + { + nsAutoCString urispec; + uri->GetSpec(urispec); + tasktracer::AddLabel("nsHttpHandler::NewProxiedChannel2 %s", urispec.get()); + } +#endif + nsCOMPtr proxyInfo; if (givenProxyInfo) { proxyInfo = do_QueryInterface(givenProxyInfo); diff --git a/python/mozbuild/mozbuild/frontend/emitter.py b/python/mozbuild/mozbuild/frontend/emitter.py index 0761b1d75d31..278f17bcc523 100644 --- a/python/mozbuild/mozbuild/frontend/emitter.py +++ b/python/mozbuild/mozbuild/frontend/emitter.py @@ -106,7 +106,6 @@ from mozbuild.base import ExecutionSummary ALLOWED_XPCOM_GLUE = { ('xpcshell', 'js/xpconnect/shell'), ('testcrasher', 'toolkit/crashreporter/test'), - ('mediaconduit_unittests', 'media/webrtc/signaling/test'), ('mediapipeline_unittest', 'media/webrtc/signaling/test'), ('signaling_unittests', 'media/webrtc/signaling/test'), ('TestMailCookie', 'mailnews/base/test'), diff --git a/security/nss/TAG-INFO b/security/nss/TAG-INFO index 40c4b91a6a47..3316ea955760 100644 --- a/security/nss/TAG-INFO +++ b/security/nss/TAG-INFO @@ -1 +1 @@ -5f2db99c258f +5e59e858012d diff --git a/security/nss/automation/buildbot-slave/build.sh b/security/nss/automation/buildbot-slave/build.sh index 0917cec6b60f..7f1a960be78a 100755 --- a/security/nss/automation/buildbot-slave/build.sh +++ b/security/nss/automation/buildbot-slave/build.sh @@ -243,6 +243,39 @@ test_jss() return ${RET} } +create_objdir_dist_link() +{ + # compute relevant 'dist' OBJDIR_NAME subdirectory names for JSS and NSS + OS_TARGET=`uname -s` + OS_RELEASE=`uname -r | sed 's/-.*//' | sed 's/-.*//' | cut -d . -f1,2` + CPU_TAG=_`uname -m` + # OBJDIR_NAME_COMPILER appears to be defined for NSS but not JSS + OBJDIR_NAME_COMPILER=_cc + LIBC_TAG=_glibc + IMPL_STRATEGY=_PTH + if [ "${RUN_BITS}" = "64" ]; then + OBJDIR_TAG=_${RUN_BITS}_${RUN_OPT}.OBJ + else + OBJDIR_TAG=_${RUN_OPT}.OBJ + fi + + # define NSS_OBJDIR_NAME + NSS_OBJDIR_NAME=${OS_TARGET}${OS_RELEASE}${CPU_TAG}${OBJDIR_NAME_COMPILER} + NSS_OBJDIR_NAME=${NSS_OBJDIR_NAME}${LIBC_TAG}${IMPL_STRATEGY}${OBJDIR_TAG} + print_log "create_objdir_dist_link(): NSS_OBJDIR_NAME='${NSS_OBJDIR_NAME}'" + + # define JSS_OBJDIR_NAME + JSS_OBJDIR_NAME=${OS_TARGET}${OS_RELEASE}${CPU_TAG} + JSS_OBJDIR_NAME=${JSS_OBJDIR_NAME}${LIBC_TAG}${IMPL_STRATEGY}${OBJDIR_TAG} + print_log "create_objdir_dist_link(): JSS_OBJDIR_NAME='${JSS_OBJDIR_NAME}'" + + if [ -e "${HGDIR}/dist/${NSS_OBJDIR_NAME}" ]; then + SOURCE=${HGDIR}/dist/${NSS_OBJDIR_NAME} + TARGET=${HGDIR}/dist/${JSS_OBJDIR_NAME} + ln -s ${SOURCE} ${TARGET} >/dev/null 2>&1 + fi +} + build_and_test() { if [ -n "${BUILD_NSS}" ]; then @@ -256,6 +289,7 @@ build_and_test() fi if [ -n "${BUILD_JSS}" ]; then + create_objdir_dist_link build_jss [ $? -eq 0 ] || return 1 fi diff --git a/security/nss/cmd/certutil/keystuff.c b/security/nss/cmd/certutil/keystuff.c index 268ad471a92e..1569313ba627 100644 --- a/security/nss/cmd/certutil/keystuff.c +++ b/security/nss/cmd/certutil/keystuff.c @@ -52,9 +52,10 @@ static int UpdateRNG(void) { char randbuf[RAND_BUF_SIZE]; - int fd, count; + int fd; int c; int rv = 0; + size_t count; #ifdef XP_UNIX cc_t orig_cc_min; cc_t orig_cc_time; diff --git a/security/nss/cmd/selfserv/selfserv.c b/security/nss/cmd/selfserv/selfserv.c index f34af7d7439d..49bb7f14bbbe 100644 --- a/security/nss/cmd/selfserv/selfserv.c +++ b/security/nss/cmd/selfserv/selfserv.c @@ -2305,7 +2305,9 @@ main(int argc, char **argv) if (SECU_ParseSSLVersionRangeString(optstate->value, enabledVersions, &enabledVersions) != SECSuccess) { + fprintf(stderr, "Bad version specified.\n"); Usage(progName); + exit(1); } break; diff --git a/security/nss/cmd/strsclnt/strsclnt.c b/security/nss/cmd/strsclnt/strsclnt.c index 209dcca96216..f65e319137fc 100644 --- a/security/nss/cmd/strsclnt/strsclnt.c +++ b/security/nss/cmd/strsclnt/strsclnt.c @@ -1350,6 +1350,7 @@ main(int argc, char **argv) if (SECU_ParseSSLVersionRangeString(optstate->value, enabledVersions, &enabledVersions) != SECSuccess) { + fprintf(stderr, "Bad version specified.\n"); Usage(progName); } break; diff --git a/security/nss/cmd/tstclnt/tstclnt.c b/security/nss/cmd/tstclnt/tstclnt.c index c7c733598bc4..c2b916e90b58 100644 --- a/security/nss/cmd/tstclnt/tstclnt.c +++ b/security/nss/cmd/tstclnt/tstclnt.c @@ -1610,6 +1610,7 @@ main(int argc, char **argv) if (SECU_ParseSSLVersionRangeString(optstate->value, enabledVersions, &enabledVersions) != SECSuccess) { + fprintf(stderr, "Bad version specified.\n"); Usage(progName); } break; diff --git a/security/nss/coreconf/coreconf.dep b/security/nss/coreconf/coreconf.dep index 5182f75552c8..590d1bfaeee3 100644 --- a/security/nss/coreconf/coreconf.dep +++ b/security/nss/coreconf/coreconf.dep @@ -10,3 +10,4 @@ */ #error "Do not include this header file." + diff --git a/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc b/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc index 7da7bbda4e48..5ed63df6cd02 100644 --- a/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc +++ b/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc @@ -209,6 +209,17 @@ TEST_P(TlsConnectTls13, AlertWrongLevel) { client_->WaitForErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT, 2000); } +TEST_F(TlsConnectStreamTls13, Tls13FailedWriteSecondFlight) { + EnsureTlsSetup(); + client_->StartConnect(); + server_->StartConnect(); + client_->Handshake(); + server_->Handshake(); // Send first flight. + client_->adapter()->CloseWrites(); + client_->Handshake(); // This will get an error, but shouldn't crash. + client_->CheckErrorCode(SSL_ERROR_SOCKET_WRITE_FAILURE); +} + INSTANTIATE_TEST_CASE_P(GenericStream, TlsConnectGeneric, ::testing::Combine(TlsConnectTestBase::kTlsModesStream, TlsConnectTestBase::kTlsVAll)); diff --git a/security/nss/gtests/ssl_gtest/test_io.cc b/security/nss/gtests/ssl_gtest/test_io.cc index 646c5a5771ad..f3fd0b24c511 100644 --- a/security/nss/gtests/ssl_gtest/test_io.cc +++ b/security/nss/gtests/ssl_gtest/test_io.cc @@ -371,7 +371,7 @@ int32_t DummyPrSocket::Recv(void *buf, int32_t buflen) { } int32_t DummyPrSocket::Write(const void *buf, int32_t length) { - if (!peer_) { + if (!peer_ || !writeable_) { PR_SetError(PR_IO_ERROR, 0); return -1; } diff --git a/security/nss/gtests/ssl_gtest/test_io.h b/security/nss/gtests/ssl_gtest/test_io.h index 9ea444c413a0..b78db0dc657a 100644 --- a/security/nss/gtests/ssl_gtest/test_io.h +++ b/security/nss/gtests/ssl_gtest/test_io.h @@ -66,20 +66,26 @@ class DummyPrSocket { int32_t Read(void* data, int32_t len); int32_t Recv(void* buf, int32_t buflen); int32_t Write(const void* buf, int32_t length); + void CloseWrites() { writeable_ = false; } Mode mode() const { return mode_; } bool readable() const { return !input_.empty(); } - bool writable() { return true; } private: DummyPrSocket(const std::string& name, Mode mode) - : name_(name), mode_(mode), peer_(nullptr), input_(), filter_(nullptr) {} + : name_(name), + mode_(mode), + peer_(nullptr), + input_(), + filter_(nullptr), + writeable_(true) {} const std::string name_; Mode mode_; DummyPrSocket* peer_; std::queue input_; PacketFilter* filter_; + bool writeable_; }; // Marker interface. diff --git a/security/nss/lib/freebl/freebl.gyp b/security/nss/lib/freebl/freebl.gyp index f5ae232ecdca..6a7dbe5c0d7f 100644 --- a/security/nss/lib/freebl/freebl.gyp +++ b/security/nss/lib/freebl/freebl.gyp @@ -159,8 +159,6 @@ 'sources': [ #TODO: building with mingw should not need this. 'ecl/uint128.c', - #TODO: clang-cl needs -msse3 here - 'intel-gcm-wrap.c', ], 'libraries': [ 'advapi32.lib', @@ -183,6 +181,16 @@ 'intel-gcm-x86-masm.asm', ], }], + [ 'cc_is_clang==1', { + 'dependencies': [ + 'intel-gcm-wrap_c_lib', + ], + }, { + # MSVC + 'sources': [ + 'intel-gcm-wrap.c', + ], + }], ], }], ['target_arch=="ia32" or target_arch=="x64"', { diff --git a/security/nss/lib/ssl/ssl3con.c b/security/nss/lib/ssl/ssl3con.c index 041eeaa9fdcf..e5e32a9500db 100644 --- a/security/nss/lib/ssl/ssl3con.c +++ b/security/nss/lib/ssl/ssl3con.c @@ -10255,7 +10255,7 @@ ssl3_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length) * until it has verified the server's Finished message." See the comment in * ssl3_FinishHandshake for more details. */ - ss->ssl3.hs.newSessionTicket.received_timestamp = ssl_Time(); + ss->ssl3.hs.newSessionTicket.received_timestamp = PR_Now(); if (length < 4) { (void)SSL3_SendAlert(ss, alert_fatal, decode_error); PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET); diff --git a/security/nss/lib/ssl/ssl3exthandle.c b/security/nss/lib/ssl/ssl3exthandle.c index ec1dc8cff932..1cb3dcb8686e 100644 --- a/security/nss/lib/ssl/ssl3exthandle.c +++ b/security/nss/lib/ssl/ssl3exthandle.c @@ -372,11 +372,7 @@ ssl3_SendSessionTicketXtn( if (session_ticket->ticket.data) { if (xtnData->ticketTimestampVerified) { extension_length += session_ticket->ticket.len; - } else if (!append && - (session_ticket->ticket_lifetime_hint == 0 || - (session_ticket->ticket_lifetime_hint + - session_ticket->received_timestamp > - ssl_Time()))) { + } else if (!append && ssl_TicketTimeValid(session_ticket)) { extension_length += session_ticket->ticket.len; xtnData->ticketTimestampVerified = PR_TRUE; } diff --git a/security/nss/lib/ssl/ssl3prot.h b/security/nss/lib/ssl/ssl3prot.h index 64542f4b5e8c..ec6d29f2e4ec 100644 --- a/security/nss/lib/ssl/ssl3prot.h +++ b/security/nss/lib/ssl/ssl3prot.h @@ -117,8 +117,10 @@ typedef enum { unrecognized_name = 112, bad_certificate_status_response = 113, bad_certificate_hash_value = 114, - no_application_protocol = 120 + no_application_protocol = 120, + /* invalid alert */ + no_alert = 256 } SSL3AlertDescription; typedef struct { @@ -284,7 +286,7 @@ typedef struct { /* NewSessionTicket handshake message. */ typedef struct { - PRUint32 received_timestamp; + PRTime received_timestamp; PRUint32 ticket_lifetime_hint; PRUint32 flags; PRUint32 ticket_age_add; diff --git a/security/nss/lib/ssl/sslimpl.h b/security/nss/lib/ssl/sslimpl.h index de1283f3f07a..36d7c04a139c 100644 --- a/security/nss/lib/ssl/sslimpl.h +++ b/security/nss/lib/ssl/sslimpl.h @@ -1842,6 +1842,7 @@ extern void ssl3_CheckCipherSuiteOrderConsistency(); extern int ssl_MapLowLevelError(int hiLevelError); extern PRUint32 ssl_Time(void); +extern PRBool ssl_TicketTimeValid(const NewSessionTicket *ticket); extern void SSL_AtomicIncrementLong(long *x); diff --git a/security/nss/lib/ssl/sslnonce.c b/security/nss/lib/ssl/sslnonce.c index 91cc870407cd..616901300ca1 100644 --- a/security/nss/lib/ssl/sslnonce.c +++ b/security/nss/lib/ssl/sslnonce.c @@ -1,3 +1,4 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file implements the CLIENT Session ID cache. * @@ -460,6 +461,20 @@ ssl_Time(void) return myTime; } +PRBool +ssl_TicketTimeValid(const NewSessionTicket *ticket) +{ + PRTime endTime; + + if (ticket->ticket_lifetime_hint == 0) { + return PR_TRUE; + } + + endTime = ticket->received_timestamp + + (PRTime)(ticket->ticket_lifetime_hint * PR_USEC_PER_MSEC); + return endTime > PR_Now(); +} + void ssl3_SetSIDSessionTicket(sslSessionID *sid, /*in/out*/ NewSessionTicket *newSessionTicket) diff --git a/security/nss/lib/ssl/tls13con.c b/security/nss/lib/ssl/tls13con.c index 5970a759a795..0d5d6bdd191f 100644 --- a/security/nss/lib/ssl/tls13con.c +++ b/security/nss/lib/ssl/tls13con.c @@ -163,15 +163,21 @@ static char * tls13_HandshakeState(SSL3WaitState st) { switch (st) { + STATE_CASE(idle_handshake); STATE_CASE(wait_client_hello); STATE_CASE(wait_client_cert); + STATE_CASE(wait_client_key); STATE_CASE(wait_cert_verify); + STATE_CASE(wait_change_cipher); STATE_CASE(wait_finished); STATE_CASE(wait_server_hello); + STATE_CASE(wait_certificate_status); STATE_CASE(wait_server_cert); + STATE_CASE(wait_server_key); STATE_CASE(wait_cert_request); + STATE_CASE(wait_hello_done); + STATE_CASE(wait_new_session_ticket); STATE_CASE(wait_encrypted_extensions); - STATE_CASE(idle_handshake); default: break; } @@ -426,10 +432,7 @@ tls13_SetupClientHello(sslSocket *ss) session_ticket = &sid->u.ssl3.locked.sessionTicket; PORT_Assert(session_ticket && session_ticket->ticket.data); - if (session_ticket->ticket_lifetime_hint == 0 || - (session_ticket->ticket_lifetime_hint + - session_ticket->received_timestamp > - ssl_Time())) { + if (ssl_TicketTimeValid(session_ticket)) { ss->statelessResume = PR_TRUE; } @@ -3620,11 +3623,71 @@ tls13_FinishHandshake(sslSocket *ss) return SECSuccess; } +/* Do the parts of sending the client's second round that require + * the XmitBuf lock. */ +static SECStatus +tls13_SendClientSecondFlight(sslSocket *ss, PRBool sendClientCert, + SSL3AlertDescription *sendAlert) +{ + SECStatus rv; + + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + + *sendAlert = internal_error; + + if (ss->ssl3.sendEmptyCert) { + ss->ssl3.sendEmptyCert = PR_FALSE; + rv = ssl3_SendEmptyCertificate(ss); + /* Don't send verify */ + if (rv != SECSuccess) { + return SECFailure; /* error code is set. */ + } + } else if (sendClientCert) { + rv = tls13_SendCertificate(ss); + if (rv != SECSuccess) { + return SECFailure; /* error code is set. */ + } + } + if (ss->ssl3.hs.certificateRequest) { + PORT_FreeArena(ss->ssl3.hs.certificateRequest->arena, PR_FALSE); + ss->ssl3.hs.certificateRequest = NULL; + } + + if (sendClientCert) { + rv = tls13_SendCertificateVerify(ss, ss->ssl3.clientPrivateKey); + SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); + ss->ssl3.clientPrivateKey = NULL; + if (rv != SECSuccess) { + return SECFailure; /* err is set. */ + } + } + + rv = tls13_SendFinished(ss, ss->ssl3.hs.clientHsTrafficSecret); + if (rv != SECSuccess) { + return SECFailure; /* err code was set. */ + } + rv = ssl3_FlushHandshake(ss, IS_DTLS(ss) ? ssl_SEND_FLAG_NO_RETRANSMIT : 0); + if (rv != SECSuccess) { + /* No point in sending an alert here because we're not going to + * be able to send it if we couldn't flush the handshake. */ + *sendAlert = no_alert; + return SECFailure; + } + + rv = dtls_StartHolddownTimer(ss); + if (rv != SECSuccess) { + return SECFailure; /* err code was set. */ + } + + return SECSuccess; +} + static SECStatus tls13_SendClientSecondRound(sslSocket *ss) { SECStatus rv; PRBool sendClientCert; + SSL3AlertDescription sendAlert = no_alert; PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); @@ -3685,48 +3748,16 @@ tls13_SendClientSecondRound(sslSocket *ss) } ssl_GetXmitBufLock(ss); /*******************************/ - if (ss->ssl3.sendEmptyCert) { - ss->ssl3.sendEmptyCert = PR_FALSE; - rv = ssl3_SendEmptyCertificate(ss); - /* Don't send verify */ - if (rv != SECSuccess) { - goto loser; /* error code is set. */ - } - } else if (sendClientCert) { - rv = tls13_SendCertificate(ss); - if (rv != SECSuccess) { - goto loser; /* error code is set. */ - } - } - if (ss->ssl3.hs.certificateRequest) { - PORT_FreeArena(ss->ssl3.hs.certificateRequest->arena, PR_FALSE); - ss->ssl3.hs.certificateRequest = NULL; - } - - if (sendClientCert) { - rv = tls13_SendCertificateVerify(ss, ss->ssl3.clientPrivateKey); - SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); - ss->ssl3.clientPrivateKey = NULL; - if (rv != SECSuccess) { - goto loser; /* err is set. */ - } - } - - rv = tls13_SendFinished(ss, ss->ssl3.hs.clientHsTrafficSecret); - if (rv != SECSuccess) { - goto loser; /* err code was set. */ - } - rv = ssl3_FlushHandshake(ss, IS_DTLS(ss) ? ssl_SEND_FLAG_NO_RETRANSMIT : 0); - if (rv != SECSuccess) { - goto loser; - } - - rv = dtls_StartHolddownTimer(ss); - if (rv != SECSuccess) { - goto loser; /* err code was set. */ - } + rv = tls13_SendClientSecondFlight(ss, sendClientCert, &sendAlert); ssl_ReleaseXmitBufLock(ss); /*******************************/ - + if (rv != SECSuccess) { + if (sendAlert != no_alert) { + FATAL_ERROR(ss, PORT_GetError(), sendAlert); + } else { + LOG_ERROR(ss, PORT_GetError()); + } + return SECFailure; + } rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData, CipherSpecWrite, PR_TRUE); if (rv != SECSuccess) { @@ -3736,11 +3767,6 @@ tls13_SendClientSecondRound(sslSocket *ss) /* The handshake is now finished */ return tls13_FinishHandshake(ss); - -loser: - ssl_ReleaseXmitBufLock(ss); /*******************************/ - FATAL_ERROR(ss, PORT_GetError(), internal_error); - return SECFailure; } /* @@ -3867,7 +3893,7 @@ tls13_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length) return SECFailure; } - ticket.received_timestamp = ssl_Time(); + ticket.received_timestamp = PR_Now(); rv = ssl3_ConsumeHandshakeNumber(ss, &ticket.ticket_lifetime_hint, 4, &b, &length); if (rv != SECSuccess) { diff --git a/security/nss/lib/ssl/tls13exthandle.c b/security/nss/lib/ssl/tls13exthandle.c index a3d1be6030cc..707297434d02 100644 --- a/security/nss/lib/ssl/tls13exthandle.c +++ b/security/nss/lib/ssl/tls13exthandle.c @@ -487,7 +487,7 @@ tls13_ClientSendPreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData, if (append) { SECStatus rv; - PRUint32 age; + PRTime age; unsigned int prefixLength; PRUint8 binder[TLS13_MAX_FINISHED_SIZE]; unsigned int binderLen; @@ -508,7 +508,8 @@ tls13_ClientSendPreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData, goto loser; /* Obfuscated age. */ - age = ssl_Time() - session_ticket->received_timestamp; + age = PR_Now() - session_ticket->received_timestamp; + age /= PR_USEC_PER_MSEC; age += session_ticket->ticket_age_add; rv = ssl3_ExtAppendHandshakeNumber(ss, age, 4); if (rv != SECSuccess) @@ -747,10 +748,10 @@ tls13_ClientSendEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData, rv = ssl3_ExtAppendHandshakeNumber(ss, 0, 2); if (rv != SECSuccess) return -1; - } - xtnData->advertised[xtnData->numAdvertised++] = - ssl_tls13_early_data_xtn; + xtnData->advertised[xtnData->numAdvertised++] = + ssl_tls13_early_data_xtn; + } return extension_length; } diff --git a/taskcluster/scripts/tester/test-macosx.sh b/taskcluster/scripts/tester/test-macosx.sh index a5c0086c1949..8c2b758fb1ce 100644 --- a/taskcluster/scripts/tester/test-macosx.sh +++ b/taskcluster/scripts/tester/test-macosx.sh @@ -40,10 +40,9 @@ download_mozharness() { if curl --fail -o mozharness.zip --retry 10 -L $MOZHARNESS_URL; then rm -rf mozharness if unzip -q mozharness.zip; then - break - else - echo "error unzipping mozharness.zip" >&2 + return 0 fi + echo "error unzipping mozharness.zip" >&2 else echo "failed to download mozharness zip" >&2 fi diff --git a/testing/cppunittest.ini b/testing/cppunittest.ini index dc3dedc364bf..acbc6684b375 100644 --- a/testing/cppunittest.ini +++ b/testing/cppunittest.ini @@ -54,7 +54,6 @@ skip-if = os == 'android' # Bug 1147630 [test_nr_socket_unittest] [jsapi-tests] skip-if = os == 'b2g' #Bug 1068946 -[mediaconduit_unittests] [mediapipeline_unittest] skip-if = os == 'b2g' || os == 'android' # Bug 919646 [mediapipeline_unittest_standalone] diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index bdb3221bc7b3..2070365cc24d 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -958,15 +958,15 @@ "alert_emails": ["jdemooij@mozilla.com"], "expires_in_version": "never", "kind": "enumerated", - "n_values": 10, - "description": "Use of SpiderMonkey's deprecated language extensions in web content: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7 (obsolete), RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete), BlockScopeFunRedecl=10" + "n_values": 11, + "description": "Use of SpiderMonkey's deprecated language extensions in web content: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7 (obsolete), RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete), BlockScopeFunRedecl=10, StringGenerics=11" }, "JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS": { "alert_emails": ["jdemooij@mozilla.com"], "expires_in_version": "never", "kind": "enumerated", - "n_values": 10, - "description": "Use of SpiderMonkey's deprecated language extensions in add-ons: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7 (obsolete), RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete), BlockScopeFunRedecl=10" + "n_values": 11, + "description": "Use of SpiderMonkey's deprecated language extensions in add-ons: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7 (obsolete), RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete), BlockScopeFunRedecl=10, StringGenerics=11" }, "XUL_CACHE_DISABLED": { "expires_in_version": "default", diff --git a/toolkit/components/telemetry/TelemetryEnvironment.jsm b/toolkit/components/telemetry/TelemetryEnvironment.jsm index 618f75a0e262..d867d3869a59 100644 --- a/toolkit/components/telemetry/TelemetryEnvironment.jsm +++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm @@ -134,7 +134,6 @@ const DEFAULT_ENVIRONMENT_PREFS = new Map([ ["browser.cache.memory.enable", {what: RECORD_PREF_VALUE}], ["browser.cache.offline.enable", {what: RECORD_PREF_VALUE}], ["browser.formfill.enable", {what: RECORD_PREF_VALUE}], - ["browser.newtab.url", {what: RECORD_PREF_STATE}], ["browser.newtabpage.enabled", {what: RECORD_PREF_VALUE}], ["browser.newtabpage.enhanced", {what: RECORD_PREF_VALUE}], ["browser.shell.checkDefaultBrowser", {what: RECORD_PREF_VALUE}], diff --git a/toolkit/components/typeaheadfind/nsITypeAheadFind.idl b/toolkit/components/typeaheadfind/nsITypeAheadFind.idl index 379d2c2a261e..0056d0d21944 100644 --- a/toolkit/components/typeaheadfind/nsITypeAheadFind.idl +++ b/toolkit/components/typeaheadfind/nsITypeAheadFind.idl @@ -18,7 +18,7 @@ interface nsIDocShell; /****************************** nsTypeAheadFind ******************************/ -[scriptable, uuid(ae501e28-c57f-4692-ac74-410e1bed98b7)] +[scriptable, uuid(3cfe7906-f189-45a0-8abe-8e4437a23cae)] interface nsITypeAheadFind : nsISupports { /****************************** Initializer ******************************/ @@ -58,7 +58,7 @@ interface nsITypeAheadFind : nsISupports void collapseSelection(); /* Check if a range is visible */ - boolean isRangeVisible(in nsIDOMRange aRange, in boolean aMustBeInViewPort); + boolean isRangeVisible(in nsIDOMRange aRange, in boolean aFlushLayout); /******************************* Attributes ******************************/ diff --git a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp index 674681581992..4ac033fb231e 100644 --- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp +++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp @@ -52,6 +52,7 @@ #include "mozilla/dom/Link.h" #include "nsRange.h" #include "nsXBLBinding.h" +#include "nsLayoutUtils.h" #include "nsTypeAheadFind.h" @@ -70,8 +71,6 @@ NS_IMPL_CYCLE_COLLECTION(nsTypeAheadFind, mFoundLink, mFoundEditable, mStartPointRange, mEndPointRange, mSoundInterface, mFind, mFoundRange) -static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID); - #define NS_FIND_CONTRACTID "@mozilla.org/embedcomp/rangefind;1" nsTypeAheadFind::nsTypeAheadFind(): @@ -422,7 +421,7 @@ nsTypeAheadFind::FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly, while (true) { // === Inner while loop: go through a single doc === mFind->Find(mTypeAheadBuffer.get(), mSearchRange, mStartPointRange, mEndPointRange, getter_AddRefs(returnRange)); - + if (!returnRange) break; // Nothing found in this doc, go to outer loop (try next doc) @@ -436,8 +435,7 @@ nsTypeAheadFind::FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly, } bool usesIndependentSelection; - if (!IsRangeVisible(presShell, presContext, returnRange, - aIsFirstVisiblePreferred, false, + if (!IsRangeVisible(presShell, presContext, returnRange, true, getter_AddRefs(mStartPointRange), &usesIndependentSelection) || (aIsLinksOnly && !isInsideLink) || @@ -817,8 +815,7 @@ nsTypeAheadFind::GetSearchContainers(nsISupports *aContainer, // Ensure visible range, move forward if necessary // This uses ignores the return value, but usese the side effect of // IsRangeVisible. It returns the first visible range after searchRange - IsRangeVisible(presShell, presContext, mSearchRange, - aIsFirstVisiblePreferred, true, + IsRangeVisible(presShell, presContext, mSearchRange, true, getter_AddRefs(mStartPointRange), nullptr); } else { @@ -1008,18 +1005,18 @@ nsTypeAheadFind::Find(const nsAString& aSearchString, bool aLinksOnly, return NS_OK; } - bool atEnd = false; + bool atEnd = false; if (mTypeAheadBuffer.Length()) { const nsAString& oldStr = Substring(mTypeAheadBuffer, 0, mTypeAheadBuffer.Length()); const nsAString& newStr = Substring(aSearchString, 0, mTypeAheadBuffer.Length()); if (oldStr.Equals(newStr)) atEnd = true; - + const nsAString& newStr2 = Substring(aSearchString, 0, aSearchString.Length()); const nsAString& oldStr2 = Substring(mTypeAheadBuffer, 0, aSearchString.Length()); if (oldStr2.Equals(newStr2)) atEnd = true; - + if (!atEnd) mStartFindRange = nullptr; } @@ -1161,7 +1158,7 @@ nsTypeAheadFind::GetFoundRange(nsIDOMRange** aFoundRange) NS_IMETHODIMP nsTypeAheadFind::IsRangeVisible(nsIDOMRange *aRange, - bool aMustBeInViewPort, + bool aFlushLayout, bool *aResult) { // Jump through hoops to extract the docShell from the range. @@ -1178,8 +1175,7 @@ nsTypeAheadFind::IsRangeVisible(nsIDOMRange *aRange, nsCOMPtr presShell (docShell->GetPresShell()); RefPtr presContext = presShell->GetPresContext(); nsCOMPtr startPointRange = new nsRange(presShell->GetDocument()); - *aResult = IsRangeVisible(presShell, presContext, aRange, - aMustBeInViewPort, false, + *aResult = IsRangeVisible(presShell, presContext, aRange, aFlushLayout, getter_AddRefs(startPointRange), nullptr); return NS_OK; @@ -1188,32 +1184,77 @@ nsTypeAheadFind::IsRangeVisible(nsIDOMRange *aRange, bool nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell, nsPresContext *aPresContext, - nsIDOMRange *aRange, bool aMustBeInViewPort, - bool aGetTopVisibleLeaf, + nsIDOMRange *aRange, + bool aFlushLayout, nsIDOMRange **aFirstVisibleRange, bool *aUsesIndependentSelection) { NS_ASSERTION(aPresShell && aPresContext && aRange && aFirstVisibleRange, "params are invalid"); - // We need to know if the range start is visible. - // Otherwise, return the first visible range start - // in aFirstVisibleRange - + // We need to know if the range start and end are both visible. + // In all cases, return the first visible range in aFirstVisibleRange. aRange->CloneRange(aFirstVisibleRange); + + if (aFlushLayout) { + aPresShell->FlushPendingNotifications(Flush_Layout); + } + nsCOMPtr node; - aRange->GetStartContainer(getter_AddRefs(node)); + aRange->GetCommonAncestorContainer(getter_AddRefs(node)); nsCOMPtr content(do_QueryInterface(node)); - if (!content) + if (!content) { return false; + } nsIFrame *frame = content->GetPrimaryFrame(); - if (!frame) + if (!frame) { return false; // No frame! Not visible then. + } - if (!frame->StyleVisibility()->IsVisible()) + // Having a primary frame doesn't mean that the range is visible inside the + // viewport. Do a hit-test to determine that quickly and properly. + bool foundContent = false; + AutoTArray frames; + nsIFrame *rootFrame = aPresShell->GetRootFrame(); + RefPtr range = static_cast(aRange); + RefPtr rects = range->GetClientRects(true, false); + for (uint32_t i = 0; i < rects->Length(); ++i) { + RefPtr rect = rects->Item(i); + nsRect r(nsPresContext::CSSPixelsToAppUnits((float)rect->X()), + nsPresContext::CSSPixelsToAppUnits((float)rect->Y()), + nsPresContext::CSSPixelsToAppUnits((float)rect->Width()), + nsPresContext::CSSPixelsToAppUnits((float)rect->Height())); + // Append visible frames to frames array. + nsLayoutUtils::GetFramesForArea(rootFrame, r, frames, + nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | + nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME | + nsLayoutUtils::ONLY_VISIBLE); + + // See if any of the frames contain the content. If they do, then the range + // is visible. We search for the content rather than the original frame, + // because nsTextContinuation frames might be returned instead of the + // original frame. + for (const auto &f: frames) { + if (f->GetContent() == content) { + foundContent = true; + break; + } + } + + if (foundContent) { + break; + } + + frames.ClearAndRetainStorage(); + } + + // Test that content appears in the list of framesForArea. If it does, then + // that means that content is at least partly visible. + if (!foundContent) { return false; + } // Detect if we are _inside_ a text control, or something else with its own // selection controller. @@ -1222,89 +1263,7 @@ nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell, (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION); } - // ---- We have a frame ---- - if (!aMustBeInViewPort) - return true; // Don't need it to be on screen, just in rendering tree - - // Get the next in flow frame that contains the range start - int32_t startRangeOffset, startFrameOffset, endFrameOffset; - aRange->GetStartOffset(&startRangeOffset); - while (true) { - frame->GetOffsets(startFrameOffset, endFrameOffset); - if (startRangeOffset < endFrameOffset) - break; - - nsIFrame *nextContinuationFrame = frame->GetNextContinuation(); - if (nextContinuationFrame) - frame = nextContinuationFrame; - else - break; - } - - // Set up the variables we need, return true if we can't get at them all - const uint16_t kMinPixels = 12; - nscoord minDistance = nsPresContext::CSSPixelsToAppUnits(kMinPixels); - - // Get the bounds of the current frame, relative to the current view. - // We don't use the more accurate AccGetBounds, because that is - // more expensive and the STATE_OFFSCREEN flag that this is used - // for only needs to be a rough indicator - nsRectVisibility rectVisibility = nsRectVisibility_kAboveViewport; - - if (!aGetTopVisibleLeaf && !frame->GetRect().IsEmpty()) { - rectVisibility = - aPresShell->GetRectVisibility(frame, - nsRect(nsPoint(0,0), frame->GetSize()), - minDistance); - - if (rectVisibility != nsRectVisibility_kAboveViewport) { - return true; - } - } - - // We know that the target range isn't usable because it's not in the - // view port. Move range forward to first visible point, - // this speeds us up a lot in long documents - nsCOMPtr frameTraversal; - nsCOMPtr trav(do_CreateInstance(kFrameTraversalCID)); - if (trav) - trav->NewFrameTraversal(getter_AddRefs(frameTraversal), - aPresContext, frame, - eLeaf, - false, // aVisual - false, // aLockInScrollView - false, // aFollowOOFs - false // aSkipPopupChecks - ); - - if (!frameTraversal) - return false; - - while (rectVisibility == nsRectVisibility_kAboveViewport) { - frameTraversal->Next(); - frame = frameTraversal->CurrentItem(); - if (!frame) - return false; - - if (!frame->GetRect().IsEmpty()) { - rectVisibility = - aPresShell->GetRectVisibility(frame, - nsRect(nsPoint(0,0), frame->GetSize()), - minDistance); - } - } - - if (frame) { - nsCOMPtr firstVisibleNode = do_QueryInterface(frame->GetContent()); - - if (firstVisibleNode) { - frame->GetOffsets(startFrameOffset, endFrameOffset); - (*aFirstVisibleRange)->SetStart(firstVisibleNode, startFrameOffset); - (*aFirstVisibleRange)->SetEnd(firstVisibleNode, endFrameOffset); - } - } - - return false; + return true; } already_AddRefed diff --git a/toolkit/components/typeaheadfind/nsTypeAheadFind.h b/toolkit/components/typeaheadfind/nsTypeAheadFind.h index 8ff5ad1bfe1e..9441ea774c58 100644 --- a/toolkit/components/typeaheadfind/nsTypeAheadFind.h +++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.h @@ -56,8 +56,8 @@ protected: // *aNewRange may not be collapsed. If you want to collapse it in a // particular way, you need to do it yourself. bool IsRangeVisible(nsIPresShell *aPresShell, nsPresContext *aPresContext, - nsIDOMRange *aRange, bool aMustBeVisible, - bool aGetTopVisibleLeaf, nsIDOMRange **aNewRange, + nsIDOMRange *aRange, bool aFlushLayout, + nsIDOMRange **aNewRange, bool *aUsesIndependentSelection); nsresult FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly, bool aIsFirstVisiblePreferred, bool aFindPrev, @@ -95,7 +95,7 @@ protected: // If we destroy mSoundInterface before sound has played, it won't play nsCOMPtr mSoundInterface; bool mIsSoundInitialized; - + // where selection was when user started the find nsCOMPtr mStartFindRange; nsCOMPtr mSearchRange; diff --git a/toolkit/content/aboutTelemetry.js b/toolkit/content/aboutTelemetry.js index 30b79c323e81..42900117abe5 100644 --- a/toolkit/content/aboutTelemetry.js +++ b/toolkit/content/aboutTelemetry.js @@ -234,7 +234,7 @@ var Settings = { el.addEventListener("click", function() { if (AppConstants.platform == "android") { Cu.import("resource://gre/modules/Messaging.jsm"); - Messaging.sendRequest({ + EventDispatcher.instance.sendRequest({ type: "Settings:Show", resource: "preferences_privacy", }); diff --git a/toolkit/modules/FinderHighlighter.jsm b/toolkit/modules/FinderHighlighter.jsm index 451f4691b039..cd50b9c4b9fb 100644 --- a/toolkit/modules/FinderHighlighter.jsm +++ b/toolkit/modules/FinderHighlighter.jsm @@ -1137,6 +1137,9 @@ FinderHighlighter.prototype = { let DOMRect = window.DOMRect; for (let [range, rectsAndTexts] of dict.modalHighlightRectsMap) { + if (!this.finder._fastFind.isRangeVisible(range, false)) + continue; + if (dict.updateAllRanges) rectsAndTexts = this._updateRangeRects(range); diff --git a/toolkit/modules/FinderIterator.jsm b/toolkit/modules/FinderIterator.jsm index 121190156801..f26bc71c2a97 100644 --- a/toolkit/modules/FinderIterator.jsm +++ b/toolkit/modules/FinderIterator.jsm @@ -567,13 +567,7 @@ this.FinderIterator = { let frame = window.frames[i]; // Don't count matches in hidden frames. let frameEl = frame && frame.frameElement; - if (!frameEl) - continue; - // Construct a range around the frame element to check its visiblity. - let range = window.document.createRange(); - range.setStart(frameEl, 0); - range.setEnd(frameEl, 0); - if (!finder._fastFind.isRangeVisible(range, this._getDocShell(range), true)) + if (!frameEl || !frameEl.getClientRects().length) continue; // All conditions pass, so push the current frame and its children on the // stack. diff --git a/toolkit/themes/shared/icons/spinner-arrows.svg b/toolkit/themes/shared/icons/spinner-arrows.svg new file mode 100644 index 000000000000..a8ba72d6b56f --- /dev/null +++ b/toolkit/themes/shared/icons/spinner-arrows.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/toolkit/themes/shared/jar.inc.mn b/toolkit/themes/shared/jar.inc.mn index 89f0d918bf79..bbf6a60fd86d 100644 --- a/toolkit/themes/shared/jar.inc.mn +++ b/toolkit/themes/shared/jar.inc.mn @@ -28,6 +28,7 @@ toolkit.jar: skin/classic/global/icons/input-clear.svg (../../shared/icons/input-clear.svg) skin/classic/global/icons/loading.png (../../shared/icons/loading.png) skin/classic/global/icons/loading@2x.png (../../shared/icons/loading@2x.png) + skin/classic/global/icons/spinner-arrows.svg (../../shared/icons/spinner-arrows.svg) skin/classic/global/icons/menubutton-dropmarker.svg (../../shared/icons/menubutton-dropmarker.svg) skin/classic/global/icons/warning.svg (../../shared/incontent-icons/warning.svg) skin/classic/global/icons/blocked.svg (../../shared/incontent-icons/blocked.svg) diff --git a/toolkit/themes/shared/timepicker.css b/toolkit/themes/shared/timepicker.css index e8d081b3012d..e053abf5fdb7 100644 --- a/toolkit/themes/shared/timepicker.css +++ b/toolkit/themes/shared/timepicker.css @@ -55,24 +55,25 @@ body { -moz-appearance: none; border: none; background: none; - background-color: var(--button-font-color); height: var(--spinner-button-height); + filter: url("chrome://global/skin/filters.svg#fill"); + fill: var(--button-font-color); } .spinner-container > button:hover { - background-color: var(--button-font-color-hover); + fill: var(--button-font-color-hover); } .spinner-container > button.active { - background-color: var(--button-font-color-active); + fill: var(--button-font-color-active); } .spinner-container > button.up { - mask: url("chrome://global/skin/icons/find-arrows.svg#glyph-find-previous") no-repeat 50% 50%; + background: url("chrome://global/skin/icons/spinner-arrows.svg#up") no-repeat 50% 50%; } .spinner-container > button.down { - mask: url("chrome://global/skin/icons/find-arrows.svg#glyph-find-next") no-repeat 50% 50%; + background: url("chrome://global/skin/icons/spinner-arrows.svg#down") no-repeat 50% 50%; } .spinner-container.hide-buttons > button { diff --git a/tools/profiler/tasktracer/SourceEventTypeMap.h b/tools/profiler/tasktracer/SourceEventTypeMap.h index 6469d68bea52..102ccfe6ac25 100644 --- a/tools/profiler/tasktracer/SourceEventTypeMap.h +++ b/tools/profiler/tasktracer/SourceEventTypeMap.h @@ -13,3 +13,4 @@ SOURCE_EVENT_NAME(Key) SOURCE_EVENT_NAME(Bluetooth) SOURCE_EVENT_NAME(Unixsocket) SOURCE_EVENT_NAME(Wifi) +SOURCE_EVENT_NAME(SocketIO)