1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2013 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "BlobRegistryImpl.h"
34
35#if ENABLE(BLOB)
36
37#include "BlobResourceHandle.h"
38#include "BlobStorageData.h"
39#include "ResourceError.h"
40#include "ResourceHandle.h"
41#include "ResourceRequest.h"
42#include "ResourceResponse.h"
43#include <wtf/MainThread.h>
44#include <wtf/StdLibExtras.h>
45
46namespace WebCore {
47
48BlobRegistryImpl::~BlobRegistryImpl()
49{
50}
51
52static PassRefPtr<ResourceHandle> createResourceHandle(const ResourceRequest& request, ResourceHandleClient* client)
53{
54    return static_cast<BlobRegistryImpl&>(blobRegistry()).createResourceHandle(request, client);
55}
56
57static void loadResourceSynchronously(NetworkingContext*, const ResourceRequest& request, StoredCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
58{
59    BlobStorageData* blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(request.url());
60    BlobResourceHandle::loadResourceSynchronously(blobData, request, error, response, data);
61}
62
63static void registerBlobResourceHandleConstructor()
64{
65    static bool didRegister = false;
66    if (!didRegister) {
67        ResourceHandle::registerBuiltinConstructor("blob", createResourceHandle);
68        ResourceHandle::registerBuiltinSynchronousLoader("blob", loadResourceSynchronously);
69        didRegister = true;
70    }
71}
72
73PassRefPtr<ResourceHandle> BlobRegistryImpl::createResourceHandle(const ResourceRequest& request, ResourceHandleClient* client)
74{
75    RefPtr<BlobResourceHandle> handle = BlobResourceHandle::createAsync(getBlobDataFromURL(request.url()), request, client);
76    if (!handle)
77        return 0;
78
79    handle->start();
80    return handle.release();
81}
82
83void BlobRegistryImpl::appendStorageItems(BlobStorageData* blobStorageData, const BlobDataItemList& items)
84{
85    for (BlobDataItemList::const_iterator iter = items.begin(); iter != items.end(); ++iter) {
86        if (iter->type == BlobDataItem::Data)
87            blobStorageData->m_data.appendData(iter->data, iter->offset, iter->length);
88#if ENABLE(FILE_SYSTEM)
89        else if (iter->type == BlobDataItem::URL)
90            blobStorageData->m_data.appendURL(iter->url, iter->offset, iter->length, iter->expectedModificationTime);
91#endif
92        else {
93            ASSERT(iter->type == BlobDataItem::File);
94            blobStorageData->m_data.appendFile(iter->path, iter->offset, iter->length, iter->expectedModificationTime);
95        }
96    }
97}
98
99void BlobRegistryImpl::appendStorageItems(BlobStorageData* blobStorageData, const BlobDataItemList& items, long long offset, long long length)
100{
101    ASSERT(length != BlobDataItem::toEndOfFile);
102
103    BlobDataItemList::const_iterator iter = items.begin();
104    if (offset) {
105        for (; iter != items.end(); ++iter) {
106            if (offset >= iter->length)
107                offset -= iter->length;
108            else
109                break;
110        }
111    }
112
113    for (; iter != items.end() && length > 0; ++iter) {
114        long long currentLength = iter->length - offset;
115        long long newLength = currentLength > length ? length : currentLength;
116        if (iter->type == BlobDataItem::Data)
117            blobStorageData->m_data.appendData(iter->data, iter->offset + offset, newLength);
118#if ENABLE(FILE_SYSTEM)
119        else if (iter->type == BlobDataItem::URL)
120            blobStorageData->m_data.appendURL(iter->url, iter->offset + offset, newLength, iter->expectedModificationTime);
121#endif
122        else {
123            ASSERT(iter->type == BlobDataItem::File);
124            blobStorageData->m_data.appendFile(iter->path, iter->offset + offset, newLength, iter->expectedModificationTime);
125        }
126        length -= newLength;
127        offset = 0;
128    }
129}
130
131void BlobRegistryImpl::registerBlobURL(const KURL& url, PassOwnPtr<BlobData> blobData)
132{
133    ASSERT(isMainThread());
134    registerBlobResourceHandleConstructor();
135
136    RefPtr<BlobStorageData> blobStorageData = BlobStorageData::create(blobData->contentType(), blobData->contentDisposition());
137
138    // The blob data is stored in the "canonical" way. That is, it only contains a list of Data and File items.
139    // 1) The Data item is denoted by the raw data and the range.
140    // 2) The File item is denoted by the file path, the range and the expected modification time.
141    // 3) The URL item is denoted by the URL, the range and the expected modification time.
142    // All the Blob items in the passing blob data are resolved and expanded into a set of Data and File items.
143
144    for (BlobDataItemList::const_iterator iter = blobData->items().begin(); iter != blobData->items().end(); ++iter) {
145        switch (iter->type) {
146        case BlobDataItem::Data:
147            blobStorageData->m_data.appendData(iter->data, 0, iter->data->length());
148            break;
149        case BlobDataItem::File:
150            blobStorageData->m_data.appendFile(iter->path, iter->offset, iter->length, iter->expectedModificationTime);
151            break;
152#if ENABLE(FILE_SYSTEM)
153        case BlobDataItem::URL:
154            blobStorageData->m_data.appendURL(iter->url, iter->offset, iter->length, iter->expectedModificationTime);
155            break;
156#endif
157        case BlobDataItem::Blob:
158            if (m_blobs.contains(iter->url.string()))
159                appendStorageItems(blobStorageData.get(), m_blobs.get(iter->url.string())->items(), iter->offset, iter->length);
160            break;
161        }
162    }
163
164    m_blobs.set(url.string(), blobStorageData);
165}
166
167void BlobRegistryImpl::registerBlobURL(const KURL& url, const KURL& srcURL)
168{
169    ASSERT(isMainThread());
170    registerBlobResourceHandleConstructor();
171
172    RefPtr<BlobStorageData> src = m_blobs.get(srcURL.string());
173    ASSERT(src);
174    if (!src)
175        return;
176
177    m_blobs.set(url.string(), src);
178}
179
180void BlobRegistryImpl::unregisterBlobURL(const KURL& url)
181{
182    ASSERT(isMainThread());
183    m_blobs.remove(url.string());
184}
185
186BlobStorageData* BlobRegistryImpl::getBlobDataFromURL(const KURL& url) const
187{
188    ASSERT(isMainThread());
189    return m_blobs.get(url.string());
190}
191
192} // namespace WebCore
193
194#endif
195