/// load a re_vision style meta file
bool loadMetaFile(const std::string& file_name, std::string& model_name, std::string& model_type, int& face_count) {
    TiXmlDocument doc(file_name);
    bool loaded = doc.LoadFile();
    if (!loaded)
        return false;

    TiXmlHandle hdoc(&doc);
    TiXmlHandle model_handle = hdoc.FirstChildElement("model");
    TiXmlElement *model_element = model_handle.Element();
    if (model_element == NULL)
        return false;
    TiXmlElement *name_element = model_handle.FirstChildElement("name").Element();
    if (name_element == NULL)
        return false;
    model_name = name_element->GetText();
    TiXmlElement *type_element = model_handle.FirstChildElement("type").Element();
    if (type_element == NULL)
        return false;
    model_type = type_element->GetText();
    TiXmlElement *faces_element = model_handle.FirstChildElement("faces").Element();
    if (faces_element == NULL)
        return false;
    std::string faces_str = faces_element->GetText();
    face_count = boost::lexical_cast<int>(faces_str);

    return true;
}
		bool doc_database::load_description_file()
		{
			bool ret = false;

			TiXmlDocument doc;
			if (bfs::exists(_conf.get_db_description_file()))
			{
				if (doc.LoadFile(_conf.get_db_description_file()))
				{
					TiXmlHandle hdoc(&doc);

					// get <folder_structure> tag
					TiXmlElement * pfold_str = hdoc.FirstChild("folder_structure").ToElement();
					if (pfold_str)
					{
						TiXmlHandle hfold_str(pfold_str);

						// get <root_path> tag
						TiXmlElement * proot_path = hfold_str.FirstChild("root_path").ToElement();
						// get root <virtual_folder> tag
						TiXmlElement * pvirt_fold = hfold_str.FirstChild("virtual_folder").ToElement();
						// check existence of both necessary tags
						if (proot_path && pvirt_fold)
						{
							// get database root path
							_root_path = proot_path->GetText();
				
							// fill database files and v folders with a XML visitor
							v_folder_visitor * pvisitor = new v_folder_visitor(_root_path);
							pvirt_fold->Accept(pvisitor);

							// retrieve root virtual folder from visitor
							_sproot_vf = pvisitor->get_root_vf();
							delete pvisitor;	

							// DBG
							print_file_item __pred;
							_sproot_vf->for_each_child(__pred, true);

							ret = true;
						}
						else
							LERR_ << "syntax error in document database description \"" << _conf.get_db_description_file() << "\"" << " no <root_path> and/or root <virtual folder> tag found";

					}
					else
						LERR_ << "syntax error in document database description \"" << _conf.get_db_description_file() << "\"" << " no <folder_structure> tag found";
				}
				else
					LERR_ << "can not load document database description \"" << _conf.get_db_description_file() << "\"" << doc.ErrorDesc();
			}

			return ret;
		}
nsresult
nsSceneTracker::GetNextScene(nsIDOMNode* aNode,
                             nsIDOMHTMLParagraphElement** result) {
  NS_ENSURE_ARG_POINTER(result);

  nsresult rv = NS_OK;

  nsCOMPtr<nsIDOMNode> node;
  if (aNode) {
    rv = aNode->GetNextSibling(getter_AddRefs(node));
  }
  else {
    nsCOMPtr<nsIDOMHTMLDocument> hdoc(do_QueryInterface(mScript));
    if (! hdoc)
      return NS_ERROR_UNEXPECTED;

    nsCOMPtr<nsIDOMHTMLElement> body;
    rv = hdoc->GetBody(getter_AddRefs(body));
    if (NS_FAILED(rv))
      return rv;

    rv = body->GetFirstChild(getter_AddRefs(node));
  }
  if (NS_FAILED(rv))
    return rv;

  nsCOMPtr<nsIDOMHTMLParagraphElement> scene;
  nsAutoString className;

  *result = nsnull;
  while (NS_SUCCEEDED(rv) && node) {
    scene = do_QueryInterface(node);
    if (scene) {
      scene->GetClassName(className);
      if (className.Equals(NS_LITERAL_STRING("sceneheading"))) {
        *result = scene;
        NS_ADDREF(*result);
        break;
      }
    }
    scene = nsnull;
    nsCOMPtr<nsIDOMNode> tmp = node;
    rv = tmp->GetNextSibling(getter_AddRefs(node));
  }
  if (NS_FAILED(rv))
    return rv;

  return NS_OK;
}
TiXmlNode* XML_TKEM::GetLayerset()
{
	TiXmlHandle	hdoc( &doc );
	TiXmlNode*	layerset;

	layerset = hdoc.FirstChild( "trakem2" )
				.FirstChild( "t2_layer_set" )
				.ToNode();

	if( !layerset ) {
		fprintf( flog, "No <t2_layer_set> tag [%s].\n", file );
		exit( 42 );
	}

	return layerset;
}
TiXmlElement* XML_TKEM::GetFirstLayer()
{
	TiXmlHandle		hdoc( &doc );
	TiXmlElement*	layer;

	layer = hdoc.FirstChild( "trakem2" )
				.FirstChild( "t2_layer_set" )
				.FirstChild( "t2_layer" )
				.ToElement();

	if( !layer ) {
		fprintf( flog, "No <t2_layer> tag [%s].\n", file );
		exit( 42 );
	}

	return layer;
}
NS_IMETHODIMP nsSceneTracker::Shutdown () {
  nsCOMPtr<nsIDOMHTMLDocument> hdoc(do_QueryInterface(mScript));
  if (! hdoc)
    return NS_OK;

  nsCOMPtr<nsIDOMHTMLElement> body;
  nsresult rv = hdoc->GetBody(getter_AddRefs(body));
  if (NS_FAILED(rv))
    return NS_OK;

  nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(body));
  target->RemoveEventListener(NS_LITERAL_STRING("DOMNodeInserted"),
    this, PR_FALSE);
  target->RemoveEventListener(NS_LITERAL_STRING("DOMNodeRemoved"),
    this, PR_FALSE);
  target->RemoveEventListener(NS_LITERAL_STRING("DOMCharacterDataModified"),
    this, PR_FALSE);

  return NS_OK;
}
void nsScriptPaginator::AdjustNextPageBreak () {
  NS_NAMED_LITERAL_STRING(kClassStr, "class");
  NS_NAMED_LITERAL_STRING(kSoftBreakStr, "softbreak");
  NS_NAMED_LITERAL_STRING(kHardBreakStr, "hardbreak");
  NS_NAMED_LITERAL_STRING(kActStr, "act");
  NS_NAMED_LITERAL_STRING(kDivStr, "div");
  // Allow an extra line per page to accommodate bottom margins
  const PRInt32 kPageHeight = PRInt32(mLineHeight * (mLinesPerPage + 1));

  nsCOMPtr<nsIDOMNode> node = mNextPageStartsAt;
  if (! node) {
    nsCOMPtr<nsIDOMHTMLDocument> hdoc(do_QueryInterface(mScript));
    nsCOMPtr<nsIDOMHTMLElement> body;
    hdoc->GetBody(getter_AddRefs(body));
    if (! body) {
      return;
    }
    body->GetFirstChild(getter_AddRefs(node));
    mCurPageBreakNum = 0;
    if (! node) {
      return;
    }
  }

  nsCOMPtr<nsIDOMNSHTMLElement> element(do_QueryInterface(node));
  if (! element)
    return;

  PRInt32 offset = 0;
  element->GetOffsetTop(&offset);
  PRUint32 pageEnd = PRUint32(offset + kPageHeight);
  nsCOMPtr<nsIDOMNSHTMLElement> innerBreak(nsnull);

  // Adjust for height of page break if we start at one
  nsString className;
  {
    nsCOMPtr<nsIDOMElement> elemnode(do_QueryInterface(node));
    elemnode->GetAttribute(kClassStr, className);
    if (className.Equals(kSoftBreakStr) || className.Equals(kHardBreakStr)) {
      PRInt32 height;
      element->GetClientHeight(&height);
      pageEnd += height;
    }
  }

  // Basic strategy:
  // 1. Get the next node
  // 2. If the next node is undefined
  // 2.1. If the page break cache extends further
  // 2.1.1. Splice the remaining page breaks
  // 2.2. Exit
  // 3. Else if the next node is a hard break (or act)
  // 3.1. Advance the page cursor
  // 3.2. Exit
  // 4. Else if the next node is a soft break
  // 4.1. If a soft break is cached as |innerBreak|
  // 4.1.1. Remove |innerBreak|
  // 4.2. Cache the node as |innerBreak|
  // 5. Else if the node does not fit the page
  // 5.1. Find the last node (inclusive) that allows a break before it
  // 5.2. If a soft break is cached as |innerBreak|
  // 5.2.1. If |innerBreak| occurs as the previous sibling to the last node
  // 5.2.1.1. Advance the page cursor
  // 5.2.1.2. Exit
  // 5.2.2. Else
  // 5.2.2.1. Remove |innerBreak|
  // 5.2.2.2. Insert a new soft page break
  // 5.2.2.3. Advance the page cursor
  // 5.2.2.4. Exit
  // 5.3. Else
  // 5.3.1. Insert a new soft page break
  // 5.3.2. Advance the page cursor
  // 5.3.3. Exit
  // 6. Else
  // 6.1. Continue

  nsCOMPtr<nsIDOMNode> tmpnode;
  PRUint16 nodeType = 0;
  nsCOMPtr<nsIDOMNode> elemnode(do_QueryInterface(element));
  elemnode->GetNextSibling(getter_AddRefs(tmpnode));
  if (tmpnode)
    tmpnode->GetNodeType(&nodeType);
  while (tmpnode && nodeType != nsIDOMNode::ELEMENT_NODE) {
    nsCOMPtr<nsIDOMNode> tmpnode2;
    tmpnode->GetNextSibling(getter_AddRefs(tmpnode2));
    tmpnode = tmpnode2;
    if (tmpnode)
      tmpnode->GetNodeType(&nodeType);
  }

  if (tmpnode)
    element = do_QueryInterface(tmpnode);
  else
    element = nsnull;

  while (element) {
    nsCOMPtr<nsIDOMElement> elemnode(do_QueryInterface(element));
    elemnode->GetAttribute(kClassStr, className);
    element->GetOffsetTop(&offset);

    // If it's an act, make sure it's got a page break before it, unless
    // it's the first element in the script.
    if (className.Equals(kActStr)) {
      // Get the previous element to this act that isn't a page break
      elemnode->GetPreviousSibling(getter_AddRefs(tmpnode));
      /*
      if (tmpnode)
        tmpnode->GetNodeType(&nodeType);
      while (tmpnode && nodeType != nsIDOMNode::ELEMENT_NODE) {
        nsCOMPtr<nsIDOMNode> tmpnode2;
        tmpnode->GetPreviousSibling(getter_AddRefs(tmpnode2));
        tmpnode = tmpnode2;
        if (tmpnode)
          tmpnode->GetNodeType(&nodeType);
      }
      */
      while (tmpnode) {
        nsCOMPtr<nsIDOMHTMLParagraphElement> para(do_QueryInterface(tmpnode));
        if (para)
          break;
        nsCOMPtr<nsIDOMNode> tmpnode2;
        tmpnode->GetPreviousSibling(getter_AddRefs(tmpnode2));
        tmpnode = tmpnode2;
      }
      nsCOMPtr<nsIDOMElement> prev(do_QueryInterface(tmpnode));
      // If no previous element, then this isn't important
      if (prev) {
        // Otherwise, make sure the act is preceded by a page break
        prev->GetAttribute(kClassStr, className);
        if (! className.Equals(kSoftBreakStr) &&
            ! className.Equals(kHardBreakStr)) {
          // Make a break!
          nsCOMPtr<nsIDOMElement> breaknode;
          mScript->CreateElement(kDivStr, getter_AddRefs(breaknode));
          prev = do_QueryInterface(breaknode);
          prev->SetAttribute(kClassStr, kSoftBreakStr);
          nsCOMPtr<nsIDOMText> text;
          mScript->CreateTextNode(NS_LITERAL_STRING(" "), getter_AddRefs(text));
          nsCOMPtr<nsIDOMNode> dummy;
          prev->AppendChild(text, getter_AddRefs(dummy));
          nsCOMPtr<nsIDOMNode> parent;
          elemnode->GetParentNode(getter_AddRefs(parent));
          parent->InsertBefore(prev, elemnode, getter_AddRefs(dummy));
        }
        nsCOMPtr<nsIDOMNSHTMLElement> htmlprev(do_QueryInterface(prev));
        htmlprev->GetOffsetTop(&offset);
        if (mCurPageBreakNum >= mPageBreakOffsets.Length())
          mPageBreakOffsets.AppendElement((PRUint32) offset);
        else
          mPageBreakOffsets[mCurPageBreakNum] = (PRUint32) offset;
        ++mCurPageBreakNum;
        mNextPageStartsAt = do_QueryInterface(element);
        return;
      }
    }

    // Hard breaks don't move. Just record the offset and keep going.
    if (className.Equals(kHardBreakStr)) {
      element->GetOffsetTop(&offset);
      if (mCurPageBreakNum >= mPageBreakOffsets.Length())
        mPageBreakOffsets.AppendElement(offset);
      else
        mPageBreakOffsets[mCurPageBreakNum] = offset;
      ++mCurPageBreakNum;
      mNextPageStartsAt = do_QueryInterface(element);
      return;
    }

    element->GetOffsetTop(&offset);
    PRInt32 height;
    element->GetClientHeight(&height);
    if (className.Equals(kSoftBreakStr)) {
      // Soft break! Make sure it's in the right position, and eliminate
      // or move up any preceding soft break that isn't necessary.
      if (innerBreak) {
        // Remove the last inner break, and make this one the new inner
        // break. No need to adjust pageEnd, because the removed page break
        // balances the current page break.
        nsCOMPtr<nsIDOMNode> parent;
        nsCOMPtr<nsIDOMNode> innernode(do_QueryInterface(innerBreak));
        nsCOMPtr<nsIDOMNode> dummy;
        innernode->GetParentNode(getter_AddRefs(parent));
        parent->RemoveChild(innernode, getter_AddRefs(dummy));
      }
      else {
        elemnode->GetNextSibling(getter_AddRefs(tmpnode));
        if (tmpnode)
          tmpnode->GetNodeType(&nodeType);
        while (tmpnode && nodeType != nsIDOMNode::ELEMENT_NODE) {
          nsCOMPtr<nsIDOMNode> tmpnode2;
          tmpnode->GetNextSibling(getter_AddRefs(tmpnode2));
          tmpnode = tmpnode2;
          if (tmpnode)
            tmpnode->GetNodeType(&nodeType);
        }
        if (tmpnode) {
          nsCOMPtr<nsIDOMNSHTMLElement> next(do_QueryInterface(tmpnode));
          PRInt32 nextOffset = 0;
          next->GetOffsetTop(&nextOffset);
          // 
          pageEnd += PRUint32(nextOffset - offset);
        }
      }
      innerBreak = element;
    }
    else if (PRUint32(offset + height) > pageEnd) {
      // Not a soft break, but it exceeds the page length so
      // we need to paginate.

      // If we can't break here, rewind until we reach a node where we can.
      tmpnode = do_QueryInterface(element);
      while (! CanBreakBeforeNode(tmpnode) && tmpnode != mNextPageStartsAt) {
        nsCOMPtr<nsIDOMNode> prev;
        tmpnode->GetPreviousSibling(getter_AddRefs(prev));
        if (! prev)
          break;
        prev->GetNodeType(&nodeType);
        while (prev && nodeType != nsIDOMNode::ELEMENT_NODE) {
          nsCOMPtr<nsIDOMNode> tmpnode2;
          prev->GetPreviousSibling(getter_AddRefs(tmpnode2));
          if (tmpnode2)
            tmpnode2->GetNodeType(&nodeType);
          prev = tmpnode2;
        }
        tmpnode = prev;
      }

      // If |tmpnode| == |mNextPageStartsAt|, we've backtracked to the
      // previous page. This generally happens when an element is too big
      // to fit on the page. Until we can break long elements in half, keep
      // going forward until the next opportunity to insert a break.
      if (tmpnode == mNextPageStartsAt) {
        // Set |element| to the next element
        elemnode->GetNextSibling(getter_AddRefs(tmpnode));
        if (tmpnode)
          tmpnode->GetNodeType(&nodeType);
        while (tmpnode && nodeType != nsIDOMNode::ELEMENT_NODE) {
          nsCOMPtr<nsIDOMNode> tmpnode2;
          tmpnode->GetNextSibling(getter_AddRefs(tmpnode2));
          tmpnode = tmpnode2;
          if (tmpnode)
            tmpnode->GetNodeType(&nodeType);
        }
        if (tmpnode)
          element = do_QueryInterface(tmpnode);
        else
          element = nsnull;
        continue;
      }

      if (innerBreak) {
        // Move up the inner break to the current position (unless it's
        // already the previous element)
        nsCOMPtr<nsIDOMNode> prev;
        tmpnode->GetPreviousSibling(getter_AddRefs(prev));
        if (! prev)
          break;
        prev->GetNodeType(&nodeType);
        while (prev && nodeType != nsIDOMNode::ELEMENT_NODE) {
          nsCOMPtr<nsIDOMNode> tmpnode2;
          prev->GetPreviousSibling(getter_AddRefs(tmpnode2));
          if (tmpnode2)
            tmpnode2->GetNodeType(&nodeType);
          prev = tmpnode2;
        }
        nsCOMPtr<nsIDOMNode> innernode(do_QueryInterface(innerBreak));
        if (prev != innernode) {
          nsCOMPtr<nsIDOMNode> parent;
          tmpnode->GetParentNode(getter_AddRefs(parent));
          nsCOMPtr<nsIDOMNode> dummy;
          parent->InsertBefore(innernode, tmpnode, getter_AddRefs(dummy));
        }
      }
      else {
        nsCOMPtr<nsIDOMElement> pagebreak;
        mScript->CreateElement(kDivStr, getter_AddRefs(pagebreak));
        pagebreak->SetAttribute(kClassStr, kSoftBreakStr);
        nsCOMPtr<nsIDOMText> text;
        nsCOMPtr<nsIDOMNode> dummy;
        mScript->CreateTextNode(NS_LITERAL_STRING(" "), getter_AddRefs(text));
        pagebreak->AppendChild(text, getter_AddRefs(dummy));
        nsCOMPtr<nsIDOMNode> parent;
        tmpnode->GetParentNode(getter_AddRefs(parent));
        parent->InsertBefore(pagebreak, tmpnode, getter_AddRefs(dummy));
        innerBreak = do_QueryInterface(pagebreak);
      }
      innerBreak->GetOffsetTop(&offset);
      if (mCurPageBreakNum >= mPageBreakOffsets.Length())
        mPageBreakOffsets.AppendElement(offset);
      else
        mPageBreakOffsets[mCurPageBreakNum] = offset;
      ++mCurPageBreakNum;
      mNextPageStartsAt = tmpnode;
      return;
    }

    // set |element| to the next element
    // Man does XPCOM make some conventions difficult! e.g., expressing
    // while (element = element.nextSibling && element.nodeType != ...)
    elemnode->GetNextSibling(getter_AddRefs(tmpnode));
    if (tmpnode)
      tmpnode->GetNodeType(&nodeType);
    while (tmpnode && nodeType != nsIDOMNode::ELEMENT_NODE) {
      nsCOMPtr<nsIDOMNode> tmpnode2;
      tmpnode->GetNextSibling(getter_AddRefs(tmpnode2));
      tmpnode = tmpnode2;
      if (tmpnode)
        tmpnode->GetNodeType(&nodeType);
    }
    if (tmpnode)
      element = do_QueryInterface(tmpnode);
    else
      element = nsnull;
  }

  // This should be guaranteed
  if (! element) {
    // Strip away any remaining page break offsets
    if (mPageBreakOffsets.Length() > 0 &&
        mPageBreakOffsets.Length() > mCurPageBreakNum + 1) {
      mPageBreakOffsets.SetLength(mCurPageBreakNum + 1);
    }
    if (innerBreak) {
      nsCOMPtr<nsIDOMNode> parent;
      nsCOMPtr<nsIDOMNode> innernode(do_QueryInterface(innerBreak));
      nsCOMPtr<nsIDOMNode> dummy;
      innernode->GetParentNode(getter_AddRefs(parent));
      parent->RemoveChild(innernode, getter_AddRefs(dummy));
    }
    mNextPageStartsAt = nsnull;
  }
}
NS_IMETHODIMP nsScriptPaginator::AdjustSynchronously () {
  nsCOMPtr<nsIDOMXPathEvaluator> xpe = do_CreateInstance(
    "@mozilla.org/dom/xpath-evaluator;1");
  nsCOMPtr<nsIDOMXPathResult> result;
  nsresult rv = xpe->Evaluate(
    NS_LITERAL_STRING("//div[@class='softbreak' or @class='hardbreak']"),
    mScript, nsnull, nsIDOMXPathResult::ORDERED_NODE_ITERATOR_TYPE, nsnull,
    getter_AddRefs(result));
  if (NS_FAILED(rv))
    return rv;

  nsCOMPtr<nsIDOMNSHTMLElement> lastbreak;
  nsCOMPtr<nsIDOMNSHTMLElement> pagebreak;
  nsCOMPtr<nsIDOMNode> cursor;
  PRUint32 breaknum = 0;

  rv = result->IterateNext(getter_AddRefs(cursor));
  while (NS_SUCCEEDED(rv) && cursor) {
    pagebreak = do_QueryInterface(cursor);
    if (pagebreak) {
      PRInt32 offset = 0;
      pagebreak->GetOffsetTop(&offset);
      if (breaknum >= mPageBreakOffsets.Length() || 
          PRUint32(offset) != mPageBreakOffsets[breaknum])
        break;
      lastbreak = pagebreak;
      ++breaknum;
    }
    rv = result->IterateNext(getter_AddRefs(cursor));
  }
  if (NS_FAILED(rv))
    return rv;

  CalculateFontMetrics();

  // Adjust from the last valid page break (or the start) onwards
  if (lastbreak) {
    nsCOMPtr<nsIDOMNode> lastbreaknode(do_QueryInterface(lastbreak));
    lastbreaknode->GetNextSibling(getter_AddRefs(cursor));
  }
  else {
    nsCOMPtr<nsIDOMHTMLDocument> hdoc(do_QueryInterface(mScript));
    hdoc->GetFirstChild(getter_AddRefs(cursor));
  }
  PRUint16 nodeType = 0;
  while (cursor) {
    cursor->GetNodeType(&nodeType);
    if (nodeType == nsIDOMNode::ELEMENT_NODE)
      break;
    nsCOMPtr<nsIDOMNode> tmp;
    cursor->GetNextSibling(getter_AddRefs(tmp));
    cursor = tmp;
  }
  mNextPageStartsAt = cursor;
  mCurPageBreakNum = breaknum;

  mEditor->BeginTransaction();
  while (mNextPageStartsAt)
    AdjustNextPageBreak();
  mEditor->EndTransaction();

  mPageCount = mPageBreakOffsets.Length() + 1;

  mLastPaginationEnded = nsTime();

  return NS_OK;
}
NS_IMETHODIMP nsScriptPaginator::AdjustPageBreaks () {
  // Update less frequently if we're not in the middle of pagination
  if ((mUpdateCount++ % 5) != 0 && ! mNextPageStartsAt)
    return NS_OK;

  PRInt32 modificationCount;
  mEditor->GetModificationCount(&modificationCount);
  if (modificationCount == mModificationCount)
    return NS_OK;

  mModificationCount = modificationCount;

  nsCOMPtr<nsIDOMXPathEvaluator> xpe = do_CreateInstance(
    "@mozilla.org/dom/xpath-evaluator;1");
  nsCOMPtr<nsIDOMXPathResult> result;
  nsresult rv = xpe->Evaluate(
    NS_LITERAL_STRING("//div[@class='softbreak' or @class='hardbreak']"),
    mScript, nsnull, nsIDOMXPathResult::ORDERED_NODE_ITERATOR_TYPE, nsnull,
    getter_AddRefs(result));
  if (NS_FAILED(rv))
    return rv;

  // |lastbreak| stores the last known correct page break
  nsCOMPtr<nsIDOMNSHTMLElement> lastbreak;
  nsCOMPtr<nsIDOMNSHTMLElement> pagebreak;
  PRUint32 breaknum = 0;

  // Determine where the first adjustment needs to be made
  nsCOMPtr<nsIDOMNode> cursor;
  rv = result->IterateNext(getter_AddRefs(cursor));
  while (NS_SUCCEEDED(rv) && cursor) {
    pagebreak = do_QueryInterface(cursor);
    if (pagebreak) {
      PRInt32 offset = 0;
      pagebreak->GetOffsetTop(&offset);
      if (breaknum >= mPageBreakOffsets.Length() || 
          PRUint32(offset) != mPageBreakOffsets[breaknum])
        break;
      lastbreak = pagebreak;
      ++breaknum;
    }
    rv = result->IterateNext(getter_AddRefs(cursor));
  }
  if (NS_FAILED(rv))
    return rv;

  // We're starting from scratch, might as well confirm font metrics
  if (! mNextPageStartsAt)
    CalculateFontMetrics();

  // Adjust all the way to the end, or until enough time has passed
  if (lastbreak) {
    nsCOMPtr<nsIDOMNode> lastbreaknode(do_QueryInterface(lastbreak));
    lastbreaknode->GetNextSibling(getter_AddRefs(cursor));
  }
  else {
    nsCOMPtr<nsIDOMHTMLDocument> hdoc(do_QueryInterface(mScript));
    nsCOMPtr<nsIDOMHTMLElement> body;
    hdoc->GetBody(getter_AddRefs(body));
    body->GetFirstChild(getter_AddRefs(cursor));
  }
  PRUint16 nodeType = 0;
  while (cursor) {
    cursor->GetNodeType(&nodeType);
    if (nodeType == nsIDOMNode::ELEMENT_NODE)
      break;
    nsCOMPtr<nsIDOMNode> tmp;
    cursor->GetNextSibling(getter_AddRefs(tmp));
    cursor = tmp;
  }
  mNextPageStartsAt = cursor;
  mCurPageBreakNum = breaknum;

  nsTime start;
  nsTime end = start + nsTime(500000);

  mEditor->BeginTransaction();
  while (mNextPageStartsAt && nsTime() < end) {
    AdjustNextPageBreak();
  }
  mEditor->EndTransaction();

  mPageCount = mPageBreakOffsets.Length() + 1;

  mLastPaginationEnded = nsTime();

  return NS_OK;
}
NS_IMETHODIMP nsSceneTracker::Init (nsIRDFDataSource* ds,
                                    nsIRDFResource* docres,
                                    nsIScriptEditor* editor) {
  NS_ENSURE_ARG_POINTER(ds);
  NS_ENSURE_ARG_POINTER(docres);
  NS_ENSURE_ARG_POINTER(editor);

  mDS = ds;
  mDocres = docres;
  mEditor = editor;
  nsresult rv;

  rv = mEditor->GetContentDocument(getter_AddRefs(mScript));
  if (NS_FAILED(rv))
    return rv;

  mNumberSvc = do_GetService("@celtx.com/scene-number-service;1");

  mRDFSvc = do_GetService("@mozilla.org/rdf/rdf-service;1");
  nsCOMPtr<nsIRDFContainerUtils> cu = do_GetService(
    "@mozilla.org/rdf/container-utils;1");
  nsCOMPtr<nsIRDFResource> scenesarc;
  mRDFSvc->GetResource(NS_LITERAL_CSTRING("http://celtx.com/NS/v1/scenes"),
    getter_AddRefs(scenesarc));

  nsCOMPtr<nsIRDFResource> scenesres;
  nsIRDFNode* scenesnode;
  mDS->GetTarget(mDocres, scenesarc, PR_TRUE, &scenesnode);
  if (scenesnode) {
    scenesres = do_QueryInterface(scenesnode);
    NS_RELEASE(scenesnode);
    if (! scenesres)
      return NS_ERROR_NO_INTERFACE;
  }
  else {
    rv = mRDFSvc->GetAnonymousResource(getter_AddRefs(scenesres));
    if (NS_FAILED(rv))
      return rv;
    mDS->Assert(mDocres, scenesarc, scenesres, PR_TRUE);
  }
  rv = CeltxRDFUtils::GetRDFSeq(mDS, scenesres, getter_AddRefs(mScenes));
  if (NS_FAILED(rv))
    return rv;

  nsCOMPtr<nsIDOMHTMLDocument> hdoc(do_QueryInterface(mScript));
  if (! hdoc)
    return NS_ERROR_INVALID_ARG;

  nsCOMPtr<nsIDOMHTMLElement> body;
  rv = hdoc->GetBody(getter_AddRefs(body));
  if (NS_FAILED(rv))
    return NS_ERROR_UNEXPECTED;

  nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(body));
  target->AddEventListener(NS_LITERAL_STRING("DOMNodeInserted"),
    this, PR_FALSE);
  target->AddEventListener(NS_LITERAL_STRING("DOMNodeRemoved"),
    this, PR_FALSE);
  target->AddEventListener(NS_LITERAL_STRING("DOMCharacterDataModified"),
    this, PR_FALSE);

  rv = Update();
  if (NS_FAILED(rv))
    return rv;

  PRInt32 revision;
  rv = mEditor->GetRevisionNumber(&revision);
  if (NS_SUCCEEDED(rv) && ! revision)
    rv = ResetSceneNumbers();

  if (NS_SUCCEEDED(rv))
    // Don't let a failure to update literals cause total failure
    UpdateAllSceneNumberLiterals();

  return rv;
}