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.geckoview;
7 
8 import android.util.Pair;
9 import androidx.annotation.AnyThread;
10 import androidx.annotation.NonNull;
11 import androidx.annotation.Nullable;
12 import java.util.Arrays;
13 import java.util.List;
14 import org.mozilla.gecko.EventDispatcher;
15 import org.mozilla.gecko.util.GeckoBundle;
16 import org.mozilla.geckoview.GeckoSession.FinderDisplayFlags;
17 import org.mozilla.geckoview.GeckoSession.FinderFindFlags;
18 import org.mozilla.geckoview.GeckoSession.FinderResult;
19 
20 /**
21  * {@code SessionFinder} instances returned by {@link GeckoSession#getFinder()} performs
22  * find-in-page operations.
23  */
24 @AnyThread
25 public final class SessionFinder {
26   private static final String LOGTAG = "GeckoSessionFinder";
27 
28   private static final List<Pair<Integer, String>> sFlagNames =
29       Arrays.asList(
30           new Pair<>(GeckoSession.FINDER_FIND_BACKWARDS, "backwards"),
31           new Pair<>(GeckoSession.FINDER_FIND_LINKS_ONLY, "linksOnly"),
32           new Pair<>(GeckoSession.FINDER_FIND_MATCH_CASE, "matchCase"),
33           new Pair<>(GeckoSession.FINDER_FIND_WHOLE_WORD, "wholeWord"));
34 
addFlagsToBundle( @inderFindFlags final int flags, @NonNull final GeckoBundle bundle)35   private static void addFlagsToBundle(
36       @FinderFindFlags final int flags, @NonNull final GeckoBundle bundle) {
37     for (final Pair<Integer, String> name : sFlagNames) {
38       if ((flags & name.first) != 0) {
39         bundle.putBoolean(name.second, true);
40       }
41     }
42   }
43 
getFlagsFromBundle(@ullable final GeckoBundle bundle)44   /* package */ static int getFlagsFromBundle(@Nullable final GeckoBundle bundle) {
45     if (bundle == null) {
46       return 0;
47     }
48 
49     int flags = 0;
50     for (final Pair<Integer, String> name : sFlagNames) {
51       if (bundle.getBoolean(name.second)) {
52         flags |= name.first;
53       }
54     }
55     return flags;
56   }
57 
58   private final EventDispatcher mDispatcher;
59   @FinderDisplayFlags private int mDisplayFlags;
60 
SessionFinder(@onNull final EventDispatcher dispatcher)61   /* package */ SessionFinder(@NonNull final EventDispatcher dispatcher) {
62     mDispatcher = dispatcher;
63     setDisplayFlags(0);
64   }
65 
66   /**
67    * Find and select a string on the current page, starting from the current selection or the start
68    * of the page if there is no selection. Optionally return results related to the search in a
69    * {@link FinderResult} object. If {@code searchString} is null, search is performed using the
70    * previous search string.
71    *
72    * @param searchString String to search, or null to find again using the previous string.
73    * @param flags Flags for performing the search; either 0 or a combination of {@link
74    *     GeckoSession#FINDER_FIND_BACKWARDS FINDER_FIND_*} constants.
75    * @return Result of the search operation as a {@link GeckoResult} object.
76    * @see #clear
77    * @see #setDisplayFlags
78    */
79   @NonNull
find( @ullable final String searchString, @FinderFindFlags final int flags)80   public GeckoResult<FinderResult> find(
81       @Nullable final String searchString, @FinderFindFlags final int flags) {
82     final GeckoBundle bundle = new GeckoBundle(sFlagNames.size() + 1);
83     bundle.putString("searchString", searchString);
84     addFlagsToBundle(flags, bundle);
85 
86     return mDispatcher
87         .queryBundle("GeckoView:FindInPage", bundle)
88         .map(response -> new FinderResult(response));
89   }
90 
91   /**
92    * Clear any highlighted find-in-page matches.
93    *
94    * @see #find
95    * @see #setDisplayFlags
96    */
clear()97   public void clear() {
98     mDispatcher.dispatch("GeckoView:ClearMatches", null);
99   }
100 
101   /**
102    * Return flags for displaying find-in-page matches.
103    *
104    * @return Display flags as a combination of {@link GeckoSession#FINDER_DISPLAY_HIGHLIGHT_ALL
105    *     FINDER_DISPLAY_*} constants.
106    * @see #setDisplayFlags
107    * @see #find
108    */
109   @FinderDisplayFlags
getDisplayFlags()110   public int getDisplayFlags() {
111     return mDisplayFlags;
112   }
113 
114   /**
115    * Set flags for displaying find-in-page matches.
116    *
117    * @param flags Display flags as a combination of {@link GeckoSession#FINDER_DISPLAY_HIGHLIGHT_ALL
118    *     FINDER_DISPLAY_*} constants.
119    * @see #getDisplayFlags
120    * @see #find
121    */
setDisplayFlags(@inderDisplayFlags final int flags)122   public void setDisplayFlags(@FinderDisplayFlags final int flags) {
123     mDisplayFlags = flags;
124 
125     final GeckoBundle bundle = new GeckoBundle(3);
126     bundle.putBoolean("highlightAll", (flags & GeckoSession.FINDER_DISPLAY_HIGHLIGHT_ALL) != 0);
127     bundle.putBoolean("dimPage", (flags & GeckoSession.FINDER_DISPLAY_DIM_PAGE) != 0);
128     bundle.putBoolean("drawOutline", (flags & GeckoSession.FINDER_DISPLAY_DRAW_LINK_OUTLINE) != 0);
129     mDispatcher.dispatch("GeckoView:DisplayMatches", bundle);
130   }
131 }
132