1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
2  * ***** BEGIN LICENSE BLOCK *****
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
6  * You can obtain one at http://mozilla.org/MPL/2.0/.
7  *
8  * ***** END LICENSE BLOCK ***** */
9 
10 package org.mozilla.gecko;
11 
12 import java.util.LinkedList;
13 
14 import org.json.JSONArray;
15 import org.json.JSONException;
16 import org.json.JSONObject;
17 
18 import android.util.Log;
19 
20 public abstract class SessionParser {
21     private static final String LOGTAG = "GeckoSessionParser";
22 
23     public static final class SessionTab {
24         final private String mTitle;
25         final private String mUrl;
26         final private JSONObject mTabObject;
27         private boolean mIsSelected;
28 
SessionTab(String title, String url, boolean isSelected, JSONObject tabObject)29         private SessionTab(String title, String url, boolean isSelected, JSONObject tabObject) {
30             mTitle = title;
31             mUrl = url;
32             mIsSelected = isSelected;
33             mTabObject = tabObject;
34         }
35 
getTitle()36         public String getTitle() {
37             return mTitle;
38         }
39 
getUrl()40         public String getUrl() {
41             return mUrl;
42         }
43 
isSelected()44         public boolean isSelected() {
45             return mIsSelected;
46         }
47 
getTabObject()48         public JSONObject getTabObject() {
49             return mTabObject;
50         }
51 
52         /**
53          * Is this tab pointing to about:home and does not contain any other history?
54          */
isAboutHomeWithoutHistory()55         public boolean isAboutHomeWithoutHistory() {
56             JSONArray entries = mTabObject.optJSONArray("entries");
57             return entries != null && entries.length() == 1 && AboutPages.isAboutHome(mUrl);
58         }
59     };
60 
onTabRead(SessionTab tab)61     abstract public void onTabRead(SessionTab tab);
62 
63     /**
64      * Placeholder method that must be overloaded to handle closedTabs while parsing session data.
65      *
66      * @param closedTabs, JSONArray of recently closed tab entries.
67      * @throws JSONException
68      */
onClosedTabsRead(final JSONArray closedTabs)69     public void onClosedTabsRead(final JSONArray closedTabs) throws JSONException {
70     }
71 
72     /**
73      * Parses the provided session store data and calls onTabRead for each tab that has been found.
74      *
75      * @param sessionStrings One or more strings containing session store data.
76      * @return False if any of the session strings provided didn't contain valid session store data.
77      */
parse(String... sessionStrings)78     public boolean parse(String... sessionStrings) {
79         final LinkedList<SessionTab> sessionTabs = new LinkedList<SessionTab>();
80         int totalCount = 0;
81         int selectedIndex = -1;
82         try {
83             for (String sessionString : sessionStrings) {
84                 final JSONArray windowsArray = new JSONObject(sessionString).getJSONArray("windows");
85                 if (windowsArray.length() == 0) {
86                     // Session json can be empty if the user has opted out of session restore.
87                     Log.d(LOGTAG, "Session restore file is empty, no session entries found.");
88                     continue;
89                 }
90 
91                 final JSONObject window = windowsArray.getJSONObject(0);
92                 final JSONArray tabs = window.getJSONArray("tabs");
93                 final int optSelected = window.optInt("selected", -1);
94                 final JSONArray closedTabs = window.optJSONArray("closedTabs");
95                 if (closedTabs != null) {
96                     onClosedTabsRead(closedTabs);
97                 }
98 
99                 for (int i = 0; i < tabs.length(); i++) {
100                     final JSONObject tab = tabs.getJSONObject(i);
101                     final int index = tab.getInt("index");
102                     final JSONArray entries = tab.getJSONArray("entries");
103                     if (index < 1 || entries.length() < index) {
104                         Log.w(LOGTAG, "Session entries and index don't agree.");
105                         continue;
106                     }
107                     final JSONObject entry = entries.getJSONObject(index - 1);
108                     final String url = entry.getString("url");
109 
110                     String title = entry.optString("title");
111                     if (title.length() == 0) {
112                         title = url;
113                     }
114 
115                     totalCount++;
116                     boolean selected = false;
117                     if (optSelected == i + 1) {
118                         selected = true;
119                         selectedIndex = totalCount;
120                     }
121                     sessionTabs.add(new SessionTab(title, url, selected, tab));
122                 }
123             }
124         } catch (JSONException e) {
125             Log.e(LOGTAG, "JSON error", e);
126             return false;
127         }
128 
129         // If no selected index was found, select the first tab.
130         if (selectedIndex == -1 && sessionTabs.size() > 0) {
131             sessionTabs.getFirst().mIsSelected = true;
132         }
133 
134         for (SessionTab tab : sessionTabs) {
135             onTabRead(tab);
136         }
137 
138         return true;
139     }
140 }
141