Bug 1510695 - Fix URL comparisons in reftestWait r=jgraham
[gecko.git] / storage / FileSystemModule.cpp
blobfac98806ff469563059523bebfa64ba71a99d1d2
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "FileSystemModule.h"
9 #include "sqlite3.h"
10 #include "nsString.h"
11 #include "nsIDirectoryEnumerator.h"
12 #include "nsIFile.h"
14 namespace {
16 struct VirtualTableCursorBase {
17 VirtualTableCursorBase() { memset(&mBase, 0, sizeof(mBase)); }
19 sqlite3_vtab_cursor mBase;
22 struct VirtualTableCursor : public VirtualTableCursorBase {
23 public:
24 VirtualTableCursor() : mRowId(-1) { mCurrentFileName.SetIsVoid(true); }
26 const nsString& DirectoryPath() const { return mDirectoryPath; }
28 const nsString& CurrentFileName() const { return mCurrentFileName; }
30 int64_t RowId() const { return mRowId; }
32 nsresult Init(const nsAString& aPath);
33 nsresult NextFile();
35 private:
36 nsCOMPtr<nsIDirectoryEnumerator> mEntries;
38 nsString mDirectoryPath;
39 nsString mCurrentFileName;
41 int64_t mRowId;
44 nsresult VirtualTableCursor::Init(const nsAString& aPath) {
45 nsCOMPtr<nsIFile> directory = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
46 NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
48 nsresult rv = directory->InitWithPath(aPath);
49 NS_ENSURE_SUCCESS(rv, rv);
51 rv = directory->GetPath(mDirectoryPath);
52 NS_ENSURE_SUCCESS(rv, rv);
54 rv = directory->GetDirectoryEntries(getter_AddRefs(mEntries));
55 NS_ENSURE_SUCCESS(rv, rv);
57 rv = NextFile();
58 NS_ENSURE_SUCCESS(rv, rv);
60 return NS_OK;
63 nsresult VirtualTableCursor::NextFile() {
64 bool hasMore;
65 nsresult rv = mEntries->HasMoreElements(&hasMore);
66 NS_ENSURE_SUCCESS(rv, rv);
68 if (!hasMore) {
69 mCurrentFileName.SetIsVoid(true);
70 return NS_OK;
73 nsCOMPtr<nsISupports> entry;
74 rv = mEntries->GetNext(getter_AddRefs(entry));
75 NS_ENSURE_SUCCESS(rv, rv);
77 nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
78 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
80 rv = file->GetLeafName(mCurrentFileName);
81 NS_ENSURE_SUCCESS(rv, rv);
83 mRowId++;
85 return NS_OK;
88 int Connect(sqlite3* aDB, void* aAux, int aArgc, const char* const* aArgv,
89 sqlite3_vtab** aVtab, char** aErr) {
90 static const char virtualTableSchema[] =
91 "CREATE TABLE fs ("
92 "name TEXT, "
93 "path TEXT"
94 ")";
96 int rc = sqlite3_declare_vtab(aDB, virtualTableSchema);
97 if (rc != SQLITE_OK) {
98 return rc;
101 sqlite3_vtab* vt = new sqlite3_vtab();
102 memset(vt, 0, sizeof(*vt));
104 *aVtab = vt;
106 return SQLITE_OK;
109 int Disconnect(sqlite3_vtab* aVtab) {
110 delete aVtab;
112 return SQLITE_OK;
115 int BestIndex(sqlite3_vtab* aVtab, sqlite3_index_info* aInfo) {
116 // Here we specify what index constraints we want to handle. That is, there
117 // might be some columns with particular constraints in which we can help
118 // SQLite narrow down the result set.
120 // For example, take the "path = x" where x is a directory. In this case,
121 // we can narrow our search to just this directory instead of the entire file
122 // system. This can be a significant optimization. So, we want to handle that
123 // constraint. To do so, we would look for two specific input conditions:
125 // 1. aInfo->aConstraint[i].iColumn == 1
126 // 2. aInfo->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_EQ
128 // The first states that the path column is being used in one of the input
129 // constraints and the second states that the constraint involves the equal
130 // operator.
132 // An even more specific search would be for name='xxx', in which case we
133 // can limit the search to a single file, if it exists.
135 // What we have to do here is look for all of our index searches and select
136 // the narrowest. We can only pick one, so obviously we want the one that
137 // is the most specific, which leads to the smallest result set.
139 for (int i = 0; i < aInfo->nConstraint; i++) {
140 if (aInfo->aConstraint[i].iColumn == 1 && aInfo->aConstraint[i].usable) {
141 if (aInfo->aConstraint[i].op & SQLITE_INDEX_CONSTRAINT_EQ) {
142 aInfo->aConstraintUsage[i].argvIndex = 1;
144 break;
147 // TODO: handle single files (constrained also by the name column)
150 return SQLITE_OK;
153 int Open(sqlite3_vtab* aVtab, sqlite3_vtab_cursor** aCursor) {
154 VirtualTableCursor* cursor = new VirtualTableCursor();
156 *aCursor = reinterpret_cast<sqlite3_vtab_cursor*>(cursor);
158 return SQLITE_OK;
161 int Close(sqlite3_vtab_cursor* aCursor) {
162 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
164 delete cursor;
166 return SQLITE_OK;
169 int Filter(sqlite3_vtab_cursor* aCursor, int aIdxNum, const char* aIdxStr,
170 int aArgc, sqlite3_value** aArgv) {
171 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
173 if (aArgc <= 0) {
174 return SQLITE_OK;
177 nsDependentString path(
178 reinterpret_cast<const char16_t*>(::sqlite3_value_text16(aArgv[0])));
180 nsresult rv = cursor->Init(path);
181 NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
183 return SQLITE_OK;
186 int Next(sqlite3_vtab_cursor* aCursor) {
187 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
189 nsresult rv = cursor->NextFile();
190 NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
192 return SQLITE_OK;
195 int Eof(sqlite3_vtab_cursor* aCursor) {
196 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
197 return cursor->CurrentFileName().IsVoid() ? 1 : 0;
200 int Column(sqlite3_vtab_cursor* aCursor, sqlite3_context* aContext,
201 int aColumnIndex) {
202 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
204 switch (aColumnIndex) {
205 // name
206 case 0: {
207 const nsString& name = cursor->CurrentFileName();
208 sqlite3_result_text16(aContext, name.get(),
209 name.Length() * sizeof(char16_t), SQLITE_TRANSIENT);
210 break;
213 // path
214 case 1: {
215 const nsString& path = cursor->DirectoryPath();
216 sqlite3_result_text16(aContext, path.get(),
217 path.Length() * sizeof(char16_t), SQLITE_TRANSIENT);
218 break;
220 default:
221 MOZ_ASSERT_UNREACHABLE("Unsupported column!");
224 return SQLITE_OK;
227 int RowId(sqlite3_vtab_cursor* aCursor, sqlite3_int64* aRowid) {
228 VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
230 *aRowid = cursor->RowId();
232 return SQLITE_OK;
235 } // namespace
237 namespace mozilla {
238 namespace storage {
240 int RegisterFileSystemModule(sqlite3* aDB, const char* aName) {
241 static sqlite3_module module = {
242 1, Connect, Connect, BestIndex, Disconnect, Disconnect, Open,
243 Close, Filter, Next, Eof, Column, RowId, nullptr,
244 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
246 return sqlite3_create_module(aDB, aName, &module, nullptr);
249 } // namespace storage
250 } // namespace mozilla