| /* |
| * Copyright 2009, Google Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| // This file implements the asynchronous file-loading glue. |
| |
| #include <algorithm> |
| |
| #include "plugin/cross/async_loading.h" |
| #include "plugin/cross/o3d_glue.h" |
| #include "plugin/cross/stream_manager.h" |
| #include "core/cross/bitmap.h" |
| #include "core/cross/error_status.h" |
| #include "core/cross/file_request.h" |
| #include "core/cross/pack.h" |
| #include "core/cross/texture.h" |
| #include "import/cross/raw_data.h" |
| |
| namespace glue { |
| namespace namespace_o3d { |
| namespace class_FileRequest { |
| |
| using _o3d::PluginObject; |
| using o3d::Bitmap; |
| using o3d::Pack; |
| using o3d::RawData; |
| using o3d::Texture; |
| |
| // StreamManager::FinishedCallback |
| // implementation that imports the file as a texture once downloaded. |
| // When the download completes, LoadTextureURLCallback::Run() will be called, |
| // which will parse and load the downloaded file. After that load is complete, |
| // onreadystatechange will be run to notify the user. |
| class LoadTextureURLCallback : public StreamManager::FinishedCallback { |
| public: |
| // Creates a new LoadTextureURLCallback. |
| static LoadTextureURLCallback *Create(FileRequest *request) { |
| return new LoadTextureURLCallback(request); |
| } |
| |
| virtual ~LoadTextureURLCallback() { |
| // If the file request was interrupted (for example we moved to a new page |
| // before the file transfer was completed) then we tell the FileRequest |
| // object that the request failed. It's important to call this here since |
| // set_success() will release the pack reference that the FileRequest holds |
| // which will allow the pack to be garbage collected. |
| if (!request_->done()) { |
| request_->set_success(false); |
| } |
| } |
| |
| // Loads the texture file, calls the JS callback to pass back the texture |
| // object. |
| virtual void Run(DownloadStream*, |
| bool success, |
| const std::string &filename, |
| const std::string &mime_type) { |
| Texture::Ref texture; |
| if (success) { |
| o3d::ErrorCollector error_collector(request_->service_locator()); |
| request_->set_ready_state(FileRequest::STATE_LOADED); |
| // Try to get the image file type from the returned MIME type. |
| // Unfortunately, TGA and DDS don't have standard MIME type, so we may |
| // have to rely on the filename, or let the image loader figure it out by |
| // itself (by trying every possible type). |
| o3d::image::ImageFileType image_type = |
| o3d::image::GetFileTypeFromMimeType(mime_type.c_str()); |
| texture = Texture::Ref( |
| request_->pack()->CreateTextureFromFile( |
| request_->uri(), |
| filename.c_str(), |
| image_type, |
| request_->generate_mipmaps())); |
| if (texture) { |
| texture->set_name(request_->uri()); |
| request_->set_texture(texture); |
| } else { |
| success = false; |
| } |
| request_->set_error(error_collector.errors()); |
| } else { |
| // No error is passed in from the stream but we MUST have an error |
| // for the request to work on the javascript side. |
| request_->set_error("Could not download texture: " + request_->uri()); |
| } |
| request_->set_success(success); |
| // Since the standard codes only go far enough to tell us that the download |
| // succeeded, we set the success [and implicitly the done] flags to give the |
| // rest of the story. |
| if (request_->onreadystatechange()) |
| request_->onreadystatechange()->Run(); |
| } |
| |
| private: |
| FileRequest::Ref request_; |
| |
| explicit LoadTextureURLCallback(FileRequest *request) |
| : request_(request) { |
| } |
| }; |
| |
| // StreamManager::FinishedCallback |
| // implementation that imports the file as a RawData once downloaded. When the |
| // download completes, LoadRawDataURLCallback::Run() will be called, which will |
| // parse and load the downloaded file. After that load is complete, |
| // onreadystatechange will be run to notify the user. |
| class LoadRawDataURLCallback : public StreamManager::FinishedCallback { |
| public: |
| // Creates a new LoadRawDataURLCallback. |
| static LoadRawDataURLCallback *Create(FileRequest *request) { |
| return new LoadRawDataURLCallback(request); |
| } |
| |
| virtual ~LoadRawDataURLCallback() { |
| // If the file request was interrupted (for example we moved to a new page |
| // before the file transfer was completed) then we tell the FileRequest |
| // object that the request failed. It's important to call this here since |
| // set_success() will release the pack reference that the FileRequest holds |
| // which will allow the pack to be garbage collected. |
| if (!request_->done()) { |
| request_->set_success(false); |
| } |
| } |
| |
| // Loads the RawData file, calls the JS callback to pass back the RawData |
| // object. |
| virtual void Run(DownloadStream*, |
| bool success, |
| const std::string &filename, |
| const std::string &mime_type) { |
| RawData::Ref data; |
| if (success) { |
| o3d::ErrorCollector error_collector(request_->service_locator()); |
| request_->set_ready_state(FileRequest::STATE_LOADED); |
| data = RawData::Ref(RawData::CreateFromFile(request_->service_locator(), |
| request_->uri(), |
| filename)); |
| if (data) { |
| request_->set_data(data); |
| } else { |
| success = false; |
| } |
| request_->set_error(error_collector.errors()); |
| } else { |
| // No error is passed in from the stream but we MUST have an error |
| // for the request to work on the javascript side. |
| request_->set_error("Could not download: " + request_->uri()); |
| } |
| request_->set_success(success); |
| // Since the standard codes only go far enough to tell us that the download |
| // succeeded, we set the success [and implicitly the done] flags to give the |
| // rest of the story. |
| if (request_->onreadystatechange()) |
| request_->onreadystatechange()->Run(); |
| } |
| |
| private: |
| FileRequest::Ref request_; |
| |
| explicit LoadRawDataURLCallback(FileRequest *request) |
| : request_(request) { |
| } |
| }; |
| |
| // Sets up the parameters required for all FileRequests. |
| void userglue_method_open(void *plugin_data, |
| FileRequest *request, |
| const String &method, |
| const String &uri, |
| bool async) { |
| if (!async) { |
| request->set_success(false); |
| O3D_ERROR(request->service_locator()) |
| << ("synchronous request not supported"); |
| return; // We don't yet support synchronous requests. |
| } |
| if (request->done()) { |
| request->set_success(false); |
| request->set_ready_state(FileRequest::STATE_INIT); // Show we're unready. |
| O3D_ERROR(request->service_locator()) |
| << "request can not be reused"; |
| return; // We don't yet support reusing FileRequests. |
| } |
| |
| String method_lower(method); |
| std::transform(method.begin(), method.end(), method_lower.begin(), ::tolower); |
| if (method_lower != "get") { |
| request->set_success(false); |
| O3D_ERROR(request->service_locator()) |
| << "request does not support POST yet"; |
| return; // We don't yet support fetching files via POST. |
| } |
| request->set_uri(uri); |
| request->set_ready_state(FileRequest::STATE_OPEN); |
| } |
| |
| // Starts downloading or reading the requested file, passing in a callback that |
| // will parse and incorporate the file upon success. |
| void userglue_method_send(void *plugin_data, |
| FileRequest *request) { |
| PluginObject *plugin_object = static_cast<PluginObject *>(plugin_data); |
| StreamManager *stream_manager = plugin_object->stream_manager(); |
| StreamManager::FinishedCallback *callback = NULL; |
| |
| if (request->done()) { |
| request->set_success(false); |
| O3D_ERROR(request->service_locator()) |
| << "request can not be reused"; |
| return; // FileRequests can't be reused. |
| } |
| if (request->ready_state() != 1) { // Forgot to call open, or other error. |
| request->set_success(false); |
| O3D_ERROR(request->service_locator()) |
| << "open must be called before send"; |
| return; |
| } |
| CHECK(request->pack()); |
| |
| switch (request->type()) { |
| case FileRequest::TYPE_TEXTURE: |
| callback = LoadTextureURLCallback::Create(request); |
| break; |
| case FileRequest::TYPE_RAWDATA: |
| callback = LoadRawDataURLCallback::Create(request); |
| break; |
| default: |
| CHECK(false); |
| } |
| if (callback) { |
| DownloadStream *stream = |
| stream_manager->LoadURL(request->uri(), |
| NULL, // new stream callback |
| NULL, // write ready callback |
| NULL, // write callback |
| callback, // finished callback |
| NP_ASFILEONLY); |
| |
| if (!stream) { |
| request->set_success(false); |
| |
| // We don't call O3D_ERROR here because the URI may be user set |
| // so we don't want to cause an error callback when the devloper |
| // may not be able to know the URI is correct. |
| request->set_error("could not create download stream"); |
| |
| // We need to call the callback to report failure. Because it's async, the |
| // code making the request can't know that once it has called send() that |
| // the request still exists since send() may have called the callback and |
| // the callback may have deleted the request. |
| request->onreadystatechange()->Run(); |
| } |
| |
| // If stream is not NULL request may not exist as LoadURL may already have |
| // completed and therefore called the callback which may have freed the |
| // request so we can't set anything on the request here. |
| } |
| } |
| |
| } // namespace class_FileRequest |
| } // namespace namespace_o3d |
| } // namespace glue |