- add sources.
[platform/framework/web/crosswalk.git] / src / android_webview / java / src / org / chromium / android_webview / AwContentsClientCallbackHelper.java
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.android_webview;
6
7 import android.graphics.Picture;
8 import android.os.Handler;
9 import android.os.Looper;
10 import android.os.Message;
11 import android.os.SystemClock;
12
13 import org.chromium.content.browser.ContentViewCore;
14
15 import java.util.concurrent.Callable;
16
17 /**
18  * This class is responsible for calling certain client callbacks on the UI thread.
19  *
20  * Most callbacks do no go through here, but get forwarded to AwContentsClient directly. The
21  * messages processed here may originate from the IO or UI thread.
22  */
23 class AwContentsClientCallbackHelper {
24
25     // TODO(boliu): Consider removing DownloadInfo and LoginRequestInfo by using native
26     // MessageLoop to post directly to AwContents.
27
28     private static class DownloadInfo {
29         final String mUrl;
30         final String mUserAgent;
31         final String mContentDisposition;
32         final String mMimeType;
33         final long mContentLength;
34
35         DownloadInfo(String url,
36                      String userAgent,
37                      String contentDisposition,
38                      String mimeType,
39                      long contentLength) {
40             mUrl = url;
41             mUserAgent = userAgent;
42             mContentDisposition = contentDisposition;
43             mMimeType = mimeType;
44             mContentLength = contentLength;
45         }
46     }
47
48     private static class LoginRequestInfo {
49         final String mRealm;
50         final String mAccount;
51         final String mArgs;
52
53         LoginRequestInfo(String realm, String account, String args) {
54           mRealm = realm;
55           mAccount = account;
56           mArgs = args;
57         }
58     }
59
60     private static class OnReceivedErrorInfo {
61         final int mErrorCode;
62         final String mDescription;
63         final String mFailingUrl;
64
65         OnReceivedErrorInfo(int errorCode, String description, String failingUrl) {
66             mErrorCode = errorCode;
67             mDescription = description;
68             mFailingUrl = failingUrl;
69         }
70     }
71
72     private final static int MSG_ON_LOAD_RESOURCE = 1;
73     private final static int MSG_ON_PAGE_STARTED = 2;
74     private final static int MSG_ON_DOWNLOAD_START = 3;
75     private final static int MSG_ON_RECEIVED_LOGIN_REQUEST = 4;
76     private final static int MSG_ON_RECEIVED_ERROR = 5;
77     private final static int MSG_ON_NEW_PICTURE = 6;
78     private final static int MSG_ON_SCALE_CHANGED_SCALED = 7;
79
80     // Minimum period allowed between consecutive onNewPicture calls, to rate-limit the callbacks.
81     private static final long ON_NEW_PICTURE_MIN_PERIOD_MILLIS = 500;
82     // Timestamp of the most recent onNewPicture callback.
83     private long mLastPictureTime = 0;
84     // True when a onNewPicture callback is currenly in flight.
85     private boolean mHasPendingOnNewPicture = false;
86
87     private final AwContentsClient mContentsClient;
88
89     private final Handler mHandler;
90
91     private class MyHandler extends Handler {
92         private MyHandler(Looper looper) {
93             super(looper);
94         }
95
96         @Override
97         public void handleMessage(Message msg) {
98             switch(msg.what) {
99                 case MSG_ON_LOAD_RESOURCE: {
100                     final String url = (String) msg.obj;
101                     mContentsClient.onLoadResource(url);
102                     break;
103                 }
104                 case MSG_ON_PAGE_STARTED: {
105                     final String url = (String) msg.obj;
106                     mContentsClient.onPageStarted(url);
107                     break;
108                 }
109                 case MSG_ON_DOWNLOAD_START: {
110                     DownloadInfo info = (DownloadInfo) msg.obj;
111                     mContentsClient.onDownloadStart(info.mUrl, info.mUserAgent,
112                             info.mContentDisposition, info.mMimeType, info.mContentLength);
113                     break;
114                 }
115                 case MSG_ON_RECEIVED_LOGIN_REQUEST: {
116                     LoginRequestInfo info = (LoginRequestInfo) msg.obj;
117                     mContentsClient.onReceivedLoginRequest(info.mRealm, info.mAccount, info.mArgs);
118                     break;
119                 }
120                 case MSG_ON_RECEIVED_ERROR: {
121                     OnReceivedErrorInfo info = (OnReceivedErrorInfo) msg.obj;
122                     mContentsClient.onReceivedError(info.mErrorCode, info.mDescription,
123                             info.mFailingUrl);
124                     break;
125                 }
126                 case MSG_ON_NEW_PICTURE: {
127                     Picture picture = null;
128                     try {
129                         if (msg.obj != null) picture = (Picture) ((Callable<?>) msg.obj).call();
130                     } catch (Exception e) {
131                         throw new RuntimeException("Error getting picture", e);
132                     }
133                     mContentsClient.onNewPicture(picture);
134                     mLastPictureTime = SystemClock.uptimeMillis();
135                     mHasPendingOnNewPicture = false;
136                     break;
137                 }
138                 case MSG_ON_SCALE_CHANGED_SCALED: {
139                     float oldScale = Float.intBitsToFloat(msg.arg1);
140                     float newScale = Float.intBitsToFloat(msg.arg2);
141                     mContentsClient.onScaleChangedScaled(oldScale, newScale);
142                     break;
143                 }
144                 default:
145                     throw new IllegalStateException(
146                             "AwContentsClientCallbackHelper: unhandled message " + msg.what);
147             }
148         }
149     };
150
151     public AwContentsClientCallbackHelper(Looper looper, AwContentsClient contentsClient) {
152         mHandler = new MyHandler(looper);
153         mContentsClient = contentsClient;
154     }
155
156     public void postOnLoadResource(String url) {
157         mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_LOAD_RESOURCE, url));
158     }
159
160     public void postOnPageStarted(String url) {
161         mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_PAGE_STARTED, url));
162     }
163
164     public void postOnDownloadStart(String url, String userAgent, String contentDisposition,
165             String mimeType, long contentLength) {
166         DownloadInfo info = new DownloadInfo(url, userAgent, contentDisposition, mimeType,
167                 contentLength);
168         mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_DOWNLOAD_START, info));
169     }
170
171     public void postOnReceivedLoginRequest(String realm, String account, String args) {
172         LoginRequestInfo info = new LoginRequestInfo(realm, account, args);
173         mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_RECEIVED_LOGIN_REQUEST, info));
174     }
175
176     public void postOnReceivedError(int errorCode, String description, String failingUrl) {
177         OnReceivedErrorInfo info = new OnReceivedErrorInfo(errorCode, description, failingUrl);
178         mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_RECEIVED_ERROR, info));
179     }
180
181     public void postOnNewPicture(Callable<Picture> pictureProvider) {
182         if (mHasPendingOnNewPicture) return;
183         mHasPendingOnNewPicture = true;
184         long pictureTime = java.lang.Math.max(mLastPictureTime + ON_NEW_PICTURE_MIN_PERIOD_MILLIS,
185                 SystemClock.uptimeMillis());
186         mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ON_NEW_PICTURE, pictureProvider),
187                 pictureTime);
188     }
189
190     public void postOnScaleChangedScaled(float oldScale, float newScale) {
191         // The float->int->float conversion here is to avoid unnecessary allocations. The
192         // documentation states that intBitsToFloat(floatToIntBits(a)) == a for all values of a
193         // (except for NaNs which are collapsed to a single canonical NaN, but we don't care for
194         // that case).
195         mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_SCALE_CHANGED_SCALED,
196                     Float.floatToIntBits(oldScale), Float.floatToIntBits(newScale)));
197     }
198 }