author Ben Turner <>
Fri, 04 Feb 2011 11:48:59 -0800
changeset 61948 9414d1eaeeeb0342fc6e44e6c4efcdb10f213803
parent 54254 0ca9e1e100fe0e6b0bdc8bb7ea2fe6dfbdc2ae04
child 70930 1abe9e62753397f24176e42e1b5ea114b95d44e0
permissions -rw-r--r--
Bug 631452 - 'LazyIdleThread can race with newly dispatched events when shutting down'. r=sdwilsh, a=blocking.

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 * The Original Code is Mozilla Corporation code.
 * The Initial Developer of the Original Code is Mozilla Foundation.
 * Portions created by the Initial Developer are Copyright (C) 2007
 * the Initial Developer. All Rights Reserved.
 * Contributor(s):
 *   Vladimir Vukicevic <>
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 * ***** END LICENSE BLOCK ***** */

#include "nsCOMPtr.h"
#include "nsTArray.h"
#include "nsString.h"
#include "nsDependentString.h"

#include "prinrval.h"

#include "nsServiceManagerUtils.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"

#include "gfxContext.h"
#include "gfxFont.h"
#include "gfxPlatform.h"

#include "gfxFontTest.h"

#include "gfxTextRunWordCache.h"

#if defined(XP_MACOSX)
#include "gfxTestCocoaHelper.h"

#include "gtk/gtk.h"

class FrameTextRunCache;

static FrameTextRunCache *gTextRuns = nsnull;

 * Cache textruns and expire them after 3*10 seconds of no use.
class FrameTextRunCache : public nsExpirationTracker<gfxTextRun,3> {
 enum { TIMEOUT_SECONDS = 10 };
     : nsExpirationTracker<gfxTextRun,3>(TIMEOUT_SECONDS*1000) {}
 ~FrameTextRunCache() {

 void RemoveFromCache(gfxTextRun* aTextRun) {
   if (aTextRun->GetExpirationState()->IsTracked()) {

 // This gets called when the timeout has expired on a gfxTextRun
 virtual void NotifyExpired(gfxTextRun* aTextRun) {
   delete aTextRun;

static gfxTextRun *
MakeTextRun(const PRUnichar *aText, PRUint32 aLength,
           gfxFontGroup *aFontGroup, const gfxFontGroup::Parameters* aParams,
           PRUint32 aFlags)
   nsAutoPtr<gfxTextRun> textRun;
   if (aLength == 0) {
       textRun = aFontGroup->MakeEmptyTextRun(aParams, aFlags);
   } else if (aLength == 1 && aText[0] == ' ') {
       textRun = aFontGroup->MakeSpaceTextRun(aParams, aFlags);
   } else {
       textRun = gfxTextRunWordCache::MakeTextRun(aText, aLength, aFontGroup,
           aParams, aFlags);
   if (!textRun)
       return nsnull;
   nsresult rv = gTextRuns->AddObject(textRun);
   if (NS_FAILED(rv)) {
       return nsnull;
   return textRun.forget();

MakeContext ()
   const int size = 200;

   nsRefPtr<gfxASurface> surface;

   surface = gfxPlatform::GetPlatform()->
       CreateOffscreenSurface(gfxIntSize(size, size),
   gfxContext *ctx = new gfxContext(surface);
   return ctx;

main (int argc, char **argv) {
   gtk_init(&argc, &argv);
#ifdef XP_MACOSX

   // Initialize XPCOM
   nsresult rv = NS_InitXPCOM2(nsnull, nsnull, nsnull);
   if (NS_FAILED(rv))
       return -1;

   rv = gfxPlatform::Init();
   if (NS_FAILED(rv))
       return -1;

   // let's get all the xpcom goop out of the system
   fflush (stderr);
   fflush (stdout);

   gTextRuns = new FrameTextRunCache();

   nsRefPtr<gfxContext> ctx = MakeContext();
       gfxFontStyle style (FONT_STYLE_NORMAL,
                           PR_FALSE, PR_FALSE, PR_FALSE,

       nsRefPtr<gfxFontGroup> fontGroup =
           gfxPlatform::GetPlatform()->CreateFontGroup(NS_LITERAL_STRING("Geneva, MS Sans Serif, Helvetica,serif"), &style, nsnull);

       gfxTextRunFactory::Parameters params = {
           ctx, nsnull, nsnull, nsnull, 0, 60

       PRUint32 flags = gfxTextRunFactory::TEXT_IS_PERSISTENT;

       // First load an Arabic word into the cache
       const char cString[] = "\xd8\xaa\xd9\x85";
       nsDependentCString cStr(cString);
       NS_ConvertUTF8toUTF16 str(cStr);
       gfxTextRun *tr = MakeTextRun(str.get(), str.Length(), fontGroup, &params, flags);
       tr->GetAdvanceWidth(0, str.Length(), nsnull);

       // Now try to trigger an assertion with a word cache bug. The first
       // word is in the cache so it gets added to the new textrun directly.
       // The second word is not in the cache 
       const char cString2[] = "\xd8\xaa\xd9\x85\n\xd8\xaa\xd8\x85 ";
       nsDependentCString cStr2(cString2);
       NS_ConvertUTF8toUTF16 str2(cStr2);
       gfxTextRun *tr2 = MakeTextRun(str2.get(), str2.Length(), fontGroup, &params, flags);
       tr2->GetAdvanceWidth(0, str2.Length(), nsnull);

   fflush (stderr);
   fflush (stdout);