Back to index

lightning-sunbird  0.9+nobinonly
nsUICommandCollector.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* vim:set ts=2 sw=2 sts=2 et cindent: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the Metrics extension.
00017  *
00018  * The Initial Developer of the Original Code is Google Inc.
00019  * Portions created by the Initial Developer are Copyright (C) 2006
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *  Marria Nazif <marria@gmail.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsUICommandCollector.h"
00040 #include "nsMetricsService.h"
00041 
00042 #include "nsServiceManagerUtils.h"
00043 #include "nsIObserverService.h"
00044 #include "nsIDOMEventTarget.h"
00045 #include "nsIDOMEvent.h"
00046 #include "nsIDOMNSEvent.h"
00047 #include "nsIDOMXULCommandEvent.h"
00048 #include "nsIDOMElement.h"
00049 #include "nsIDOMWindow.h"
00050 #include "nsDataHashtable.h"
00051 #include "nsMemory.h"
00052 #ifndef MOZ_PLACES
00053 #include "nsIRDFService.h"
00054 #include "nsIRDFResource.h"
00055 #include "nsIRDFContainer.h"
00056 #include "nsIBookmarksService.h"
00057 #include "nsIArray.h"
00058 #include "nsComponentManagerUtils.h"
00059 #endif
00060 
00061 const nsUICommandCollector::EventHandler nsUICommandCollector::kEvents[] = {
00062   { "command", &nsUICommandCollector::HandleCommandEvent },
00063   { "TabMove", &nsUICommandCollector::HandleTabMoveEvent },
00064 };
00065 
00066 NS_IMPL_ISUPPORTS3(nsUICommandCollector, nsIObserver, nsIDOMEventListener,
00067                    nsIMetricsCollector)
00068 
00069 /* static */
00070 PLDHashOperator PR_CALLBACK nsUICommandCollector::AddCommandEventListener(
00071 const nsIDOMWindow* key, PRUint32 windowID, void* userArg)
00072 {
00073   nsCOMPtr<nsIDOMEventTarget> windowTarget =
00074     do_QueryInterface(NS_CONST_CAST(nsIDOMWindow *, key));
00075   if (!windowTarget) {
00076     MS_LOG(("Error casting domeventtarget"));
00077     return PL_DHASH_NEXT;
00078   }
00079 
00080   nsUICommandCollector* collector = NS_STATIC_CAST(nsUICommandCollector*,
00081                                                    userArg);
00082   collector->AddEventListeners(windowTarget);
00083   return PL_DHASH_NEXT;
00084 }
00085 
00086 /* static */
00087 PLDHashOperator PR_CALLBACK nsUICommandCollector::RemoveCommandEventListener(
00088 const nsIDOMWindow* key, PRUint32 windowID, void* userArg)
00089 {
00090   nsCOMPtr<nsIDOMEventTarget> windowTarget =
00091     do_QueryInterface(NS_CONST_CAST(nsIDOMWindow *, key));
00092   if (!windowTarget) {
00093     MS_LOG(("Error casting domeventtarget"));
00094     return PL_DHASH_NEXT;
00095   }
00096 
00097   nsUICommandCollector* collector = NS_STATIC_CAST(nsUICommandCollector*,
00098                                                    userArg);
00099   collector->RemoveEventListeners(windowTarget);
00100   return PL_DHASH_NEXT;
00101 }
00102 
00103 nsUICommandCollector::nsUICommandCollector()
00104 {
00105 }
00106 
00107 nsUICommandCollector::~nsUICommandCollector()
00108 {
00109 }
00110 
00111 // nsIMetricsCollector
00112 NS_IMETHODIMP
00113 nsUICommandCollector::OnAttach()
00114 {
00115   nsresult rv;
00116   nsCOMPtr<nsIObserverService> obsSvc =
00117     do_GetService("@mozilla.org/observer-service;1");
00118   NS_ENSURE_STATE(obsSvc);
00119 
00120   // Listen for newly opened windows, so that we can attach a command event
00121   // listener to each window
00122   rv = obsSvc->AddObserver(this, "domwindowopened", PR_FALSE);
00123   NS_ENSURE_SUCCESS(rv, rv);
00124 
00125   // Attach to all existing windows
00126   nsMetricsService *ms = nsMetricsService::get();
00127   NS_ENSURE_STATE(ms);
00128 
00129   ms->WindowMap().EnumerateRead(AddCommandEventListener,
00130                                 NS_STATIC_CAST(nsIDOMEventListener*, this));
00131 
00132   return NS_OK;
00133 }
00134 
00135 NS_IMETHODIMP
00136 nsUICommandCollector::OnDetach()
00137 {
00138   nsresult rv;
00139   nsCOMPtr<nsIObserverService> obsSvc =
00140     do_GetService("@mozilla.org/observer-service;1");
00141   NS_ENSURE_STATE(obsSvc);
00142 
00143   // Remove our observer for open windows
00144   rv = obsSvc->RemoveObserver(this, "domwindowopened");
00145   NS_ENSURE_SUCCESS(rv, rv);
00146 
00147   // Also iterate through all windows and try to remove command event
00148   // listeners.  It is possible that we never attached one to some
00149   // of the windows (if we were detached and then attached) so
00150   // continue on even if it fails
00151   nsMetricsService *ms = nsMetricsService::get();
00152   NS_ENSURE_STATE(ms);
00153 
00154   ms->WindowMap().EnumerateRead(RemoveCommandEventListener,
00155     NS_STATIC_CAST(nsIDOMEventListener*, this));
00156 
00157   return NS_OK;
00158 }
00159 
00160 NS_IMETHODIMP
00161 nsUICommandCollector::OnNewLog()
00162 {
00163   return NS_OK;
00164 }
00165 
00166 // nsIObserver
00167 NS_IMETHODIMP
00168 nsUICommandCollector::Observe(nsISupports *subject,
00169                               const char *topic,
00170                               const PRUnichar *data)
00171 {
00172   if (strcmp(topic, "domwindowopened") == 0) {
00173     nsCOMPtr<nsIDOMEventTarget> window = do_QueryInterface(subject);
00174     NS_ENSURE_STATE(window);
00175     AddEventListeners(window);
00176   }
00177 
00178   return NS_OK;
00179 }
00180 
00181 // nsIDOMEventListener
00182 NS_IMETHODIMP
00183 nsUICommandCollector::HandleEvent(nsIDOMEvent* event)
00184 {
00185   // First check that this is an event type that we expect.
00186   // If so, call the appropriate handler method.
00187   nsString type;
00188   nsresult rv = event->GetType(type);
00189   NS_ENSURE_SUCCESS(rv, rv);
00190 
00191   for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(kEvents); ++i) {
00192     if (NS_ConvertASCIItoUTF16(kEvents[i].event).Equals(type)) {
00193       return (this->*(kEvents[i].handler))(event);
00194     }
00195   }
00196 
00197   MS_LOG(("UICommandCollector: Unexpected event type %s received",
00198           NS_ConvertUTF16toUTF8(type).get()));
00199   return NS_ERROR_UNEXPECTED;
00200 }
00201 
00202 nsresult
00203 nsUICommandCollector::HandleCommandEvent(nsIDOMEvent* event)
00204 {
00205   PRUint32 window;
00206   if (NS_FAILED(GetEventWindow(event, &window))) {
00207     return NS_OK;
00208   }
00209 
00210   nsString targetId, targetAnonId;
00211   if (NS_FAILED(GetEventTargets(event, targetId, targetAnonId))) {
00212     return NS_OK;
00213   }
00214   NS_ASSERTION(!targetId.IsEmpty(), "can't have an empty target id");
00215 
00216   nsString keyId;
00217   GetEventKeyId(event, keyId);
00218 
00219   // Fill a property bag with what we want to log
00220   nsCOMPtr<nsIWritablePropertyBag2> properties;
00221   nsMetricsUtils::NewPropertyBag(getter_AddRefs(properties));
00222   NS_ENSURE_STATE(properties);
00223 
00224   nsresult rv;
00225   rv = properties->SetPropertyAsUint32(NS_LITERAL_STRING("window"), window);
00226   NS_ENSURE_SUCCESS(rv, rv);
00227 
00228   rv = properties->SetPropertyAsAString(NS_LITERAL_STRING("action"),
00229                                         NS_LITERAL_STRING("command"));
00230   NS_ENSURE_SUCCESS(rv, rv);
00231 
00232   rv = SetHashedValue(properties, NS_LITERAL_STRING("targetidhash"), targetId);
00233   NS_ENSURE_SUCCESS(rv, rv);
00234 
00235   if (!targetAnonId.IsEmpty()) {
00236     rv = SetHashedValue(properties, NS_LITERAL_STRING("targetanonidhash"),
00237                         targetAnonId);
00238     NS_ENSURE_SUCCESS(rv, rv);
00239   }
00240   if (!keyId.IsEmpty()) {
00241     rv = SetHashedValue(properties, NS_LITERAL_STRING("keyidhash"), keyId);
00242     NS_ENSURE_SUCCESS(rv, rv);
00243   }
00244 
00245   nsMetricsService *ms = nsMetricsService::get();
00246   NS_ENSURE_STATE(ms);
00247 
00248   nsCOMPtr<nsIMetricsEventItem> item;
00249   ms->CreateEventItem(NS_LITERAL_STRING("uielement"), getter_AddRefs(item));
00250   NS_ENSURE_STATE(item);
00251   item->SetProperties(properties);
00252 
00253   // Capture extra bookmark state onto the event if the target is a bookmark.
00254   rv = LogBookmarkInfo(targetId, item);
00255   NS_ENSURE_SUCCESS(rv, rv);
00256 
00257   // Actually log it
00258   rv = ms->LogEvent(item);
00259   NS_ENSURE_SUCCESS(rv, rv);
00260 
00261   MS_LOG(("Successfully logged UI Event"));
00262   return NS_OK;
00263 }
00264 
00265 nsresult
00266 nsUICommandCollector::HandleTabMoveEvent(nsIDOMEvent* event)
00267 {
00268   PRUint32 window;
00269   if (NS_FAILED(GetEventWindow(event, &window))) {
00270     return NS_OK;
00271   }
00272 
00273   // Fill a property bag with what we want to log
00274   nsCOMPtr<nsIWritablePropertyBag2> properties;
00275   nsMetricsUtils::NewPropertyBag(getter_AddRefs(properties));
00276   NS_ENSURE_STATE(properties);
00277 
00278   nsresult rv;
00279   rv = properties->SetPropertyAsUint32(NS_LITERAL_STRING("window"), window);
00280   NS_ENSURE_SUCCESS(rv, rv);
00281 
00282   rv = properties->SetPropertyAsAString(NS_LITERAL_STRING("action"),
00283                                         NS_LITERAL_STRING("comand"));
00284   NS_ENSURE_SUCCESS(rv, rv);
00285 
00286   // TabMove events just have a dummy target id of "TabMove_Event".
00287   rv = SetHashedValue(properties, NS_LITERAL_STRING("targetidhash"),
00288                       NS_LITERAL_STRING("TabMove_Event"));
00289   NS_ENSURE_SUCCESS(rv, rv);
00290 
00291   nsMetricsService *ms = nsMetricsService::get();
00292   NS_ENSURE_STATE(ms);
00293 
00294   rv = ms->LogEvent(NS_LITERAL_STRING("uielement"), properties);
00295   NS_ENSURE_SUCCESS(rv, rv);
00296 
00297   MS_LOG(("Successfully logged UI Event"));
00298   return NS_OK;
00299 }
00300 
00301 nsresult
00302 nsUICommandCollector::GetEventTargets(nsIDOMEvent *event,
00303                                       nsString &targetId,
00304                                       nsString &targetAnonId) const
00305 {
00306   // This code deals with both anonymous and explicit (non-anonymous) content.
00307   //
00308   // For explicit content, we just return the id of the event target in
00309   // targetId, and leave targetAnonId empty.  If there is no id, then
00310   // we return failure.
00311   //
00312   // For anonymous content, we return the id of the event target (after
00313   // retargeting), in targetId.  We return the anonid of the event's
00314   // originalTarget in targetAnonId, so that XBL child elements can be
00315   // distinguished.  If there is no anonid, then we return failure.
00316   // Note that the originalTarget is set after text node retargting, but
00317   // before any XBL retargeting.
00318   //
00319   // We assume that if the originalTarget has no id, we're dealing with
00320   // anonymous content (this isn't always true, but it's good enough for what
00321   // this code does).
00322 
00323   nsCOMPtr<nsIDOMNSEvent> nsEvent = do_QueryInterface(event);
00324   NS_ENSURE_STATE(nsEvent);
00325 
00326   nsCOMPtr<nsIDOMEventTarget> originalTarget;
00327   nsEvent->GetOriginalTarget(getter_AddRefs(originalTarget));
00328   NS_ENSURE_STATE(originalTarget);
00329 
00330   nsString origElementId;
00331   nsCOMPtr<nsIDOMElement> origElement(do_QueryInterface(originalTarget));
00332   if (origElement) {
00333     origElement->GetAttribute(NS_LITERAL_STRING("id"), origElementId);
00334     origElement->GetAttribute(NS_LITERAL_STRING("anonid"), targetAnonId);
00335   }
00336 
00337   nsCOMPtr<nsIDOMEventTarget> target;
00338   event->GetTarget(getter_AddRefs(target));
00339   NS_ENSURE_STATE(target);
00340 
00341   nsCOMPtr<nsIDOMElement> targetElement(do_QueryInterface(target));
00342   if (targetElement) {
00343     targetElement->GetAttribute(NS_LITERAL_STRING("id"), targetId);
00344   }
00345 
00346   MS_LOG(("Original Target Id: %s, Original Target Anonid: %s, Target Id: %s",
00347           NS_ConvertUTF16toUTF8(origElementId).get(),
00348           NS_ConvertUTF16toUTF8(targetAnonId).get(),
00349           NS_ConvertUTF16toUTF8(targetId).get()));
00350 
00351   if (targetId.IsEmpty()) {
00352     // There's nothing useful to log in this case -- even if we have an anonid,
00353     // it's not possible to determine its position in the document.
00354     MS_LOG(("Warning: skipping logging because of empty target ID"));
00355     return NS_ERROR_FAILURE;
00356   }
00357 
00358   if (origElementId.IsEmpty()) {
00359     // We're dealing with anonymous content, so don't continue if we didn't
00360     // find an anonid.
00361     if (targetAnonId.IsEmpty()) {
00362       MS_LOG(("Warning: skipping logging because of empty anonid"));
00363       return NS_ERROR_FAILURE;
00364     }
00365   } else {
00366     // We're dealing with normal explicit content, so don't return an anonid.
00367     targetAnonId.SetLength(0);
00368   }
00369 
00370   return NS_OK;
00371 }
00372 
00373 void
00374 nsUICommandCollector::GetEventKeyId(nsIDOMEvent *event, nsString &keyId) const
00375 {
00376   // The source event will give us the original event in the case where a new
00377   // event was dispatched due to a command= attribute.
00378   nsCOMPtr<nsIDOMXULCommandEvent> commandEvent = do_QueryInterface(event);
00379   if (!commandEvent) {
00380     // nsIDOMXULCommandEvent is only in Gecko 1.8.1+
00381     return;
00382   }
00383 
00384   nsCOMPtr<nsIDOMEvent> sourceEvent;
00385   commandEvent->GetSourceEvent(getter_AddRefs(sourceEvent));
00386   if (!sourceEvent) {
00387     return;
00388   }
00389 
00390   nsCOMPtr<nsIDOMEventTarget> sourceEventTarget;
00391   sourceEvent->GetTarget(getter_AddRefs(sourceEventTarget));
00392   nsCOMPtr<nsIDOMElement> sourceElement = do_QueryInterface(sourceEventTarget);
00393   if (!sourceElement) {
00394     return;
00395   }
00396 
00397   nsString namespaceURI, localName;
00398   sourceElement->GetNamespaceURI(namespaceURI);
00399   sourceElement->GetLocalName(localName);
00400   if (namespaceURI.Equals(NS_LITERAL_STRING("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul")) &&
00401       localName.Equals(NS_LITERAL_STRING("key"))) {
00402     sourceElement->GetAttribute(NS_LITERAL_STRING("id"), keyId);
00403     MS_LOG(("Key Id: %s", NS_ConvertUTF16toUTF8(keyId).get()));
00404   }
00405 }
00406 
00407 nsresult
00408 nsUICommandCollector::GetEventWindow(nsIDOMEvent *event,
00409                                      PRUint32 *window) const
00410 {
00411   nsCOMPtr<nsIDOMEventTarget> target;
00412   event->GetTarget(getter_AddRefs(target));
00413   nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(target);
00414   if (!targetNode) {
00415     MS_LOG(("Warning: couldn't get window id because target is not a node"));
00416     return NS_ERROR_FAILURE;
00417   }
00418 
00419   *window = nsMetricsUtils::FindWindowForNode(targetNode);
00420   return NS_OK;
00421 }
00422 
00423 void
00424 nsUICommandCollector::AddEventListeners(nsIDOMEventTarget *window)
00425 {
00426   for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(kEvents); ++i) {
00427     // Attach a capturing event listener to the window.
00428     // Use capturing instead of bubbling so that we still record
00429     // the event even if propagation is canceled for some reason.
00430 
00431     // Using NS_LITERAL_STRING in const data is not allowed, so convert here.
00432     // This is not performance-sensitive.
00433     nsresult rv;
00434     rv = window->AddEventListener(NS_ConvertASCIItoUTF16(kEvents[i].event),
00435                                   this, PR_TRUE);
00436     if (NS_FAILED(rv)) {
00437       MS_LOG(("Couldn't add event listener %s", kEvents[i]));
00438     }
00439   }
00440 }
00441 
00442 void
00443 nsUICommandCollector::RemoveEventListeners(nsIDOMEventTarget *window)
00444 {
00445   for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(kEvents); ++i) {
00446     // Using NS_LITERAL_STRING in const data is not allowed, so convert here.
00447     // This is not performance-sensitive.
00448     nsresult rv;
00449     rv = window->RemoveEventListener(NS_ConvertASCIItoUTF16(kEvents[i].event),
00450                                      this, PR_TRUE);
00451     if (NS_FAILED(rv)) {
00452       MS_LOG(("Couldn't remove event listener %s", kEvents[i]));
00453     }
00454   }
00455 }
00456 
00457 /* static */ nsresult
00458 nsUICommandCollector::SetHashedValue(nsIWritablePropertyBag2 *properties,
00459                                      const nsString &propertyName,
00460                                      const nsString &propertyValue) const
00461 {
00462   nsMetricsService *ms = nsMetricsService::get();
00463   NS_ENSURE_STATE(ms);
00464 
00465   nsCString hashedValue;
00466   nsresult rv = ms->HashUTF16(propertyValue, hashedValue);
00467   NS_ENSURE_SUCCESS(rv, rv);
00468 
00469   return properties->SetPropertyAsACString(propertyName, hashedValue);
00470 }
00471 
00472 nsresult
00473 nsUICommandCollector::LogBookmarkInfo(const nsString& id,
00474                                       nsIMetricsEventItem* parentItem)
00475 {
00476 #ifdef MOZ_PLACES
00477   // TODO: write me!
00478   return NS_OK;
00479 
00480 #else
00481 
00482   // First check whether this is an anonymous RDF id.
00483   // If it's not, we know it's not a bookmark id at all.
00484   if (!StringHead(id, strlen("rdf:")).Equals(NS_LITERAL_STRING("rdf:"))) {
00485     return NS_OK;
00486   }
00487 
00488   nsCOMPtr<nsIRDFService> rdfSvc =
00489     do_GetService("@mozilla.org/rdf/rdf-service;1");
00490   NS_ENSURE_STATE(rdfSvc);
00491 
00492   nsCOMPtr<nsIRDFResource> targetResource;
00493   rdfSvc->GetUnicodeResource(id, getter_AddRefs(targetResource));
00494   NS_ENSURE_STATE(targetResource);
00495 
00496   nsCOMPtr<nsIWritablePropertyBag2> bmProps;
00497   nsMetricsUtils::NewPropertyBag(getter_AddRefs(bmProps));
00498   NS_ENSURE_STATE(bmProps);
00499 
00500   nsCOMPtr<nsIBookmarksService> bmSvc =
00501     do_GetService(NS_BOOKMARKS_SERVICE_CONTRACTID);
00502   NS_ENSURE_STATE(bmSvc);
00503 
00504   nsCOMPtr<nsIArray> parentChain;
00505   bmSvc->GetParentChain(targetResource, getter_AddRefs(parentChain));
00506   NS_ENSURE_STATE(parentChain);
00507 
00508   PRUint32 depth = 0;
00509   parentChain->GetLength(&depth);
00510   bmProps->SetPropertyAsInt32(NS_LITERAL_STRING("depth"), depth);
00511   if (depth == 0) {
00512     // Hm, an event on the bookmarks root?  Not much to log in this case.
00513     return NS_OK;
00514   }
00515 
00516   nsCOMPtr<nsIRDFDataSource> bmDS =
00517     do_GetService(NS_BOOKMARKS_DATASOURCE_CONTRACTID);
00518   NS_ENSURE_STATE(bmDS);
00519 
00520   // Find the bookmark's position in its parent folder.
00521   // do_QueryElementAt() isn't a frozen export :-(
00522   nsCOMPtr<nsIRDFResource> parent;
00523   parentChain->QueryElementAt(depth - 1, NS_GET_IID(nsIRDFResource),
00524                               getter_AddRefs(parent));
00525   NS_ENSURE_STATE(parent);
00526 
00527   nsCOMPtr<nsIRDFContainer> container =
00528     do_CreateInstance("@mozilla.org/rdf/container;1");
00529   NS_ENSURE_STATE(container);
00530 
00531   nsresult rv = container->Init(bmDS, parent);
00532   NS_ENSURE_SUCCESS(rv, rv);
00533 
00534   PRInt32 pos;
00535   rv = container->IndexOf(targetResource, &pos);
00536   NS_ENSURE_SUCCESS(rv, rv);
00537   if (pos == -1) {
00538     MS_LOG(("Bookmark not contained in its parent?"));
00539   } else {
00540     bmProps->SetPropertyAsInt32(NS_LITERAL_STRING("pos"), pos);
00541   }
00542 
00543   // Determine whether the bookmark is under the toolbar folder
00544   PRBool isToolbarBM = PR_FALSE;
00545   nsCOMPtr<nsIRDFResource> toolbarFolder;
00546   bmSvc->GetBookmarksToolbarFolder(getter_AddRefs(toolbarFolder));
00547   if (toolbarFolder) {
00548     // Since the user can designate any folder as the toolbar folder,
00549     // we must walk the entire parent chain looking for it.
00550     for (PRUint32 i = 0; i < depth; ++i) {
00551       nsCOMPtr<nsIRDFResource> item;
00552       parentChain->QueryElementAt(i, NS_GET_IID(nsIRDFResource),
00553                                   getter_AddRefs(item));
00554       if (toolbarFolder == item) {
00555         isToolbarBM = PR_TRUE;
00556         break;
00557       }
00558     }
00559   }
00560   bmProps->SetPropertyAsBool(NS_LITERAL_STRING("toolbar"), isToolbarBM);
00561 
00562   return nsMetricsUtils::AddChildItem(parentItem,
00563                                       NS_LITERAL_STRING("bookmark"), bmProps);
00564 #endif  // MOZ_PLACES
00565 }