|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko; |
|
7 |
|
8 import org.mozilla.gecko.db.BrowserDB; |
|
9 import org.mozilla.gecko.gfx.LayerView; |
|
10 import org.mozilla.gecko.mozglue.GeckoLoader; |
|
11 import org.mozilla.gecko.util.Clipboard; |
|
12 import org.mozilla.gecko.util.HardwareUtils; |
|
13 import org.mozilla.gecko.util.GeckoEventListener; |
|
14 import org.mozilla.gecko.util.ThreadUtils; |
|
15 |
|
16 import org.json.JSONException; |
|
17 import org.json.JSONObject; |
|
18 |
|
19 import android.app.Activity; |
|
20 import android.content.Context; |
|
21 import android.content.Intent; |
|
22 import android.content.SharedPreferences; |
|
23 import android.content.res.TypedArray; |
|
24 import android.os.Handler; |
|
25 import android.util.AttributeSet; |
|
26 import android.util.Log; |
|
27 import java.util.ArrayList; |
|
28 import java.util.Collections; |
|
29 import java.util.List; |
|
30 |
|
31 public class GeckoView extends LayerView |
|
32 implements GeckoEventListener, ContextGetter { |
|
33 |
|
34 private static final String DEFAULT_SHARED_PREFERENCES_FILE = "GeckoView"; |
|
35 private static final String LOGTAG = "GeckoView"; |
|
36 |
|
37 private ChromeDelegate mChromeDelegate; |
|
38 private ContentDelegate mContentDelegate; |
|
39 |
|
40 public GeckoView(Context context) { |
|
41 super(context); |
|
42 init(context, null, true); |
|
43 } |
|
44 |
|
45 public GeckoView(Context context, AttributeSet attrs) { |
|
46 super(context, attrs); |
|
47 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GeckoView); |
|
48 String url = a.getString(R.styleable.GeckoView_url); |
|
49 boolean doInit = a.getBoolean(R.styleable.GeckoView_doinit, true); |
|
50 a.recycle(); |
|
51 init(context, url, doInit); |
|
52 } |
|
53 |
|
54 private void init(Context context, String url, boolean doInit) { |
|
55 // TODO: Fennec currently takes care of its own initialization, so this |
|
56 // flag is a hack used in Fennec to prevent GeckoView initialization. |
|
57 // This should go away once Fennec also uses GeckoView for |
|
58 // initialization. |
|
59 if (!doInit) |
|
60 return; |
|
61 |
|
62 // If running outside of a GeckoActivity (eg, from a library project), |
|
63 // load the native code and disable content providers |
|
64 boolean isGeckoActivity = false; |
|
65 try { |
|
66 isGeckoActivity = context instanceof GeckoActivity; |
|
67 } catch (NoClassDefFoundError ex) {} |
|
68 |
|
69 if (!isGeckoActivity) { |
|
70 // Set the GeckoInterface if the context is an activity and the GeckoInterface |
|
71 // has not already been set |
|
72 if (context instanceof Activity && getGeckoInterface() == null) { |
|
73 setGeckoInterface(new BaseGeckoInterface(context)); |
|
74 } |
|
75 |
|
76 Clipboard.init(context); |
|
77 HardwareUtils.init(context); |
|
78 |
|
79 // If you want to use GeckoNetworkManager, start it. |
|
80 |
|
81 GeckoLoader.loadMozGlue(); |
|
82 BrowserDB.setEnableContentProviders(false); |
|
83 } |
|
84 |
|
85 if (url != null) { |
|
86 GeckoThread.setUri(url); |
|
87 GeckoThread.setAction(Intent.ACTION_VIEW); |
|
88 GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(url)); |
|
89 } |
|
90 GeckoAppShell.setContextGetter(this); |
|
91 if (context instanceof Activity) { |
|
92 Tabs tabs = Tabs.getInstance(); |
|
93 tabs.attachToContext(context); |
|
94 } |
|
95 |
|
96 GeckoAppShell.registerEventListener("Gecko:Ready", this); |
|
97 GeckoAppShell.registerEventListener("Content:StateChange", this); |
|
98 GeckoAppShell.registerEventListener("Content:LoadError", this); |
|
99 GeckoAppShell.registerEventListener("Content:PageShow", this); |
|
100 GeckoAppShell.registerEventListener("DOMTitleChanged", this); |
|
101 GeckoAppShell.registerEventListener("Link:Favicon", this); |
|
102 GeckoAppShell.registerEventListener("Prompt:Show", this); |
|
103 GeckoAppShell.registerEventListener("Prompt:ShowTop", this); |
|
104 |
|
105 ThreadUtils.setUiThread(Thread.currentThread(), new Handler()); |
|
106 initializeView(GeckoAppShell.getEventDispatcher()); |
|
107 |
|
108 if (GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.Launched)) { |
|
109 // This is the first launch, so finish initialization and go. |
|
110 GeckoProfile profile = GeckoProfile.get(context).forceCreate(); |
|
111 BrowserDB.initialize(profile.getName()); |
|
112 |
|
113 GeckoAppShell.setLayerView(this); |
|
114 GeckoThread.createAndStart(); |
|
115 } else if(GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) { |
|
116 // If Gecko is already running, that means the Activity was |
|
117 // destroyed, so we need to re-attach Gecko to this GeckoView. |
|
118 connectToGecko(); |
|
119 } |
|
120 } |
|
121 |
|
122 /** |
|
123 * Add a Browser to the GeckoView container. |
|
124 * @param url The URL resource to load into the new Browser. |
|
125 */ |
|
126 public Browser addBrowser(String url) { |
|
127 Tab tab = Tabs.getInstance().loadUrl(url, Tabs.LOADURL_NEW_TAB); |
|
128 if (tab != null) { |
|
129 return new Browser(tab.getId()); |
|
130 } |
|
131 return null; |
|
132 } |
|
133 |
|
134 /** |
|
135 * Remove a Browser from the GeckoView container. |
|
136 * @param browser The Browser to remove. |
|
137 */ |
|
138 public void removeBrowser(Browser browser) { |
|
139 Tab tab = Tabs.getInstance().getTab(browser.getId()); |
|
140 if (tab != null) { |
|
141 Tabs.getInstance().closeTab(tab); |
|
142 } |
|
143 } |
|
144 |
|
145 /** |
|
146 * Set the active/visible Browser. |
|
147 * @param browser The Browser to make selected. |
|
148 */ |
|
149 public void setCurrentBrowser(Browser browser) { |
|
150 Tab tab = Tabs.getInstance().getTab(browser.getId()); |
|
151 if (tab != null) { |
|
152 Tabs.getInstance().selectTab(tab.getId()); |
|
153 } |
|
154 } |
|
155 |
|
156 /** |
|
157 * Get the active/visible Browser. |
|
158 * @return The current selected Browser. |
|
159 */ |
|
160 public Browser getCurrentBrowser() { |
|
161 Tab tab = Tabs.getInstance().getSelectedTab(); |
|
162 if (tab != null) { |
|
163 return new Browser(tab.getId()); |
|
164 } |
|
165 return null; |
|
166 } |
|
167 |
|
168 /** |
|
169 * Get the list of current Browsers in the GeckoView container. |
|
170 * @return An unmodifiable List of Browser objects. |
|
171 */ |
|
172 public List<Browser> getBrowsers() { |
|
173 ArrayList<Browser> browsers = new ArrayList<Browser>(); |
|
174 Iterable<Tab> tabs = Tabs.getInstance().getTabsInOrder(); |
|
175 for (Tab tab : tabs) { |
|
176 browsers.add(new Browser(tab.getId())); |
|
177 } |
|
178 return Collections.unmodifiableList(browsers); |
|
179 } |
|
180 |
|
181 /** |
|
182 * Not part of the public API. Ignore. |
|
183 */ |
|
184 public void handleMessage(final String event, final JSONObject message) { |
|
185 ThreadUtils.postToUiThread(new Runnable() { |
|
186 @Override |
|
187 public void run() { |
|
188 try { |
|
189 if (event.equals("Gecko:Ready")) { |
|
190 GeckoView.this.handleReady(message); |
|
191 } else if (event.equals("Content:StateChange")) { |
|
192 GeckoView.this.handleStateChange(message); |
|
193 } else if (event.equals("Content:LoadError")) { |
|
194 GeckoView.this.handleLoadError(message); |
|
195 } else if (event.equals("Content:PageShow")) { |
|
196 GeckoView.this.handlePageShow(message); |
|
197 } else if (event.equals("DOMTitleChanged")) { |
|
198 GeckoView.this.handleTitleChanged(message); |
|
199 } else if (event.equals("Link:Favicon")) { |
|
200 GeckoView.this.handleLinkFavicon(message); |
|
201 } else if (event.equals("Prompt:Show") || event.equals("Prompt:ShowTop")) { |
|
202 GeckoView.this.handlePrompt(message); |
|
203 } |
|
204 } catch (Exception e) { |
|
205 Log.w(LOGTAG, "handleMessage threw for " + event, e); |
|
206 } |
|
207 } |
|
208 }); |
|
209 } |
|
210 |
|
211 private void connectToGecko() { |
|
212 GeckoThread.setLaunchState(GeckoThread.LaunchState.GeckoRunning); |
|
213 Tab selectedTab = Tabs.getInstance().getSelectedTab(); |
|
214 if (selectedTab != null) |
|
215 Tabs.getInstance().notifyListeners(selectedTab, Tabs.TabEvents.SELECTED); |
|
216 geckoConnected(); |
|
217 GeckoAppShell.setLayerClient(getLayerClientObject()); |
|
218 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Viewport:Flush", null)); |
|
219 } |
|
220 |
|
221 private void handleReady(final JSONObject message) { |
|
222 connectToGecko(); |
|
223 |
|
224 if (mChromeDelegate != null) { |
|
225 mChromeDelegate.onReady(this); |
|
226 } |
|
227 } |
|
228 |
|
229 private void handleStateChange(final JSONObject message) throws JSONException { |
|
230 int state = message.getInt("state"); |
|
231 if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) { |
|
232 if ((state & GeckoAppShell.WPL_STATE_START) != 0) { |
|
233 if (mContentDelegate != null) { |
|
234 int id = message.getInt("tabID"); |
|
235 mContentDelegate.onPageStart(this, new Browser(id), message.getString("uri")); |
|
236 } |
|
237 } else if ((state & GeckoAppShell.WPL_STATE_STOP) != 0) { |
|
238 if (mContentDelegate != null) { |
|
239 int id = message.getInt("tabID"); |
|
240 mContentDelegate.onPageStop(this, new Browser(id), message.getBoolean("success")); |
|
241 } |
|
242 } |
|
243 } |
|
244 } |
|
245 |
|
246 private void handleLoadError(final JSONObject message) throws JSONException { |
|
247 if (mContentDelegate != null) { |
|
248 int id = message.getInt("tabID"); |
|
249 mContentDelegate.onPageStop(GeckoView.this, new Browser(id), false); |
|
250 } |
|
251 } |
|
252 |
|
253 private void handlePageShow(final JSONObject message) throws JSONException { |
|
254 if (mContentDelegate != null) { |
|
255 int id = message.getInt("tabID"); |
|
256 mContentDelegate.onPageShow(GeckoView.this, new Browser(id)); |
|
257 } |
|
258 } |
|
259 |
|
260 private void handleTitleChanged(final JSONObject message) throws JSONException { |
|
261 if (mContentDelegate != null) { |
|
262 int id = message.getInt("tabID"); |
|
263 mContentDelegate.onReceivedTitle(GeckoView.this, new Browser(id), message.getString("title")); |
|
264 } |
|
265 } |
|
266 |
|
267 private void handleLinkFavicon(final JSONObject message) throws JSONException { |
|
268 if (mContentDelegate != null) { |
|
269 int id = message.getInt("tabID"); |
|
270 mContentDelegate.onReceivedFavicon(GeckoView.this, new Browser(id), message.getString("href"), message.getInt("size")); |
|
271 } |
|
272 } |
|
273 |
|
274 private void handlePrompt(final JSONObject message) throws JSONException { |
|
275 if (mChromeDelegate != null) { |
|
276 String hint = message.optString("hint"); |
|
277 if ("alert".equals(hint)) { |
|
278 String text = message.optString("text"); |
|
279 mChromeDelegate.onAlert(GeckoView.this, null, text, new PromptResult(message.optString("guid"))); |
|
280 } else if ("confirm".equals(hint)) { |
|
281 String text = message.optString("text"); |
|
282 mChromeDelegate.onConfirm(GeckoView.this, null, text, new PromptResult(message.optString("guid"))); |
|
283 } else if ("prompt".equals(hint)) { |
|
284 String text = message.optString("text"); |
|
285 String defaultValue = message.optString("textbox0"); |
|
286 mChromeDelegate.onPrompt(GeckoView.this, null, text, defaultValue, new PromptResult(message.optString("guid"))); |
|
287 } else if ("remotedebug".equals(hint)) { |
|
288 mChromeDelegate.onDebugRequest(GeckoView.this, new PromptResult(message.optString("guid"))); |
|
289 } |
|
290 } |
|
291 } |
|
292 |
|
293 /** |
|
294 * Set the chrome callback handler. |
|
295 * This will replace the current handler. |
|
296 * @param chrome An implementation of GeckoViewChrome. |
|
297 */ |
|
298 public void setChromeDelegate(ChromeDelegate chrome) { |
|
299 mChromeDelegate = chrome; |
|
300 } |
|
301 |
|
302 /** |
|
303 * Set the content callback handler. |
|
304 * This will replace the current handler. |
|
305 * @param content An implementation of ContentDelegate. |
|
306 */ |
|
307 public void setContentDelegate(ContentDelegate content) { |
|
308 mContentDelegate = content; |
|
309 } |
|
310 |
|
311 public static void setGeckoInterface(final BaseGeckoInterface geckoInterface) { |
|
312 GeckoAppShell.setGeckoInterface(geckoInterface); |
|
313 } |
|
314 |
|
315 public static GeckoAppShell.GeckoInterface getGeckoInterface() { |
|
316 return GeckoAppShell.getGeckoInterface(); |
|
317 } |
|
318 |
|
319 protected String getSharedPreferencesFile() { |
|
320 return DEFAULT_SHARED_PREFERENCES_FILE; |
|
321 } |
|
322 |
|
323 public SharedPreferences getSharedPreferences() { |
|
324 return getContext().getSharedPreferences(getSharedPreferencesFile(), 0); |
|
325 } |
|
326 |
|
327 /** |
|
328 * Wrapper for a browser in the GeckoView container. Associated with a browser |
|
329 * element in the Gecko system. |
|
330 */ |
|
331 public class Browser { |
|
332 private final int mId; |
|
333 private Browser(int Id) { |
|
334 mId = Id; |
|
335 } |
|
336 |
|
337 /** |
|
338 * Get the ID of the Browser. This is the same ID used by Gecko for it's underlying |
|
339 * browser element. |
|
340 * @return The integer ID of the Browser. |
|
341 */ |
|
342 private int getId() { |
|
343 return mId; |
|
344 } |
|
345 |
|
346 /** |
|
347 * Load a URL resource into the Browser. |
|
348 * @param url The URL string. |
|
349 */ |
|
350 public void loadUrl(String url) { |
|
351 JSONObject args = new JSONObject(); |
|
352 try { |
|
353 args.put("url", url); |
|
354 args.put("parentId", -1); |
|
355 args.put("newTab", false); |
|
356 args.put("tabID", mId); |
|
357 } catch (Exception e) { |
|
358 Log.w(LOGTAG, "Error building JSON arguments for loadUrl.", e); |
|
359 } |
|
360 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tab:Load", args.toString())); |
|
361 } |
|
362 |
|
363 /** |
|
364 * Reload the current URL resource into the Browser. The URL is force loaded from the |
|
365 * network and is not pulled from cache. |
|
366 */ |
|
367 public void reload() { |
|
368 Tab tab = Tabs.getInstance().getTab(mId); |
|
369 if (tab != null) { |
|
370 tab.doReload(); |
|
371 } |
|
372 } |
|
373 |
|
374 /** |
|
375 * Stop the current loading operation. |
|
376 */ |
|
377 public void stop() { |
|
378 Tab tab = Tabs.getInstance().getTab(mId); |
|
379 if (tab != null) { |
|
380 tab.doStop(); |
|
381 } |
|
382 } |
|
383 |
|
384 /** |
|
385 * Check to see if the Browser has session history and can go back to a |
|
386 * previous page. |
|
387 * @return A boolean flag indicating if previous session exists. |
|
388 * This method will likely be removed and replaced by a callback in GeckoViewContent |
|
389 */ |
|
390 public boolean canGoBack() { |
|
391 Tab tab = Tabs.getInstance().getTab(mId); |
|
392 if (tab != null) { |
|
393 return tab.canDoBack(); |
|
394 } |
|
395 return false; |
|
396 } |
|
397 |
|
398 /** |
|
399 * Move backward in the session history, if that's possible. |
|
400 */ |
|
401 public void goBack() { |
|
402 Tab tab = Tabs.getInstance().getTab(mId); |
|
403 if (tab != null) { |
|
404 tab.doBack(); |
|
405 } |
|
406 } |
|
407 |
|
408 /** |
|
409 * Check to see if the Browser has session history and can go forward to a |
|
410 * new page. |
|
411 * @return A boolean flag indicating if forward session exists. |
|
412 * This method will likely be removed and replaced by a callback in GeckoViewContent |
|
413 */ |
|
414 public boolean canGoForward() { |
|
415 Tab tab = Tabs.getInstance().getTab(mId); |
|
416 if (tab != null) { |
|
417 return tab.canDoForward(); |
|
418 } |
|
419 return false; |
|
420 } |
|
421 |
|
422 /** |
|
423 * Move forward in the session history, if that's possible. |
|
424 */ |
|
425 public void goForward() { |
|
426 Tab tab = Tabs.getInstance().getTab(mId); |
|
427 if (tab != null) { |
|
428 tab.doForward(); |
|
429 } |
|
430 } |
|
431 } |
|
432 |
|
433 /* Provides a means for the client to indicate whether a JavaScript |
|
434 * dialog request should proceed. An instance of this class is passed to |
|
435 * various GeckoViewChrome callback actions. |
|
436 */ |
|
437 public class PromptResult { |
|
438 private final int RESULT_OK = 0; |
|
439 private final int RESULT_CANCEL = 1; |
|
440 |
|
441 private final String mGUID; |
|
442 |
|
443 public PromptResult(String guid) { |
|
444 mGUID = guid; |
|
445 } |
|
446 |
|
447 private JSONObject makeResult(int resultCode) { |
|
448 JSONObject result = new JSONObject(); |
|
449 try { |
|
450 result.put("guid", mGUID); |
|
451 result.put("button", resultCode); |
|
452 } catch(JSONException ex) { } |
|
453 return result; |
|
454 } |
|
455 |
|
456 /** |
|
457 * Handle a confirmation response from the user. |
|
458 */ |
|
459 public void confirm() { |
|
460 JSONObject result = makeResult(RESULT_OK); |
|
461 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Prompt:Reply", result.toString())); |
|
462 } |
|
463 |
|
464 /** |
|
465 * Handle a confirmation response from the user. |
|
466 * @param value String value to return to the browser context. |
|
467 */ |
|
468 public void confirmWithValue(String value) { |
|
469 JSONObject result = makeResult(RESULT_OK); |
|
470 try { |
|
471 result.put("textbox0", value); |
|
472 } catch(JSONException ex) { } |
|
473 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Prompt:Reply", result.toString())); |
|
474 } |
|
475 |
|
476 /** |
|
477 * Handle a cancellation response from the user. |
|
478 */ |
|
479 public void cancel() { |
|
480 JSONObject result = makeResult(RESULT_CANCEL); |
|
481 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Prompt:Reply", result.toString())); |
|
482 } |
|
483 } |
|
484 |
|
485 public interface ChromeDelegate { |
|
486 /** |
|
487 * Tell the host application that Gecko is ready to handle requests. |
|
488 * @param view The GeckoView that initiated the callback. |
|
489 */ |
|
490 public void onReady(GeckoView view); |
|
491 |
|
492 /** |
|
493 * Tell the host application to display an alert dialog. |
|
494 * @param view The GeckoView that initiated the callback. |
|
495 * @param browser The Browser that is loading the content. |
|
496 * @param message The string to display in the dialog. |
|
497 * @param result A PromptResult used to send back the result without blocking. |
|
498 * Defaults to cancel requests. |
|
499 */ |
|
500 public void onAlert(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result); |
|
501 |
|
502 /** |
|
503 * Tell the host application to display a confirmation dialog. |
|
504 * @param view The GeckoView that initiated the callback. |
|
505 * @param browser The Browser that is loading the content. |
|
506 * @param message The string to display in the dialog. |
|
507 * @param result A PromptResult used to send back the result without blocking. |
|
508 * Defaults to cancel requests. |
|
509 */ |
|
510 public void onConfirm(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result); |
|
511 |
|
512 /** |
|
513 * Tell the host application to display an input prompt dialog. |
|
514 * @param view The GeckoView that initiated the callback. |
|
515 * @param browser The Browser that is loading the content. |
|
516 * @param message The string to display in the dialog. |
|
517 * @param defaultValue The string to use as default input. |
|
518 * @param result A PromptResult used to send back the result without blocking. |
|
519 * Defaults to cancel requests. |
|
520 */ |
|
521 public void onPrompt(GeckoView view, GeckoView.Browser browser, String message, String defaultValue, GeckoView.PromptResult result); |
|
522 |
|
523 /** |
|
524 * Tell the host application to display a remote debugging request dialog. |
|
525 * @param view The GeckoView that initiated the callback. |
|
526 * @param result A PromptResult used to send back the result without blocking. |
|
527 * Defaults to cancel requests. |
|
528 */ |
|
529 public void onDebugRequest(GeckoView view, GeckoView.PromptResult result); |
|
530 } |
|
531 |
|
532 public interface ContentDelegate { |
|
533 /** |
|
534 * A Browser has started loading content from the network. |
|
535 * @param view The GeckoView that initiated the callback. |
|
536 * @param browser The Browser that is loading the content. |
|
537 * @param url The resource being loaded. |
|
538 */ |
|
539 public void onPageStart(GeckoView view, GeckoView.Browser browser, String url); |
|
540 |
|
541 /** |
|
542 * A Browser has finished loading content from the network. |
|
543 * @param view The GeckoView that initiated the callback. |
|
544 * @param browser The Browser that was loading the content. |
|
545 * @param success Whether the page loaded successfully or an error occured. |
|
546 */ |
|
547 public void onPageStop(GeckoView view, GeckoView.Browser browser, boolean success); |
|
548 |
|
549 /** |
|
550 * A Browser is displaying content. This page could have been loaded via |
|
551 * network or from the session history. |
|
552 * @param view The GeckoView that initiated the callback. |
|
553 * @param browser The Browser that is showing the content. |
|
554 */ |
|
555 public void onPageShow(GeckoView view, GeckoView.Browser browser); |
|
556 |
|
557 /** |
|
558 * A page title was discovered in the content or updated after the content |
|
559 * loaded. |
|
560 * @param view The GeckoView that initiated the callback. |
|
561 * @param browser The Browser that is showing the content. |
|
562 * @param title The title sent from the content. |
|
563 */ |
|
564 public void onReceivedTitle(GeckoView view, GeckoView.Browser browser, String title); |
|
565 |
|
566 /** |
|
567 * A link element was discovered in the content or updated after the content |
|
568 * loaded that specifies a favicon. |
|
569 * @param view The GeckoView that initiated the callback. |
|
570 * @param browser The Browser that is showing the content. |
|
571 * @param url The href of the link element specifying the favicon. |
|
572 * @param size The maximum size specified for the favicon, or -1 for any size. |
|
573 */ |
|
574 public void onReceivedFavicon(GeckoView view, GeckoView.Browser browser, String url, int size); |
|
575 } |
|
576 |
|
577 } |