sbFileSystemTreeState.cpp
Go to the documentation of this file.
1 /*
2 //
3 // BEGIN SONGBIRD GPL
4 //
5 // This file is part of the Songbird web player.
6 //
7 // Copyright(c) 2005-2009 POTI, Inc.
8 // http://songbirdnest.com
9 //
10 // This file may be licensed under the terms of of the
11 // GNU General Public License Version 2 (the "GPL").
12 //
13 // Software distributed under the License is distributed
14 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
15 // express or implied. See the GPL for the specific language
16 // governing rights and limitations.
17 //
18 // You should have received a copy of the GPL along with this
19 // program. If not, go to http://www.gnu.org/licenses/gpl.html
20 // or write to the Free Software Foundation, Inc.,
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 // END SONGBIRD GPL
24 //
25 */
26 
27 #include "sbFileSystemTreeState.h"
28 #include "sbFileSystemTree.h"
29 #include <nsCOMPtr.h>
30 #include <nsAutoPtr.h>
31 #include <nsComponentManagerUtils.h>
32 #include <nsServiceManagerUtils.h>
33 #include <nsIPipe.h>
34 #include <nsIAsyncOutputStream.h>
35 #include <nsIAsyncInputStream.h>
36 #include <nsILocalFile.h>
37 #include <nsIFileStreams.h>
38 #include <nsIProperties.h>
39 #include <nsAppDirectoryServiceDefs.h>
40 #include <nsMemory.h>
41 #include <stack>
42 
43 #define TREE_FOLDER_NAME "fstrees"
44 #define SESSION_FILENAME_EXTENSION ".tree"
45 #define TREE_SCHEMA_VERSION 1
46 
47 
48 //
49 // To serialize the tree structure, the parent/child dependency is broken
50 // down and recorded with ID references to their genealogy. When the tree
51 // is de-serialized, the relationship is set back up using the ID of the
52 // parent and child nodes.
53 //
54 // See below for a example of the sequence of data:
55 //
56 // FILENAME: '{sessionid}.tree'
57 // -----------------------------------------------------------
58 // | 1.) Serialization schema version (PRUint32) |
59 // -----------------------------------------------------------
60 // | 2.) Tree root absolute path (nsString) |
61 // -----------------------------------------------------------
62 // | 3.) Is tree recursive watch (PRBool) |
63 // -----------------------------------------------------------
64 // | 4.) Number of nodes (PRUint32) |
65 // -----------------------------------------------------------
66 // | 5.) Node (sbFileSystemNode) |
67 // -----------------------------------------------------------
68 // | .. Node (sbFileSystemNode) |
69 // -----------------------------------------------------------
70 // | ... |
71 // -----------------------------------------------------------
72 // | -> EOF |
73 // -----------------------------------------------------------
74 //
75 
77 
79 {
80 }
81 
83 {
84 }
85 
86 nsresult
88  const nsID & aSessionID)
89 {
90  NS_ENSURE_ARG_POINTER(aTree);
91 
92  // Setup and write the serialized data as defined above.
93  nsresult rv;
94  nsCOMPtr<nsIFile> savedSessionFile;
95  rv = GetTreeSessionFile(aSessionID,
96  PR_TRUE, // do create
97  getter_AddRefs(savedSessionFile));
98  NS_ENSURE_SUCCESS(rv, rv);
99 
100  nsRefPtr<sbFileObjectOutputStream> fileObjectStream =
102  NS_ENSURE_TRUE(fileObjectStream, NS_ERROR_OUT_OF_MEMORY);
103 
104  rv = fileObjectStream->InitWithFile(savedSessionFile);
105  NS_ENSURE_SUCCESS(rv, rv);
106 
107  // Now begin to write out the data in the sequence described above:
108  // 1.) The tree schema version.
109  rv = fileObjectStream->WriteUint32(TREE_SCHEMA_VERSION);
110  NS_ENSURE_SUCCESS(rv, rv);
111 
112  // 2.) Tree root absolute path
113  rv = fileObjectStream->WriteString(aTree->mRootPath);
114  NS_ENSURE_SUCCESS(rv, rv);
115 
116  // 3.) Is tree recursive watch.
117  rv = fileObjectStream->WritePRBool(aTree->mIsRecursiveBuild);
118  NS_ENSURE_SUCCESS(rv, rv);
119 
120  // 4.) Number of nodes
121  PRUint32 nodeCount = 0;
122  rv = GetTreeNodeCount(aTree->mRootNode, &nodeCount);
123  NS_ENSURE_SUCCESS(rv, rv);
124 
125  rv = fileObjectStream->WriteUint32(nodeCount);
126  NS_ENSURE_SUCCESS(rv, rv);
127 
128  // 5.) Node data
129  std::stack<nsRefPtr<sbFileSystemNode> > nodeStack;
130  nodeStack.push(aTree->mRootNode);
131 
132  // Used to create a unique identifier for a parent node.
133  // NOTE: The root node will always be 0.
134  PRUint32 curNodeID = 0;
135 
136  while (!nodeStack.empty()) {
137  nsRefPtr<sbFileSystemNode> curNode = nodeStack.top();
138  nodeStack.pop();
139 
140  if (!curNode) {
141  NS_WARNING("Coult not get the node from the node stack!");
142  continue;
143  }
144 
145  // Set the id of the current node.
146  rv = curNode->SetNodeID(curNodeID);
147  if (NS_FAILED(rv)) {
148  NS_WARNING("Could not set the node ID!");
149  continue;
150  }
151 
152  rv = WriteNode(fileObjectStream, curNode);
153  if (NS_FAILED(rv)) {
154  NS_WARNING("Could not write curNode to disk!");
155  continue;
156  }
157 
158  sbNodeMap *curNodeChildren = curNode->GetChildren();
159  if (curNodeChildren && curNodeChildren->size() > 0) {
160  // Iterate through the entire child node map to set the parent ID
161  // and push each node into the node stack.
162  sbNodeMapIter begin = curNodeChildren->begin();
163  sbNodeMapIter end = curNodeChildren->end();
165  for (next = begin; next != end; ++next) {
166  nsRefPtr<sbFileSystemNode> curChildNode(next->second);
167  if (!curChildNode) {
168  NS_WARNING("Could not get get curChildNode!");
169  continue;
170  }
171 
172  rv = curChildNode->SetParentID(curNodeID);
173  if (NS_FAILED(rv)) {
174  NS_WARNING("Could not set the parent GUID!");
175  continue;
176  }
177 
178  nodeStack.push(curChildNode);
179  }
180  }
181 
182  // Bump the node ID count.
183  ++curNodeID;
184  }
185 
186  rv = fileObjectStream->Close();
187  NS_ENSURE_SUCCESS(rv, rv);
188 
189  return NS_OK;
190 }
191 
192 nsresult
194  nsString & aSessionAbsolutePath,
195  PRBool *aIsRecursiveWatch,
196  sbFileSystemNode **aOutRootNode)
197 {
198  NS_ENSURE_ARG_POINTER(aOutRootNode);
199 
200  // Setup and read the serialized data as defined above.
201  nsresult rv;
202  nsCOMPtr<nsIFile> savedSessionFile;
203  rv = GetTreeSessionFile(aSessionID,
204  PR_FALSE, // don't create
205  getter_AddRefs(savedSessionFile));
206  NS_ENSURE_SUCCESS(rv, rv);
207 
208  // Ensure that the session file exists.
209  PRBool exists = PR_FALSE;
210  if (NS_FAILED(savedSessionFile->Exists(&exists)) || !exists) {
211  NS_WARNING("The saved session file no longer exists!");
212  return NS_ERROR_UNEXPECTED;
213  }
214 
215  nsRefPtr<sbFileObjectInputStream> fileObjectStream =
217  NS_ENSURE_TRUE(fileObjectStream, NS_ERROR_OUT_OF_MEMORY);
218 
219  rv = fileObjectStream->InitWithFile(savedSessionFile);
220  NS_ENSURE_SUCCESS(rv, rv);
221 
222  // Now begin to read in the data in the sequence defined above:
223  // 1.) The tree schema version.
224  PRUint32 schemaVersion = 0;
225  rv = fileObjectStream->ReadUint32(&schemaVersion);
226  NS_ENSURE_SUCCESS(rv, rv);
227 
228  // For now, just ensure that the schema version is the same.
229  // In the future, a migration handler will need to be written.
230  if (schemaVersion != TREE_SCHEMA_VERSION) {
231  return NS_ERROR_FAILURE;
232  }
233 
234  // 2.) Tree root absolute path
235  rv = fileObjectStream->ReadString(aSessionAbsolutePath);
236  NS_ENSURE_SUCCESS(rv, rv);
237 
238  // 3.) Is tree recursive watch.
239  rv = fileObjectStream->ReadPRBool(aIsRecursiveWatch);
240  NS_ENSURE_SUCCESS(rv, rv);
241 
242  // 4.) Number of nodes
243  PRUint32 nodeCount = 0;
244  rv = fileObjectStream->ReadUint32(&nodeCount);
245  NS_ENSURE_SUCCESS(rv, rv);
246 
247  // 5.) Node data
248  // Use the map to store guids to help rebuild the parent/child relationship.
249  nsRefPtr<sbFileSystemNode> savedRootNode;
250  sbNodeIDMap nodeIDMap;
251  for (PRUint32 i = 0; i < nodeCount; i++) {
252  nsRefPtr<sbFileSystemNode> curNode;
253  rv = ReadNode(fileObjectStream, getter_AddRefs(curNode));
254  // If one of the nodes is missing, it could corrupt the entire tree.
255  NS_ENSURE_SUCCESS(rv, rv);
256  NS_ENSURE_TRUE(curNode, NS_ERROR_UNEXPECTED);
257 
258  // Assign this node into the node ID map.
259  PRUint32 curNodeID;
260  rv = curNode->GetNodeID(&curNodeID);
261  // Once again, this will corrupt the entire tree if it fails.
262  NS_ENSURE_SUCCESS(rv, rv);
263 
264  nodeIDMap.insert(sbNodeIDMapPair(curNodeID, curNode));
265 
266  // If this is the first node read, it is the root node. Simply stash the
267  // references for later and continue.
268  if (i == 0) {
269  savedRootNode = curNode;
270  continue;
271  }
272 
273  // Setup the relationship between parent and child.
274  rv = AssignRelationships(curNode, nodeIDMap);
275  // If this fails, it will also corrupt the entire tree.
276  NS_ENSURE_SUCCESS(rv, rv);
277  }
278 
279  rv = fileObjectStream->Close();
280  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Could not close the file object stream!");
281 
282  savedRootNode.forget(aOutRootNode);
283  return NS_OK;
284 }
285 
286 /* static */ nsresult
288 {
289  nsresult rv;
290  nsCOMPtr<nsIFile> sessionFile;
291  rv = GetTreeSessionFile(aSessionID, PR_FALSE, getter_AddRefs(sessionFile));
292  NS_ENSURE_SUCCESS(rv, rv);
293 
294  PRBool fileExists = PR_FALSE;
295  if (NS_SUCCEEDED(sessionFile->Exists(&fileExists)) && fileExists) {
296  rv = sessionFile->Remove(PR_FALSE);
297  NS_ENSURE_SUCCESS(rv, rv);
298  }
299 
300  return NS_OK;
301 }
302 
303 nsresult
305  sbFileSystemNode *aOutNode)
306 {
307  NS_ENSURE_ARG_POINTER(aOutputStream);
308  NS_ENSURE_ARG_POINTER(aOutNode);
309 
310  nsresult rv;
311  nsCOMPtr<nsISupports> writeSupports =
312  do_QueryInterface(NS_ISUPPORTS_CAST(nsISerializable *, aOutNode), &rv);
313  NS_ENSURE_SUCCESS(rv, rv);
314 
315  return aOutputStream->WriteObject(writeSupports, PR_TRUE);
316 }
317 
318 nsresult
320  sbFileSystemNode **aOutNode)
321 {
322  NS_ENSURE_ARG_POINTER(aInputStream);
323  NS_ENSURE_ARG_POINTER(aOutNode);
324 
325  nsresult rv;
326  nsCOMPtr<nsISupports> readSupports;
327  rv = aInputStream->ReadObject(PR_TRUE, getter_AddRefs(readSupports));
328  NS_ENSURE_SUCCESS(rv, rv);
329 
330  // Cast to a nsISerializable to work around ambigious issue with
331  // the sbFileSystemNode class.
332  nsCOMPtr<nsISerializable> serializable =
333  do_QueryInterface(readSupports, &rv);
334  NS_ENSURE_SUCCESS(rv, rv);
335 
336  *aOutNode = static_cast<sbFileSystemNode *>(serializable.get());
337  NS_IF_ADDREF(*aOutNode);
338 
339  return NS_OK;
340 }
341 
342 nsresult
344  sbNodeIDMap & aParentGuidMap)
345 {
346  NS_ENSURE_ARG_POINTER(aChildNode);
347 
348  nsresult rv;
349  PRUint32 parentID;
350  rv = aChildNode->GetParentID(&parentID);
351  NS_ENSURE_SUCCESS(rv, rv);
352 
353  sbNodeIDMapIter found = aParentGuidMap.find(parentID);
354  if (found == aParentGuidMap.end()) {
355  return NS_ERROR_UNEXPECTED;
356  }
357 
358  nsRefPtr<sbFileSystemNode> parentNode(found->second);
359  NS_ENSURE_TRUE(parentNode, NS_ERROR_UNEXPECTED);
360 
361  rv = parentNode->AddChild(aChildNode);
362  NS_ENSURE_SUCCESS(rv, rv);
363 
364  return NS_OK;
365 }
366 
367 /* static */ nsresult
369  PRBool aShouldCreate,
370  nsIFile **aOutFile)
371 {
372  char idChars[NSID_LENGTH];
373  aSessionID.ToProvidedString(idChars);
374  nsString sessionFilename;
375  sessionFilename.Append(NS_ConvertASCIItoUTF16(idChars));
376  sessionFilename.Append(NS_LITERAL_STRING(SESSION_FILENAME_EXTENSION));
377 
378  nsresult rv;
379  nsCOMPtr<nsIProperties> dirService =
380  do_GetService("@mozilla.org/file/directory_service;1", &rv);
381  NS_ENSURE_SUCCESS(rv, rv);
382 
383  nsCOMPtr<nsIFile> profileDir;
384  rv = dirService->Get(NS_APP_PREFS_50_DIR,
385  NS_GET_IID(nsIFile),
386  getter_AddRefs(profileDir));
387  NS_ENSURE_SUCCESS(rv, rv);
388 
389  nsCOMPtr<nsIFile> treeFolder;
390  rv = profileDir->Clone(getter_AddRefs(treeFolder));
391  NS_ENSURE_SUCCESS(rv, rv);
392 
393  rv = treeFolder->Append(NS_LITERAL_STRING(TREE_FOLDER_NAME));
394  NS_ENSURE_SUCCESS(rv, rv);
395 
396  PRBool folderExists = PR_FALSE;
397  if (NS_SUCCEEDED(treeFolder->Exists(&folderExists)) && !folderExists) {
398  rv = treeFolder->Create(nsIFile::DIRECTORY_TYPE, 0755);
399  NS_ENSURE_SUCCESS(rv, rv);
400  }
401 
402  nsCOMPtr<nsIFile> newFile;
403  rv = treeFolder->Clone(getter_AddRefs(newFile));
404  NS_ENSURE_SUCCESS(rv, rv);
405 
406  rv = newFile->Append(sessionFilename);
407  NS_ENSURE_SUCCESS(rv, rv);
408 
409  if (aShouldCreate) {
410  PRBool exists = PR_FALSE;
411  if (NS_SUCCEEDED(newFile->Exists(&exists)) && exists) {
412  rv = newFile->Remove(PR_FALSE);
413  NS_ENSURE_SUCCESS(rv, rv);
414  }
415 
416  rv = newFile->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
417  NS_ENSURE_SUCCESS(rv, rv);
418  }
419 
420  newFile.swap(*aOutFile);
421  return NS_OK;
422 }
423 
424 nsresult
426  PRUint32 *aNodeCount)
427 {
428  NS_ENSURE_ARG_POINTER(aRootNode);
429  NS_ENSURE_ARG_POINTER(aNodeCount);
430 
431  PRUint32 nodeCount = 0;
432 
433  std::stack<nsRefPtr<sbFileSystemNode> > nodeStack;
434  nodeStack.push(aRootNode);
435 
436  while (!nodeStack.empty()) {
437  nsRefPtr<sbFileSystemNode> curNode = nodeStack.top();
438  nodeStack.pop();
439 
440  // Increment the node count.
441  nodeCount++;
442 
443  sbNodeMap *childMap = curNode->GetChildren();
444  if (!childMap || childMap->size() == 0) {
445  continue;
446  }
447 
448  // Push all children into the stack to count.
449  sbNodeMapIter begin = childMap->begin();
450  sbNodeMapIter end = childMap->end();
452  for (next = begin; next != end; ++next) {
453  nodeStack.push(next->second);
454  }
455  }
456 
457  *aNodeCount = nodeCount;
458  return NS_OK;
459 }
460 
nsresult LoadTreeState(nsID &aSessionID, nsString &aSessionAbsolutePath, PRBool *aIsRecursiveWatch, sbFileSystemNode **aOutRootNode)
NS_IMPL_THREADSAFE_ISUPPORTS0(sbMetadataCrashTracker)
#define TREE_SCHEMA_VERSION
return NS_OK
#define SESSION_FILENAME_EXTENSION
nsresult GetTreeNodeCount(sbFileSystemNode *aRootNode, PRUint32 *aNodeCount)
static nsresult DeleteSavedTreeState(const nsID &aSessionID)
nsresult WriteObject(nsISupports *aSupports, PRBool aIsStrongRef)
nsresult AssignRelationships(sbFileSystemNode *aChildNode, sbNodeIDMap &aParentGuidMap)
NS_DECL_ISUPPORTS nsresult SaveTreeState(sbFileSystemTree *aFileSystemTree, const nsID &aSessionID)
std::map< nsString, nsRefPtr< sbFileSystemNode > > sbNodeMap
nsresult GetParentID(PRUint32 *aOutID)
sbNodeMap::const_iterator sbNodeMapIter
nsISerializable
static nsresult GetTreeSessionFile(const nsID &aSessionID, PRBool aShouldCreate, nsIFile **aOutFile)
nsresult ReadObject(PRBool aIsStrongRef, nsISupports **aOutObject)
sbNodeIDMap::const_iterator sbNodeIDMapIter
std::map< PRUint32, nsRefPtr< sbFileSystemNode > > sbNodeIDMap
nsresult WriteNode(sbFileObjectOutputStream *aOutputStream, sbFileSystemNode *aOutNode)
sbNodeIDMap::value_type sbNodeIDMapPair
#define TREE_FOLDER_NAME
var profileDir
_getSelectedPageStyle s i
nsresult ReadNode(sbFileObjectInputStream *aInputStream, sbFileSystemNode **aOutNode)
function next()