@@ -0,0 +1,111 @@
1+module WebExtension.Polyfill where
2+
3+import Prelude
4+import Control.Promise (Promise, toAffE)
5+import Data.Either (Either(..))
6+import Data.Maybe (Maybe(..))
7+import Data.Semigroup.Foldable (intercalateMap)
8+import Effect (Effect)
9+import Effect.Aff (Aff)
10+import Effect.Class.Console as Console
11+import Effect.Uncurried (EffectFn1, EffectFn2, mkEffectFn1, runEffectFn1, runEffectFn2)
12+import Foreign (Foreign, renderForeignError)
13+import Foreign.Object (Object)
14+import Foreign.Object as Object
15+import Prim.Row (class Union)
16+import Yoga.JSON (class ReadForeign, class WriteForeign, read)
17+import Yoga.JSON as JSON
18+import WebExtension.Polyfill.Types (StorageKey(..))
19+
20+foreign import browserImpl ∷ Type
21+
22+-- Unusal API. The keys could also be an array in which case there'd be an object
23+-- with keys for the array of strings passed in originally values. Maybe this is
24+-- for efficiency reasons when using the "sync" API which is apparently kind of
25+-- throttled. Until then, we store keys and values.
26+foreign import data LocalStorage ∷ Type
27+
28+foreign import loadFromLocalStorageImpl ∷ String -> Effect (Promise (Object Foreign))
29+
30+foreign import saveInLocalStorageImpl ∷ Object Foreign -> Effect (Promise Unit)
31+
32+foreign import removeFromLocalStorageImpl ∷ String -> Effect (Promise Unit)
33+
34+loadFromLocalStorage ∷ ∀ a. ReadForeign a => StorageKey -> Aff (Maybe a)
35+loadFromLocalStorage (StorageKey key) = do
36+ Console.info $ "Loading from Local Storage " <> key
37+ result <- toAffE $ loadFromLocalStorageImpl key
38+ pure case Object.lookup key result of
39+ Nothing -> Nothing
40+ Just raw -> JSON.read_ raw
41+
42+saveInLocalStorage ∷ ∀ a. WriteForeign a => StorageKey -> a -> Aff Unit
43+saveInLocalStorage (StorageKey key) value = do
44+ Console.info $ "Saving in Local Storage " <> key <> " : " <> JSON.writeJSON value
45+ saveInLocalStorageImpl (Object.singleton key $ JSON.write value) # toAffE
46+
47+removeFromLocalStorage ∷ StorageKey -> Aff Unit
48+removeFromLocalStorage (StorageKey key) = do
49+ Console.info $ "Removing from Local Storage " <> key
50+ removeFromLocalStorageImpl key # toAffE
51+
52+foreign import data Port ∷ Type
53+
54+foreign import sendMessageViaPortImpl ∷ EffectFn2 Foreign Port Unit
55+
56+sendMessageViaPort ∷ ∀ msg. WriteForeign msg => msg -> Port -> Effect Unit
57+sendMessageViaPort m = runEffectFn2 sendMessageViaPortImpl (JSON.write m)
58+
59+foreign import addOnPortMessageListenerImpl ∷ EffectFn2 (EffectFn1 Foreign Unit) Port Unit
60+
61+addOnPortMessageListener ∷ (Foreign -> Effect Unit) -> Port -> Effect Unit
62+addOnPortMessageListener listener = runEffectFn2 addOnPortMessageListenerImpl (mkEffectFn1 listener)
63+
64+foreign import onContentScriptConnectedImpl ∷ (Port -> Effect Unit) -> Effect Unit
65+
66+onContentScriptConnected ∷ (Port -> Effect Unit) -> Effect Unit
67+onContentScriptConnected = onContentScriptConnectedImpl
68+
69+type ConnectOptions =
70+ (name ∷ String)
71+
72+foreign import connectToBackgroundScriptImpl ∷ ∀ opts. EffectFn1 (Record opts) Port
73+
74+connectToBackgroundScript ∷ ∀ opts missing. Union opts missing ConnectOptions => Record opts -> Effect Port
75+connectToBackgroundScript = runEffectFn1 connectToBackgroundScriptImpl
76+
77+foreign import openNewWindowImpl ∷ ∀ opts. Record opts -> Effect Unit
78+
79+openNewWindow ∷ ∀ opts. Record opts -> Effect Unit
80+openNewWindow = openNewWindowImpl
81+
82+foreign import reload ∷ Effect Unit
83+
84+foreign import data BrowserHistory ∷ Type
85+
86+foreign import history ∷ Effect BrowserHistory
87+
88+foreign import addOnVisitedListenerImpl ∷ (EffectFn1 Foreign Unit) -> BrowserHistory -> Effect Unit
89+
90+addOnVisitedListener
91+ ∷ (HistoryItem -> Effect Unit)
92+ -> BrowserHistory
93+ -> Effect Unit
94+addOnVisitedListener callback = addOnVisitedListenerImpl (mkEffectFn1 rawCallback)
95+ where
96+ rawCallback f = do
97+ let parsed = read f
98+ case parsed of
99+ Left err ->
100+ Console.error
101+ $ "Could not parse history item "
102+ <> intercalateMap "\n" renderForeignError err
103+ Right ok -> callback ok
104+
105+type HistoryItem =
106+ { id ∷ String
107+ , url ∷ Maybe String
108+ , title ∷ Maybe String
109+ , lastVisitTime ∷ Maybe Number -- really an instant
110+ , visitCount ∷ Maybe Int
111+ }