1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/components/build/nsAndroidHistory.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,302 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "nsThreadUtils.h" 1.9 +#include "nsAndroidHistory.h" 1.10 +#include "AndroidBridge.h" 1.11 +#include "Link.h" 1.12 +#include "nsIURI.h" 1.13 +#include "mozilla/Services.h" 1.14 +#include "nsIObserverService.h" 1.15 + 1.16 +#define NS_LINK_VISITED_EVENT_TOPIC "link-visited" 1.17 + 1.18 +using namespace mozilla; 1.19 +using mozilla::dom::Link; 1.20 + 1.21 +NS_IMPL_ISUPPORTS(nsAndroidHistory, IHistory, nsIRunnable) 1.22 + 1.23 +nsAndroidHistory* nsAndroidHistory::sHistory = nullptr; 1.24 + 1.25 +/*static*/ 1.26 +nsAndroidHistory* 1.27 +nsAndroidHistory::GetSingleton() 1.28 +{ 1.29 + if (!sHistory) { 1.30 + sHistory = new nsAndroidHistory(); 1.31 + NS_ENSURE_TRUE(sHistory, nullptr); 1.32 + } 1.33 + 1.34 + NS_ADDREF(sHistory); 1.35 + return sHistory; 1.36 +} 1.37 + 1.38 +nsAndroidHistory::nsAndroidHistory() 1.39 +{ 1.40 +} 1.41 + 1.42 +NS_IMETHODIMP 1.43 +nsAndroidHistory::RegisterVisitedCallback(nsIURI *aURI, Link *aContent) 1.44 +{ 1.45 + if (!aContent || !aURI) 1.46 + return NS_OK; 1.47 + 1.48 + // Silently return if URI is something we would never add to DB. 1.49 + bool canAdd; 1.50 + nsresult rv = CanAddURI(aURI, &canAdd); 1.51 + NS_ENSURE_SUCCESS(rv, rv); 1.52 + if (!canAdd) { 1.53 + return NS_OK; 1.54 + } 1.55 + 1.56 + nsAutoCString uri; 1.57 + rv = aURI->GetSpec(uri); 1.58 + if (NS_FAILED(rv)) return rv; 1.59 + NS_ConvertUTF8toUTF16 uriString(uri); 1.60 + 1.61 + nsTArray<Link*>* list = mListeners.Get(uriString); 1.62 + if (! list) { 1.63 + list = new nsTArray<Link*>(); 1.64 + mListeners.Put(uriString, list); 1.65 + } 1.66 + list->AppendElement(aContent); 1.67 + 1.68 + if (AndroidBridge::HasEnv()) { 1.69 + mozilla::widget::android::GeckoAppShell::CheckURIVisited(uriString); 1.70 + } 1.71 + 1.72 + return NS_OK; 1.73 +} 1.74 + 1.75 +NS_IMETHODIMP 1.76 +nsAndroidHistory::UnregisterVisitedCallback(nsIURI *aURI, Link *aContent) 1.77 +{ 1.78 + if (!aContent || !aURI) 1.79 + return NS_OK; 1.80 + 1.81 + nsAutoCString uri; 1.82 + nsresult rv = aURI->GetSpec(uri); 1.83 + if (NS_FAILED(rv)) return rv; 1.84 + NS_ConvertUTF8toUTF16 uriString(uri); 1.85 + 1.86 + nsTArray<Link*>* list = mListeners.Get(uriString); 1.87 + if (! list) 1.88 + return NS_OK; 1.89 + 1.90 + list->RemoveElement(aContent); 1.91 + if (list->IsEmpty()) { 1.92 + mListeners.Remove(uriString); 1.93 + delete list; 1.94 + } 1.95 + return NS_OK; 1.96 +} 1.97 + 1.98 +void 1.99 +nsAndroidHistory::AppendToRecentlyVisitedURIs(nsIURI* aURI) { 1.100 + if (mRecentlyVisitedURIs.Length() < RECENTLY_VISITED_URI_SIZE) { 1.101 + // Append a new element while the array is not full. 1.102 + mRecentlyVisitedURIs.AppendElement(aURI); 1.103 + } else { 1.104 + // Otherwise, replace the oldest member. 1.105 + mRecentlyVisitedURIsNextIndex %= RECENTLY_VISITED_URI_SIZE; 1.106 + mRecentlyVisitedURIs.ElementAt(mRecentlyVisitedURIsNextIndex) = aURI; 1.107 + mRecentlyVisitedURIsNextIndex++; 1.108 + } 1.109 +} 1.110 + 1.111 +inline bool 1.112 +nsAndroidHistory::IsRecentlyVisitedURI(nsIURI* aURI) { 1.113 + bool equals = false; 1.114 + RecentlyVisitedArray::index_type i; 1.115 + RecentlyVisitedArray::size_type length = mRecentlyVisitedURIs.Length(); 1.116 + for (i = 0; i < length && !equals; ++i) { 1.117 + aURI->Equals(mRecentlyVisitedURIs.ElementAt(i), &equals); 1.118 + } 1.119 + return equals; 1.120 +} 1.121 + 1.122 +void 1.123 +nsAndroidHistory::AppendToEmbedURIs(nsIURI* aURI) { 1.124 + if (mEmbedURIs.Length() < EMBED_URI_SIZE) { 1.125 + // Append a new element while the array is not full. 1.126 + mEmbedURIs.AppendElement(aURI); 1.127 + } else { 1.128 + // Otherwise, replace the oldest member. 1.129 + mEmbedURIsNextIndex %= EMBED_URI_SIZE; 1.130 + mEmbedURIs.ElementAt(mEmbedURIsNextIndex) = aURI; 1.131 + mEmbedURIsNextIndex++; 1.132 + } 1.133 +} 1.134 + 1.135 +inline bool 1.136 +nsAndroidHistory::IsEmbedURI(nsIURI* aURI) { 1.137 + bool equals = false; 1.138 + EmbedArray::index_type i; 1.139 + EmbedArray::size_type length = mEmbedURIs.Length(); 1.140 + for (i = 0; i < length && !equals; ++i) { 1.141 + aURI->Equals(mEmbedURIs.ElementAt(i), &equals); 1.142 + } 1.143 + return equals; 1.144 +} 1.145 + 1.146 +NS_IMETHODIMP 1.147 +nsAndroidHistory::VisitURI(nsIURI *aURI, nsIURI *aLastVisitedURI, uint32_t aFlags) 1.148 +{ 1.149 + if (!aURI) 1.150 + return NS_OK; 1.151 + 1.152 + // Silently return if URI is something we shouldn't add to DB. 1.153 + bool canAdd; 1.154 + nsresult rv = CanAddURI(aURI, &canAdd); 1.155 + NS_ENSURE_SUCCESS(rv, rv); 1.156 + if (!canAdd) { 1.157 + return NS_OK; 1.158 + } 1.159 + 1.160 + if (aLastVisitedURI) { 1.161 + bool same; 1.162 + rv = aURI->Equals(aLastVisitedURI, &same); 1.163 + NS_ENSURE_SUCCESS(rv, rv); 1.164 + if (same && IsRecentlyVisitedURI(aURI)) { 1.165 + // Do not save refresh visits if we have visited this URI recently. 1.166 + return NS_OK; 1.167 + } 1.168 + } 1.169 + 1.170 + if (!(aFlags & VisitFlags::TOP_LEVEL)) { 1.171 + AppendToEmbedURIs(aURI); 1.172 + return NS_OK; 1.173 + } 1.174 + 1.175 + if (aFlags & VisitFlags::REDIRECT_SOURCE) 1.176 + return NS_OK; 1.177 + 1.178 + if (aFlags & VisitFlags::UNRECOVERABLE_ERROR) 1.179 + return NS_OK; 1.180 + 1.181 + if (AndroidBridge::HasEnv()) { 1.182 + nsAutoCString uri; 1.183 + rv = aURI->GetSpec(uri); 1.184 + if (NS_FAILED(rv)) return rv; 1.185 + NS_ConvertUTF8toUTF16 uriString(uri); 1.186 + mozilla::widget::android::GeckoAppShell::MarkURIVisited(uriString); 1.187 + } 1.188 + 1.189 + AppendToRecentlyVisitedURIs(aURI); 1.190 + 1.191 + // Finally, notify that we've been visited. 1.192 + nsCOMPtr<nsIObserverService> obsService = 1.193 + mozilla::services::GetObserverService(); 1.194 + if (obsService) { 1.195 + obsService->NotifyObservers(aURI, NS_LINK_VISITED_EVENT_TOPIC, nullptr); 1.196 + } 1.197 + 1.198 + return NS_OK; 1.199 +} 1.200 + 1.201 +NS_IMETHODIMP 1.202 +nsAndroidHistory::SetURITitle(nsIURI *aURI, const nsAString& aTitle) 1.203 +{ 1.204 + // Silently return if URI is something we shouldn't add to DB. 1.205 + bool canAdd; 1.206 + nsresult rv = CanAddURI(aURI, &canAdd); 1.207 + NS_ENSURE_SUCCESS(rv, rv); 1.208 + if (!canAdd) { 1.209 + return NS_OK; 1.210 + } 1.211 + 1.212 + if (IsEmbedURI(aURI)) { 1.213 + return NS_OK; 1.214 + } 1.215 + 1.216 + if (AndroidBridge::HasEnv()) { 1.217 + nsAutoCString uri; 1.218 + nsresult rv = aURI->GetSpec(uri); 1.219 + if (NS_FAILED(rv)) return rv; 1.220 + NS_ConvertUTF8toUTF16 uriString(uri); 1.221 + mozilla::widget::android::GeckoAppShell::SetURITitle(uriString, aTitle); 1.222 + } 1.223 + return NS_OK; 1.224 +} 1.225 + 1.226 +NS_IMETHODIMP 1.227 +nsAndroidHistory::NotifyVisited(nsIURI *aURI) 1.228 +{ 1.229 + if (aURI && sHistory) { 1.230 + nsAutoCString spec; 1.231 + (void)aURI->GetSpec(spec); 1.232 + sHistory->mPendingURIs.Push(NS_ConvertUTF8toUTF16(spec)); 1.233 + NS_DispatchToMainThread(sHistory); 1.234 + } 1.235 + return NS_OK; 1.236 +} 1.237 + 1.238 +NS_IMETHODIMP 1.239 +nsAndroidHistory::Run() 1.240 +{ 1.241 + while (! mPendingURIs.IsEmpty()) { 1.242 + nsString uriString = mPendingURIs.Pop(); 1.243 + nsTArray<Link*>* list = sHistory->mListeners.Get(uriString); 1.244 + if (list) { 1.245 + for (unsigned int i = 0; i < list->Length(); i++) { 1.246 + list->ElementAt(i)->SetLinkState(eLinkState_Visited); 1.247 + } 1.248 + // as per the IHistory interface contract, remove the 1.249 + // Link pointers once they have been notified 1.250 + mListeners.Remove(uriString); 1.251 + delete list; 1.252 + } 1.253 + } 1.254 + return NS_OK; 1.255 +} 1.256 + 1.257 +// Filter out unwanted URIs such as "chrome:", "mailbox:", etc. 1.258 +// 1.259 +// The model is if we don't know differently then add which basically means 1.260 +// we are suppose to try all the things we know not to allow in and then if 1.261 +// we don't bail go on and allow it in. 1.262 +// 1.263 +// Logic ported from nsNavHistory::CanAddURI. 1.264 + 1.265 +NS_IMETHODIMP 1.266 +nsAndroidHistory::CanAddURI(nsIURI* aURI, bool* canAdd) 1.267 +{ 1.268 + NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread"); 1.269 + NS_ENSURE_ARG(aURI); 1.270 + NS_ENSURE_ARG_POINTER(canAdd); 1.271 + 1.272 + nsAutoCString scheme; 1.273 + nsresult rv = aURI->GetScheme(scheme); 1.274 + NS_ENSURE_SUCCESS(rv, rv); 1.275 + 1.276 + // first check the most common cases (HTTP, HTTPS) to allow in to avoid most 1.277 + // of the work 1.278 + if (scheme.EqualsLiteral("http")) { 1.279 + *canAdd = true; 1.280 + return NS_OK; 1.281 + } 1.282 + if (scheme.EqualsLiteral("https")) { 1.283 + *canAdd = true; 1.284 + return NS_OK; 1.285 + } 1.286 + 1.287 + // now check for all bad things 1.288 + if (scheme.EqualsLiteral("about") || 1.289 + scheme.EqualsLiteral("imap") || 1.290 + scheme.EqualsLiteral("news") || 1.291 + scheme.EqualsLiteral("mailbox") || 1.292 + scheme.EqualsLiteral("moz-anno") || 1.293 + scheme.EqualsLiteral("view-source") || 1.294 + scheme.EqualsLiteral("chrome") || 1.295 + scheme.EqualsLiteral("resource") || 1.296 + scheme.EqualsLiteral("data") || 1.297 + scheme.EqualsLiteral("wyciwyg") || 1.298 + scheme.EqualsLiteral("javascript") || 1.299 + scheme.EqualsLiteral("blob")) { 1.300 + *canAdd = false; 1.301 + return NS_OK; 1.302 + } 1.303 + *canAdd = true; 1.304 + return NS_OK; 1.305 +}