blob: e0f577f427477fac7ed44642cd31867c2b145601 [file] [log] [blame]
/*
* 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