1 /* 2 * Copyright 2006, The Android Open Source Project 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #define LOG_TAG "favicons" 27 28 #include "config.h" 29 #include "WebIconDatabase.h" 30 31 #include "FileSystem.h" 32 #include "GraphicsJNI.h" 33 #include "IconDatabase.h" 34 #include "Image.h" 35 #include "IntRect.h" 36 #include "JavaSharedClient.h" 37 #include "KURL.h" 38 #include "WebCoreJni.h" 39 40 #include <JNIHelp.h> 41 #include <JNIUtility.h> 42 #include <SharedBuffer.h> 43 #include <SkBitmap.h> 44 #include <SkImageDecoder.h> 45 #include <SkTemplates.h> 46 #include <pthread.h> 47 #include <utils/misc.h> 48 #include <wtf/Platform.h> 49 #include <wtf/text/CString.h> 50 51 namespace android { 52 53 SkBitmap* webcoreImageToSkBitmap(WebCore::Image* icon) 54 { 55 if (!icon) 56 return 0; 57 WebCore::SharedBuffer* buffer = icon->data(); 58 if (!buffer) 59 return 0; 60 SkBitmap* bm = new SkBitmap; 61 if (!SkImageDecoder::DecodeMemory(buffer->data(), buffer->size(), bm, 62 SkBitmap::kNo_Config, 63 SkImageDecoder::kDecodePixels_Mode) 64 || bm->isNull() || !bm->width() || !bm->height() 65 || bm->config() == SkBitmap::kNo_Config) { 66 delete bm; 67 return 0; 68 } 69 return bm; 70 } 71 72 jobject webcoreImageToJavaBitmap(JNIEnv* env, WebCore::Image* icon) 73 { 74 SkBitmap* bm = webcoreImageToSkBitmap(icon); 75 if (!bm) 76 return 0; 77 return GraphicsJNI::createBitmap(env, bm, false, NULL); 78 } 79 80 static WebIconDatabase* gIconDatabaseClient = new WebIconDatabase(); 81 82 bool WebIconDatabase::performImport() 83 { 84 // We don't do do any old-style database importing. 85 return true; 86 } 87 88 // Called on the WebCore thread 89 void WebIconDatabase::didImportIconURLForPageURL(const WTF::String& pageURL) 90 { 91 // FIXME: After http://trac.webkit.org/changeset/81719 this method is called 92 // on the WebCore thread, so switching threads via this queue is superfluous 93 // and should be removed. http://b/4565022 94 mNotificationsMutex.lock(); 95 mNotifications.append(pageURL); 96 if (!mDeliveryRequested) { 97 mDeliveryRequested = true; 98 JavaSharedClient::EnqueueFunctionPtr(DeliverNotifications, this); 99 } 100 mNotificationsMutex.unlock(); 101 } 102 103 void WebIconDatabase::didImportIconDataForPageURL(const WTF::String& pageURL) 104 { 105 // WebKit1 only has a single "icon did change" notification. 106 didImportIconURLForPageURL(pageURL); 107 } 108 109 void WebIconDatabase::didChangeIconForPageURL(const WTF::String& pageURL) 110 { 111 // WebKit1 only has a single "icon did change" notification. 112 didImportIconURLForPageURL(pageURL); 113 } 114 115 void WebIconDatabase::didRemoveAllIcons() 116 { 117 } 118 119 void WebIconDatabase::didFinishURLImport() 120 { 121 } 122 123 // Called in the WebCore thread 124 void WebIconDatabase::RegisterForIconNotification(WebIconDatabaseClient* client) 125 { 126 WebIconDatabase* db = gIconDatabaseClient; 127 for (unsigned i = 0; i < db->mClients.size(); ++i) { 128 // Do not add the same client twice. 129 if (db->mClients[i] == client) 130 return; 131 } 132 gIconDatabaseClient->mClients.append(client); 133 } 134 135 // Called in the WebCore thread 136 void WebIconDatabase::UnregisterForIconNotification(WebIconDatabaseClient* client) 137 { 138 WebIconDatabase* db = gIconDatabaseClient; 139 for (unsigned i = 0; i < db->mClients.size(); ++i) { 140 if (db->mClients[i] == client) { 141 db->mClients.remove(i); 142 break; 143 } 144 } 145 } 146 147 // Called in the WebCore thread 148 void WebIconDatabase::DeliverNotifications(void* v) 149 { 150 ASSERT(v); 151 ((WebIconDatabase*)v)->deliverNotifications(); 152 } 153 154 // Called in the WebCore thread 155 void WebIconDatabase::deliverNotifications() 156 { 157 ASSERT(mDeliveryRequested); 158 159 // Swap the notifications queue 160 Vector<WTF::String> queue; 161 mNotificationsMutex.lock(); 162 queue.swap(mNotifications); 163 mDeliveryRequested = false; 164 mNotificationsMutex.unlock(); 165 166 // Swap the clients queue 167 Vector<WebIconDatabaseClient*> clients; 168 clients.swap(mClients); 169 170 for (unsigned i = 0; i < queue.size(); ++i) { 171 for (unsigned j = 0; j < clients.size(); ++j) { 172 clients[j]->didAddIconForPageUrl(queue[i]); 173 } 174 } 175 } 176 177 static void Open(JNIEnv* env, jobject obj, jstring path) 178 { 179 WebCore::IconDatabaseBase& iconDb = WebCore::iconDatabase(); 180 if (iconDb.isOpen()) 181 return; 182 iconDb.setEnabled(true); 183 iconDb.setClient(gIconDatabaseClient); 184 ALOG_ASSERT(path, "No path given to nativeOpen"); 185 WTF::String pathStr = jstringToWtfString(env, path); 186 WTF::CString fullPath = WebCore::pathByAppendingComponent(pathStr, 187 WebCore::IconDatabase::defaultDatabaseFilename()).utf8(); 188 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; 189 bool didSetPermissions = false; 190 if (access(fullPath.data(), F_OK) == 0) { 191 if (chmod(fullPath.data(), mode) == 0) 192 didSetPermissions = true; 193 } else { 194 int fd = open(fullPath.data(), O_CREAT, mode); 195 if (fd >= 0) { 196 close(fd); 197 didSetPermissions = true; 198 } 199 } 200 if (didSetPermissions) { 201 ALOGV("Opening WebIconDatabase file '%s'", pathStr.latin1().data()); 202 bool res = iconDb.open(pathStr, WebCore::IconDatabase::defaultDatabaseFilename()); 203 if (!res) 204 ALOGE("Open failed!"); 205 } else 206 ALOGE("Failed to set permissions on '%s'", fullPath.data()); 207 } 208 209 static void Close(JNIEnv* env, jobject obj) 210 { 211 WebCore::iconDatabase().close(); 212 } 213 214 static void RemoveAllIcons(JNIEnv* env, jobject obj) 215 { 216 ALOGV("Removing all icons"); 217 WebCore::iconDatabase().removeAllIcons(); 218 } 219 220 static jobject IconForPageUrl(JNIEnv* env, jobject obj, jstring url) 221 { 222 ALOG_ASSERT(url, "No url given to iconForPageUrl"); 223 WTF::String urlStr = jstringToWtfString(env, url); 224 225 // FIXME: This method should not be used from outside WebCore and will be removed. 226 // http://trac.webkit.org/changeset/81484 227 WebCore::Image* icon = WebCore::iconDatabase().synchronousIconForPageURL(urlStr, WebCore::IntSize(16, 16)); 228 ALOGV("Retrieving icon for '%s' %p", urlStr.latin1().data(), icon); 229 return webcoreImageToJavaBitmap(env, icon); 230 } 231 232 static void RetainIconForPageUrl(JNIEnv* env, jobject obj, jstring url) 233 { 234 ALOG_ASSERT(url, "No url given to retainIconForPageUrl"); 235 WTF::String urlStr = jstringToWtfString(env, url); 236 237 ALOGV("Retaining icon for '%s'", urlStr.latin1().data()); 238 WebCore::iconDatabase().retainIconForPageURL(urlStr); 239 } 240 241 static void ReleaseIconForPageUrl(JNIEnv* env, jobject obj, jstring url) 242 { 243 ALOG_ASSERT(url, "No url given to releaseIconForPageUrl"); 244 WTF::String urlStr = jstringToWtfString(env, url); 245 246 ALOGV("Releasing icon for '%s'", urlStr.latin1().data()); 247 WebCore::iconDatabase().releaseIconForPageURL(urlStr); 248 } 249 250 /* 251 * JNI registration 252 */ 253 static JNINativeMethod gWebIconDatabaseMethods[] = { 254 { "nativeOpen", "(Ljava/lang/String;)V", 255 (void*) Open }, 256 { "nativeClose", "()V", 257 (void*) Close }, 258 { "nativeRemoveAllIcons", "()V", 259 (void*) RemoveAllIcons }, 260 { "nativeIconForPageUrl", "(Ljava/lang/String;)Landroid/graphics/Bitmap;", 261 (void*) IconForPageUrl }, 262 { "nativeRetainIconForPageUrl", "(Ljava/lang/String;)V", 263 (void*) RetainIconForPageUrl }, 264 { "nativeReleaseIconForPageUrl", "(Ljava/lang/String;)V", 265 (void*) ReleaseIconForPageUrl } 266 }; 267 268 int registerWebIconDatabase(JNIEnv* env) 269 { 270 #ifndef NDEBUG 271 jclass webIconDatabase = env->FindClass("android/webkit/WebIconDatabaseClassic"); 272 ALOG_ASSERT(webIconDatabase, "Unable to find class android.webkit.WebIconDatabaseClassic"); 273 env->DeleteLocalRef(webIconDatabase); 274 #endif 275 276 return jniRegisterNativeMethods(env, "android/webkit/WebIconDatabaseClassic", 277 gWebIconDatabaseMethods, NELEM(gWebIconDatabaseMethods)); 278 } 279 280 } 281