michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "gtest/gtest.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsTArray.h" michael@0: #include "nsString.h" michael@0: #include "nsDependentString.h" michael@0: michael@0: #include "prinrval.h" michael@0: michael@0: #include "gfxContext.h" michael@0: #include "gfxFont.h" michael@0: #include "gfxPlatform.h" michael@0: michael@0: #include "gfxFontTest.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: class FrameTextRunCache; michael@0: michael@0: static FrameTextRunCache *gTextRuns = nullptr; michael@0: michael@0: /* michael@0: * Cache textruns and expire them after 3*10 seconds of no use. michael@0: */ michael@0: class FrameTextRunCache MOZ_FINAL : public nsExpirationTracker { michael@0: public: michael@0: enum { TIMEOUT_SECONDS = 10 }; michael@0: FrameTextRunCache() michael@0: : nsExpirationTracker(TIMEOUT_SECONDS*1000) {} michael@0: ~FrameTextRunCache() { michael@0: AgeAllGenerations(); michael@0: } michael@0: michael@0: void RemoveFromCache(gfxTextRun* aTextRun) { michael@0: if (aTextRun->GetExpirationState()->IsTracked()) { michael@0: RemoveObject(aTextRun); michael@0: } michael@0: } michael@0: michael@0: // This gets called when the timeout has expired on a gfxTextRun michael@0: virtual void NotifyExpired(gfxTextRun* aTextRun) { michael@0: RemoveFromCache(aTextRun); michael@0: delete aTextRun; michael@0: } michael@0: }; michael@0: michael@0: static gfxTextRun * michael@0: MakeTextRun(const char16_t *aText, uint32_t aLength, michael@0: gfxFontGroup *aFontGroup, const gfxFontGroup::Parameters* aParams, michael@0: uint32_t aFlags) michael@0: { michael@0: nsAutoPtr textRun; michael@0: if (aLength == 0) { michael@0: abort(); michael@0: //textRun = aFontGroup->MakeEmptyTextRun(aParams, aFlags); michael@0: } else if (aLength == 1 && aText[0] == ' ') { michael@0: abort(); michael@0: //textRun = aFontGroup->MakeSpaceTextRun(aParams, aFlags); michael@0: } else { michael@0: textRun = aFontGroup->MakeTextRun(aText, aLength, aParams, aFlags); michael@0: } michael@0: if (!textRun) michael@0: return nullptr; michael@0: nsresult rv = gTextRuns->AddObject(textRun); michael@0: if (NS_FAILED(rv)) { michael@0: gTextRuns->RemoveFromCache(textRun); michael@0: return nullptr; michael@0: } michael@0: return textRun.forget(); michael@0: } michael@0: michael@0: static already_AddRefed michael@0: MakeContext () michael@0: { michael@0: const int size = 200; michael@0: michael@0: nsRefPtr surface; michael@0: michael@0: surface = gfxPlatform::GetPlatform()-> michael@0: CreateOffscreenSurface(IntSize(size, size), michael@0: gfxASurface::ContentFromFormat(gfxImageFormat::RGB24)); michael@0: nsRefPtr ctx = new gfxContext(surface); michael@0: return ctx.forget(); michael@0: } michael@0: michael@0: TEST(Gfx, WordCache) { michael@0: gTextRuns = new FrameTextRunCache(); michael@0: michael@0: nsRefPtr ctx = MakeContext(); michael@0: { michael@0: gfxFontStyle style (mozilla::gfx::FontStyle::NORMAL, michael@0: 139, michael@0: 10.0, michael@0: 0, michael@0: NS_NewPermanentAtom(NS_LITERAL_STRING("en")), michael@0: 0.0, michael@0: false, false, michael@0: NS_LITERAL_STRING("")); michael@0: michael@0: nsRefPtr fontGroup = michael@0: gfxPlatform::GetPlatform()->CreateFontGroup(NS_LITERAL_STRING("Geneva, MS Sans Serif, Helvetica,serif"), &style, nullptr); michael@0: michael@0: gfxTextRunFactory::Parameters params = { michael@0: ctx, nullptr, nullptr, nullptr, 0, 60 michael@0: }; michael@0: michael@0: uint32_t flags = gfxTextRunFactory::TEXT_IS_PERSISTENT; michael@0: michael@0: // First load an Arabic word into the cache michael@0: const char cString[] = "\xd8\xaa\xd9\x85"; michael@0: nsDependentCString cStr(cString); michael@0: NS_ConvertUTF8toUTF16 str(cStr); michael@0: gfxTextRun *tr = MakeTextRun(str.get(), str.Length(), fontGroup, ¶ms, flags); michael@0: tr->GetAdvanceWidth(0, str.Length(), nullptr); michael@0: michael@0: // Now try to trigger an assertion with a word cache bug. The first michael@0: // word is in the cache so it gets added to the new textrun directly. michael@0: // The second word is not in the cache michael@0: const char cString2[] = "\xd8\xaa\xd9\x85\n\xd8\xaa\xd8\x85 "; michael@0: nsDependentCString cStr2(cString2); michael@0: NS_ConvertUTF8toUTF16 str2(cStr2); michael@0: gfxTextRun *tr2 = MakeTextRun(str2.get(), str2.Length(), fontGroup, ¶ms, flags); michael@0: tr2->GetAdvanceWidth(0, str2.Length(), nullptr); michael@0: } michael@0: michael@0: delete gTextRuns; michael@0: gTextRuns = nullptr; michael@0: michael@0: }