Thursday, May 23, 2019
  • Index
  •  » Code Exchange
  •  » Safari CookieCleaner - full featured white/junk list based cleaner

#1 2018-06-03 09:26:48 pm

Ron_Pinkas
Member
Registered: 2018-06-04
Posts: 2

Safari CookieCleaner - full featured white/junk list based cleaner

Safari-CookieCleaner is a full featured applet allowing automated quick and safe cleanup of Safari's Cookie/Cache/Local Storage/Databases.

The optional Whitelist allows intuitive definition of domains to be protected, supporting patterns:
   starts with
   ends with
   contains
   exact

The optional Junk list (intersected against the Whitelist) allows quick and safe definition of common/frequent privacy threats. Whitelisted domains will be protected even if matched. If the first item of the Junk list is "*" then all EXCEPT the Whitelisted domains will be deleted, but ONLY if a Whitelist is indeed defined.

The applet will make sure to first find, and move to Trash, respective Databases (of matched non whitelisted junk), so as to circumvent the common difficulty of removal of cookies from domains with database.

The applet is a small single script which can be run through Script Editor or saved as an application and run like any other app.

The only required setup is to enable Assistive Accessibility:
(System Preferences->Security & Privacy->Privacy->Accessibility)
to Script Editor (if not enabled yet) and/or to the generated application file, if desired to be run as a stand alone.

The script is maintained and available at: https://github.com/ronpinkas/Safari-CookieCleaner

Your input is very welcome.

Applescript:


(*
Author: Ron Pinkas

You are free to use this code in any way permitable by the GNU General Public License V3.0
*)


on main()
   set debugLog to false
   
   # Item starting with "*" will match domain names ENDING with its subssequent text
   # Item endiding with "*" will match domain names BEGINING with its preceding text
   # Items starting and ending with "*" will match domain names CONTAINING its enclosed text    
   # Otherwise NON GRIDY so use full name INCLUDING .com etc.!
   set whiteList to {"amazon.com", ¬
       "ebay.com", ¬
       "facebook.com", "facebook.net", ¬
       "github.com", "*.githubusercontent.com", ¬
       "google.com", "google-analytics.com", "*.googleapis.com", "googleusercontent.com", "gstatic.com", ¬
       "instagram.com", "cdninstagram.com", ¬
       "netflix.com", ¬
       "paypal.com", ¬
       "twitter.com", "twimg.com", ¬
       "visualstudio.com", ¬
       "whatsapp.*", ¬
       "youtube.com"}
   
   # Items in this list will be fed AS-IS to Safari's cookie search field, which is GRIDY, i.e
   # it uses CONTAINS logic to search for matches - so be careful or better yet
   # protect valuable domains using the above whiteList!
   # IF FIRST Element is "*" then the junkList will be ignored and ALL EXCEPT whiteList will be deleted!
   set junkList to {"*", ".co.", "addthis", "adobe", "affirm", "akc", "allure", "akamai", "azureedge", ¬
       "barron", "beaver", "bam-x", "bing", "bkrtx", "blue", "bold", "boot", "bounce", "btt", "b-cdn", ¬
       "cdn", "chartbeat", "chimp", "click", "cloudflare", "cloudfront", "cnbc", "cohesion", "cookie", "count", "coust", "crazy", "createjs", "criteo", "crsspxl", "crwd", ¬
       "dazz", "demdex", "dental", "desk", "digitech", "disqus", "docashop", ¬
       "exelator", "expose", "ggpht", "facebook", "fbcdn", "google-analytics", "googleadservices", "googletag", "googleusercontent", "gravatar", "gstatic", ¬
       "home", "imgur", "jeeng", "link", "local", "mac", "networks", "newrelic", "omny", "opentok", "optimizer", "parsely", "petametric", "pir.fm", "porn", "quantserve", ¬
       "reddit", "resources", "score", "sekindo", "sstatic", "stack", "taboola", "track", "truspilot", "twimg", "user", "visistat", "ytimg"}
   
   set homePath to (path to home folder)
   set databasesPath to alias ((homePath as text) & "Library:Safari:Databases")
   set indexedDBPath to alias ((databasesPath as text) & "___IndexedDB")
   
   tell application "Safari" to activate
   
   tell application "System Events"
       tell process "Safari"
           set frontmost to true
           
           set myMenu to menu "Safari" of menu bar item "Safari" of menu bar 1
           click menu item "Preferences…" of myMenu
           # WAIT till opened.
           repeat until (exists button "Privacy" of toolbar 1) of window 1
               delay 0.01
           end repeat
           
           set windowPreferences to window 1
           
           tell windowPreferences
               click button "Privacy" of toolbar 1
               # WAIT till opened.
               repeat until (exists button "Manage Website Data…" of group 1 of group 1)
                   delay 0.01
               end repeat
               
               click button "Manage Website Data…" of group 1 of group 1
               # WAIT till opened.
               repeat until (exists table 1 of scroll area 1 of sheet 1)
                   delay 0.01
               end repeat
               
               set myTable to table 1 of scroll area 1 of sheet 1
               set searchField to text field 1 of sheet 1
               
               -- Allow ALL except WHITE list to be considered JUNK by setting FIRST Specifier to "*"!
               try
                   if (count of whiteList) > 0 and first item of junkList is equal to "*" then
                       # will never get here if whiteList is undefined or empty!
                       # Simulate an ALL is JUNK mode - ignoring the rest of junkList.
                       set junkList to {""}
                   end if
               end try
               
               # SEARCH MATCHES for ALL Specifiers of junkList
               repeat with theJunkSpecifier in junkList
                   if theJunkSpecifier is equal to "" and theJunkSpecifier is not equal to first item of junkList then
                       display notification "Invalid junk specifier \"\""
                       set theJunkSpecifier to "*empty specifier ignored*"
                   end if
                   
                   select searchField
                   set value of searchField to theJunkSpecifier
                   
                   -- WAIT UNTIL Search finalizes, with either some rows OR "No Saved Website Data" is displayed!
                   set prospectiveMatches to true
                   repeat while prospectiveMatches
                       delay 0.01
                       
                       if (count of (rows of myTable)) > 0 then
                           --display notification "Found prospective junk for: '" & theJunkSpecifier & "'"
                           exit repeat
                       else
                           try
                               set uiChildren to entire contents of sheet 1
                               
                               repeat with theUIChild in uiChildren
                                   if class of theUIChild is static text and value of theUIChild contains "No Saved Website Data" then
                                       set prospectiveMatches to false
                                       exit repeat
                                   end if
                               end repeat
                           end try
                       end if
                   end repeat
                   -- END WAIT (repeat while prospectiveMatches                    
                   
                   set prospectiveJunkRows to count of (rows of myTable)
                   set whiteListed to false
                   set rowSite to ""
                   set rowIndex to 1
                   
                   #Reset Scroll bar to TOP, incase Windos was alreasdy opened, and scrolled by User
                   set value of scroll bar 1 of scroll area 1 of sheet 1 to 0
                   
                   # PROCESS ALL Lines MATCHING the junk specifier
                   repeat while prospectiveJunkRows ≥ rowIndex
                       try
                           set selectedRow to row rowIndex of myTable
                           select selectedRow
                           set rowSite to description of first UI element of selectedRow
                           set domainName to first item of my textTokens(rowSite, space)
                           
                           if debugLog then
                               log "-------- TOP"
                               log "Cookie: '" & rowSite & "'"
                               log "Domain: '" & domainName & "'"
                               log "rowIndex: " & rowIndex as text
                               log "prospectiveJunkRows: " & prospectiveJunkRows as text
                               log "count of rows: " & (count of (rows of myTable)) as text
                           end if
                           
                           -- AppleScript indexing is 1 based BUT item 0 maps to 1 so it can be confusing!
                           -- SCROLL so selected row (as well as at least the prior and next rows) are in the VISIBLE Scroll port.
                           repeat while (count of value of attribute "AXVisibleChildren" of row (my min(rowIndex + 1, prospectiveJunkRows)) of myTable) = 0
                               --tell scroll area 1 of sheet 1 to perform action "AXScrollUpByPage"
                               click button 1 of scroll bar 1 of scroll area 1 of sheet 1
                               delay 0.01
                           end repeat
                           repeat while (count of value of attribute "AXVisibleChildren" of row (my max(rowIndex - 1, 1)) of myTable) = 0
                               --tell scroll area 1 of sheet 1 to perform action "AXScrollDownByPage"
                               click button 2 of scroll bar 1 of scroll area 1 of sheet 1
                               delay 0.01
                           end repeat
                           -- END SCROLL
                           
                           -- SEARCH whiteList
                           set whiteListed to false
                           repeat with whiteSite in whiteList
                               --log "White: '" & whiteSite & "'"
                               
                               # whiteSite is a REFERENCE which must be DEreferenced to be correctly compared!
                               --log "Equals: " & (domainName is equal to whiteSite) as text
                               --log "Equals: " & (domainName is equal to whiteSite as text) as text
                               
                               # INTENTIONALLY not comparing equality when "*" found!
                               if first character of whiteSite = "*" and last character of whiteSite = "*" then
                                   if domainName contains (text 2 through -2 of whiteSite) then
                                       log "Contains: " & (text 2 through -2 of whiteSite)
                                       set whiteListed to true
                                       exit repeat
                                   end if
                               else if first character of whiteSite = "*" then
                                   if domainName ends with text 2 through -1 of whiteSite then
                                       log "Ends with: " & (text 2 through -1 of whiteSite)
                                       set whiteListed to true
                                       exit repeat
                                   end if
                               else if last character of whiteSite = "*" then
                                   if domainName begins with text 1 through -2 of whiteSite then
                                       log "Begins with: " & (text 1 through -2 of whiteSite)
                                       set whiteListed to true
                                       exit repeat
                                   end if
                               else if domainName is equal to whiteSite as text then
                                   log "Equals to: " & whiteSite
                                   set whiteListed to true
                                   exit repeat
                               end if
                           end repeat
                           -- END SEARCH whiteList
                           
                           if whiteListed then
                               log "White: " & rowSite
                               set rowIndex to rowIndex + 1
                           else
                               log "Delete: " & rowSite
                               
                               # DELETE DATABASE first if present.
                               if rowSite contains "Databases" then
                                   -- FIND & DELETE DATABASE file
                                   log domainName & ": Has Databases!"
                                   
                                   # Limiting GRIDINESS of conatins by prefixing a "." and "_"                                    
                                   set myFiles to (files of application "System Events"'s folder (databasesPath as text) where (name contains ("." & domainName)) or name contains ("_" & domainName))
                                   set myFiles to myFiles & (folders of application "System Events"'s folder (indexedDBPath as text) where (name contains ("." & domainName)) or name contains ("_" & domainName))
                                   
                                   repeat with theFile in myFiles
                                       log "Trash: " & name of theFile
                                       move theFile to application "System Events"'s trash
                                   end repeat
                                   -- END FIND & DELETE
                               end if
                               -- END DELETE DATABASE
                               
                               # DELETE COOCKIE!
                               set repeatCount to 0
                               repeat until (repeatCount > 10) or (prospectiveJunkRows > (count of (rows of myTable)))
                                   set repeatCount to repeatCount + 1
                                   
                                   # Because row may be last row and might have been deleted just after above <until...> was evaluated.
                                   try
                                       # Row must be SELECTED (even if was) to enable the Remove button!
                                       repeat until selected of selectedRow
                                           select selectedRow
                                           delay 0.01
                                       end repeat
                                       click button "Remove" of sheet 1
                                       delay 0.1
                                   end try
                               end repeat
                               
                               if prospectiveJunkRows = (count of (rows of myTable)) then
                                   log "Failed deleting: " & rowSite
                               end if
                               -- END DELETE COOCKIE
                           end if
                       on error errorMessage number errorNumber from f to t partial result p
                           --log "*** ERROR! " & errorMessage
                           error errorMessage number errorNumber from f to t partial result p
                       end try
                       
                       # Here instead of just after DELETE because it might also change outside of our control!
                       set prospectiveJunkRows to count of (rows of myTable)
                       
                       if debugLog then
                           log "-------- BOTTOM"
                           log "Cookie: '" & rowSite & "'"
                           log "Domain: '" & domainName & "'"
                           log "rowIndex: " & rowIndex as text
                           log "prospectiveJunkRows: " & prospectiveJunkRows as text
                           log "count of rows: " & (count of (rows of myTable)) as text
                       end if
                   end repeat #while prospectiveJunkRows > rowIndex    
                   -- END PROCESS ALL Lines MATCHING
               end repeat #with theJunkSpecifier in junkList
               -- ENND SEARCH
               
               click button "Done" of sheet 1
               
               # Don't like this style.
               --keystroke "w" using command down
               
               # This is hard coded but works
               --click button 1
               
               # WARNING: this line will FAIL upon removal of the () around <butons where...>
               # because the <where ...> clause will be wrongly applied the implied
               # <of windowPreferences> target instead of its <buttons> collection!    
               click button 1 of (buttons where role description = "close button")
           end tell #windowPreferences
       end tell
   end tell
end main

on textTokens(textToParse, parseDelimiters)
   set listTokens to missing value
   set asTID to AppleScript's text item delimiters
   
   try
       set AppleScript's text item delimiters to parseDelimiters
       set listTokens to text items of textToParse
   end try
   
   set AppleScript's text item delimiters to asTID
   return listTokens
end textTokens

on min(n1, n2)
   if n1 ≤ n2 then
       return n1
   end if
   
   return n2
end min

on max(n1, n2)
   if n1 ≥ n2 then
       return n1
   end if
   
   return n2
end max

on run {}
   main()
end run

* Revised to reflect 2 suggestions by Nigel.
* Revised 2 <repeat while not ...> to <repeat until ...>

Last edited by Ron_Pinkas (2018-06-05 08:42:35 am)


Filed under: GUI, safari, Cleaner, cookie

Offline

 

#2 2018-06-04 03:01:55 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4869

Re: Safari CookieCleaner - full featured white/junk list based cleaner

Hi Ron.

Welcome to MacScripter and thanks for posting your code. I haven't got round to trying it yet, but just looking at the beginning of the System Events block:

Applescript:

tell application "System Events"
   tell process "Safari"
       activate
       set frontmost to true
       
       set myMenu to menu "Safari" of menu bar item "Safari" of menu bar 1
       click menu item "Preferences…" of myMenu
       
       repeat while not (exists window 1)
           delay 0.01
       end repeat
       set windowPreferences to window 1
       
       -- etc. …
   end tell
end tell



'activate' is an application command, so here it's being applied to the application "System Events", not to the process "Safari". If you want to activate Safari, the command must go to the Safari application.

Repeating while window 1 doesn't exist assumes that Safari has no windows open at all when the "Preferences…" menu item's clicked. It would be safer to count the windows first, then click the menu item and wait until the number of windows is more than it was:

Applescript:

tell application "Safari" to activate

tell application "System Events"
   tell process "Safari"
       set frontmost to true
       
       set windowCount to (count windows)
       
       set myMenu to menu "Safari" of menu bar item "Safari" of menu bar 1
       click menu item "Preferences…" of myMenu
       
       repeat until ((count windows) > windowCount)
           delay 0.01
       end repeat
       set windowPreferences to window 1
   end tell
   
   -- etc. …
end tell


NG

Offline

 

#3 2018-06-04 08:15:42 am

Ron_Pinkas
Member
Registered: 2018-06-04
Posts: 2

Re: Safari CookieCleaner - full featured white/junk list based cleaner

Hi Nigel,

Many thanks indeed.

Strangely somehow in all the samples I saw, Safari was listed only as a process rather than an application, and I was myself curious about the distinction. I somehow missed the fact that process and application are not mutually exclusive.

Right after posting I did notice the weakness of <while not exists window 1> but decided to postpone the revision to today. I was wondering if someone will notice before I got to it. smile

But, your suggested correction also assumes that the preferncesWindow is not already open smile

The correct fix is:

Applescript:


           set myMenu to menu "Safari" of menu bar item "Safari" of menu bar 1
           click menu item "Preferences…" of myMenu
           # WAIT till opened.
           repeat until (exists button "Privacy" of toolbar 1) of window 1
               delay 0.01
           end repeat

And while at it, I of course corrected the equally important omission of "macsripter.net" from whiteList. smile

Thanks again.

Ron

Last edited by Ron_Pinkas (2018-06-04 01:16:45 pm)

Offline

 

#4 2019-03-05 03:17:53 am

KniazidisR
Member
Registered: 2019-03-03
Posts: 126

Re: Safari CookieCleaner - full featured white/junk list based cleaner

Hi, Nigel.

Thanks for your post. This script is amazing, but not complete - it won't work for Safari menu items on other languages.

For example on greek Safari "Privacy" is "Απόρρητο", "Preferences" is "Προτιμήσεις…", "Manage Website Data…" is "Διαχείριση δεδομένων ιστότοπων…" and so on. After replacing english with greek terms script works fine:)

Maybe exists any way for getting Safari menu item's names directly from current user's Safari (Not hardcoding names)?

Last edited by KniazidisR (2019-03-05 03:34:57 am)


macOS Mojave -- version 10.14.4
Safari -- version 12.1

Offline

 

#5 2019-03-06 05:28:00 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4869

Re: Safari CookieCleaner - full featured white/junk list based cleaner

KniazidisR wrote:

Maybe exists any way for getting Safari menu item's names directly from current user's Safari (Not hardcoding names)?


This is the best I can do so far, opening the sheet without knowing the menu item and button names. I don't know how well it works with non-English localisations:

Applescript:

-- Open the "Manage Website Data" sheet in Safari's Preferences window.

-- This has only been tested in English in Mojave and assumes:
-- • That no one would be stupid enough to run it with anything other than Safari's documents or Preferences windows open.
-- • That the first item with an associated keystroke in the application menu is "Preferences…" in the local language.
-- • That the "Privacy" button is the seventh in the Preferences window toolbar.
-- • That the "Privacy" pane has seven UI elements in group 1 of group 1 and that it's the only pane with this many in the group.
-- • That the first named button in the group is "Manage Website Data…".

tell application "Safari"
   activate
   set windowCount to (count windows) -- Safari only counts its document windows
end tell

tell application "System Events"
   tell process "Safari"
       set frontmost to true
       
       -- Click the first menu item in the "Safari" menu which has a keyboard shortcut. This is hopefully "Preferences…" in the local language.
       set myMenu to menu 1 of menu bar item 2 of menu bar 1
       click (first menu item of myMenu where class of value of attribute "AXMenuItemCmdChar" is not missing value)
       
       -- Delay until the number of Safari windows is more than the number of its document windows.
       -- This assumes that that the only non-document window likely to be open at the time is the Preferences window itself.
       repeat until ((count windows) > windowCount)
           delay 0.1
       end repeat
       -- Use a 'tell' statement with an index reference to the window, because setting a variable would result in a name reference — and the window's name may change when the button's clicked.
       tell window 1
           -- Cllck the seventh button in the window's toolbar, delay until there are seven UI elements in group 1 of group 1 (only true for the "Privacy" pane), then click the first named button in that group.
           click button 7 of toolbar 1
           tell group 1 of group 1
               repeat until ((count UI elements) is 7)
                   delay 0.1
               end repeat
               click button (first text of (get name of buttons))
           end tell
           
           -- Delay until the drop-down sheet appears
           repeat until (sheet 1 exists)
               delay 0.1
           end repeat
           -- Then delay until the "Loading Website Data…" message disappears.
           repeat while ((count sheet 1's static texts) > 1)
               delay 0.5
           end repeat
           
           set myTable to table 1 of scroll area 1 of sheet 1
           set searchField to text field 1 of sheet 1
           
       end tell
   end tell
   
   -- etc. …
end tell

Last edited by Nigel Garvey (2019-03-06 07:33:07 am)


NG

Offline

 

#6 2019-03-09 01:25:38 am

KniazidisR
Member
Registered: 2019-03-03
Posts: 126

Re: Safari CookieCleaner - full featured white/junk list based cleaner

Hi, Nigel. A tried your solution, but it throw error and script failes. One bug found: if in cookies-window exists less than 7 sites, scroll area don't exists. Exists only static list without scroll. So, referring to scroll area in such case throws the next error (other type error, not with localization):

error "System Events got an error: Can’t get scroll bar 1 of scroll area 1 of sheet 1 of window \"Privacy\" of application process \"Safari\". Invalid index." number -1719 from scroll bar 1 of scroll area 1 of sheet 1 of window "Privacy" of application process "Safari"

Now, I found nice solution for clicking buttons without hardcoding names and script below works, (with problem,descripted above). I add one more handler also for timing. My changes has BOLD chars. now I am looking for replace 2 hardcode names yet (with BOLD too, "Δεν υπάρχουν αποθηκευμένα δεδομένα ιστοτόπων and  "Βάσεις δεδομένων". Sorry for my English

(*
Author: Ron Pinkas (edited by KniazidisR)

You are free to use this code in any way permitable by the GNU General Public License V3.0
*)

on main()
    set debugLog to false
   
    # Item starting with "*" will match domain names ENDING with its subssequent text
    # Item endiding with "*" will match domain names BEGINING with its preceding text
    # Items starting and ending with "*" will match domain names CONTAINING its enclosed text   
    # Otherwise NON GRIDY so use full name INCLUDING .com etc.!
    set whiteList to {"amazon.com", ¬
        "ebay.com", ¬
        "facebook.com", "facebook.net", ¬
        "github.com", "*.githubusercontent.com", ¬
        "google.com", "google-analytics.com", "*.googleapis.com", "googleusercontent.com", "gstatic.com", ¬
        "instagram.com", "cdninstagram.com", ¬
        "netflix.com", ¬
        "paypal.com", ¬
        "twitter.com", "twimg.com", ¬
        "visualstudio.com", ¬
        "whatsapp.*", ¬
        "youtube.com"}
   
    # Items in this list will be fed AS-IS to Safari's cookie search field, which is GRIDY, i.e
    # it uses CONTAINS logic to search for matches - so be careful or better yet
    # protect valuable domains using the above whiteList!
    # IF FIRST Element is "*" then the junkList will be ignored and ALL EXCEPT whiteList will be deleted!
    set junkList to {"*", "adroll.com", ".co.", "addthis", "adobe", "affirm", "akc", "allure", "akamai", "azureedge", ¬
        "barron", "beaver", "bam-x", "bing", "bkrtx", "blue", "bold", "boot", "bounce", "btt", "b-cdn", ¬
        "cdn", "chartbeat", "chimp", "click", "cloudflare", "cloudfront", "cnbc", "cohesion", "cookie", "count", "coust", "crazy", "createjs", "criteo", "crsspxl", "crwd", ¬
        "dazz", "demdex", "dental", "desk", "digitech", "disqus", "docashop", ¬
        "exelator", "expose", "ggpht", "facebook", "fbcdn", "google-analytics", "googleadservices", "googletag", "googleusercontent", "gravatar", "gstatic", ¬
        "home", "imgur", "jeeng", "link", "local", "mac", "networks", "newrelic", "omny", "opentok", "optimizer", "parsely", "petametric", "pir.fm", "porn", "quantserve", ¬
        "reddit", "resources", "score", "sekindo", "sstatic", "stack", "taboola", "track", "truspilot", "twimg", "user", "visistat", "ytimg"}
   
    set homePath to (path to home folder)
    set databasesPath to alias ((homePath as text) & "Library:Safari:Databases")
    set indexedDBPath to alias ((databasesPath as text) & "___IndexedDB")
    set timeoutSeconds to 2.0
   
    -- Open application "Safari" via Dock
    set uiScript to "click UI Element \"Safari\" of list 1 of application process \"Dock\""
    my doWithTimeout(uiScript, timeoutSeconds)
   
    -- Click menu item "Safari" of Safari menu bar
    set uiScript to "click menu item 4 of menu 1 of menu bar item \"Safari\" of menu bar 1 of application process \"Safari\""
    my doWithTimeout(uiScript, timeoutSeconds)
   
    -- Click menu item "Preferences" of menu item "Safari"
    set uiScript to "click menu item 4 of menu 1 of menu bar item \"Safari\" of menu bar 1 of application process \"Safari\""
    my doWithTimeout(uiScript, timeoutSeconds)
   
    -- Click UI element "Privacy"
    set uiScript to "click UI Element 7 of tool bar 1 of window 1 of application process \"Safari\""
    my doWithTimeout(uiScript, timeoutSeconds)
   
    -- Click button "Manage Website Data…"
    set uiScript to "click UI Element 8 of group 1 of group 1 of window 1 of application process \"Safari\""
    my doWithTimeout(uiScript, timeoutSeconds)

   
   
    tell application "System Events"
        tell process "Safari"
            set myMenu to menu "Safari" of menu bar item "Safari" of menu bar 1
            set windowPreferences to window 1
            tell windowPreferences
               
                repeat until (exists table 1 of scroll area 1 of sheet 1)
                    delay 0.01
                end repeat
               
                set myTable to table 1 of scroll area 1 of sheet 1
                set searchField to text field 1 of sheet 1
               
               
                -- Allow ALL except WHITE list to be considered JUNK by setting FIRST Specifier to "*"!
                try
                    if (count of whiteList) > 0 and first item of junkList is equal to "*" then
                        # will never get here if whiteList is undefined or empty!
                        # Simulate an ALL is JUNK mode - ignoring the rest of junkList.
                        set junkList to {""}
                    end if
                end try
               
                # SEARCH MATCHES for ALL Specifiers of junkList
                repeat with theJunkSpecifier in junkList
                    if theJunkSpecifier is equal to "" and theJunkSpecifier is not equal to first item of junkList then
                        display notification "Invalid junk specifier \"\""
                        set theJunkSpecifier to "*empty specifier ignored*"
                    end if
                   
                    select searchField
                    set value of searchField to theJunkSpecifier
                   
                    -- WAIT UNTIL Search finalizes, with either some rows OR "No Saved Website Data" is displayed!
                    set prospectiveMatches to true
                    repeat while prospectiveMatches
                        delay 0.01
                       
                        if (count of (rows of myTable)) > 0 then
                            --display notification "Found prospective junk for: '" & theJunkSpecifier & "'"
                            exit repeat
                        else
                            try
                                set uiChildren to entire contents of sheet 1
                               
                                repeat with theUIChild in uiChildren
                                    if class of theUIChild is static text and value of theUIChild contains "Δεν υπάρχουν αποθηκευμένα δεδομένα ιστοτόπων" then
                                        set prospectiveMatches to false
                                        exit repeat
                                    end if
                                end repeat
                            end try
                        end if
                    end repeat
                    -- END WAIT (repeat while prospectiveMatches                   
                   
                    set prospectiveJunkRows to count of (rows of myTable)
                    set whiteListed to false
                    set rowSite to ""
                    set rowIndex to 1
                   
                    #Reset Scroll bar to TOP, incase Windos was alreasdy opened, and scrolled by User
                    set value of scroll bar 1 of scroll area 1 of sheet 1 to 0
                   
                    # PROCESS ALL Lines MATCHING the junk specifier
                    repeat while prospectiveJunkRows ≥ rowIndex
                        try
                            set selectedRow to row rowIndex of myTable
                            select selectedRow
                            set rowSite to description of first UI element of selectedRow
                            set domainName to first item of my textTokens(rowSite, space)
                           
                            if debugLog then
                                log "-------- TOP"
                                log "Cookie: '" & rowSite & "'"
                                log "Domain: '" & domainName & "'"
                                log "rowIndex: " & rowIndex as text
                                log "prospectiveJunkRows: " & prospectiveJunkRows as text
                                log "count of rows: " & (count of (rows of myTable)) as text
                            end if
                           
                            -- AppleScript indexing is 1 based BUT item 0 maps to 1 so it can be confusing!
                            -- SCROLL so selected row (as well as at least the prior and next rows) are in the VISIBLE Scroll port.
                            repeat while (count of value of attribute "AXVisibleChildren" of row (my min(rowIndex + 1, prospectiveJunkRows)) of myTable) = 0
                                --tell scroll area 1 of sheet 1 to perform action "AXScrollUpByPage"
                                click button 1 of scroll bar 1 of scroll area 1 of sheet 1
                                delay 0.01
                            end repeat
                            repeat while (count of value of attribute "AXVisibleChildren" of row (my max(rowIndex - 1, 1)) of myTable) = 0
                                --tell scroll area 1 of sheet 1 to perform action "AXScrollDownByPage"
                                click button 2 of scroll bar 1 of scroll area 1 of sheet 1
                                delay 0.01
                            end repeat
                            -- END SCROLL
                           
                            -- SEARCH whiteList
                            set whiteListed to false
                            repeat with whiteSite in whiteList
                                --log "White: '" & whiteSite & "'"
                               
                                # whiteSite is a REFERENCE which must be DEreferenced to be correctly compared!
                                --log "Equals: " & (domainName is equal to whiteSite) as text
                                --log "Equals: " & (domainName is equal to whiteSite as text) as text
                               
                                # INTENTIONALLY not comparing equality when "*" found!
                                if first character of whiteSite = "*" and last character of whiteSite = "*" then
                                    if domainName contains (text 2 through -2 of whiteSite) then
                                        log "Contains: " & (text 2 through -2 of whiteSite)
                                        set whiteListed to true
                                        exit repeat
                                    end if
                                else if first character of whiteSite = "*" then
                                    if domainName ends with text 2 through -1 of whiteSite then
                                        log "Ends with: " & (text 2 through -1 of whiteSite)
                                        set whiteListed to true
                                        exit repeat
                                    end if
                                else if last character of whiteSite = "*" then
                                    if domainName begins with text 1 through -2 of whiteSite then
                                        log "Begins with: " & (text 1 through -2 of whiteSite)
                                        set whiteListed to true
                                        exit repeat
                                    end if
                                else if domainName is equal to whiteSite as text then
                                    log "Equals to: " & whiteSite
                                    set whiteListed to true
                                    exit repeat
                                end if
                            end repeat
                            -- END SEARCH whiteList
                           
                            if whiteListed then
                                log "White: " & rowSite
                                set rowIndex to rowIndex + 1
                            else
                                log "Delete: " & rowSite
                               
                                # DELETE DATABASE first if present.
                                if rowSite contains "Βάσεις δεδομένων" then
                                    -- FIND & DELETE DATABASE file
                                    log domainName & ": Has Databases!"
                                   
                                    # Limiting GRIDINESS of conatins by prefixing a "." and "_"                                   
                                    set myFiles to (files of application "System Events"'s folder (databasesPath as text) where (name contains ("." & domainName)) or name contains ("_" & domainName))
                                    set myFiles to myFiles & (folders of application "System Events"'s folder (indexedDBPath as text) where (name contains ("." & domainName)) or name contains ("_" & domainName))
                                   
                                    repeat with theFile in myFiles
                                        log "Trash: " & name of theFile
                                        move theFile to application "System Events"'s trash
                                    end repeat
                                    -- END FIND & DELETE
                                end if
                                -- END DELETE DATABASE
                               
                                # DELETE COOCKIE!
                                set repeatCount to 0
                                repeat until (repeatCount > 10) or (prospectiveJunkRows > (count of (rows of myTable)))
                                    set repeatCount to repeatCount + 1
                                   
                                    # Because row may be last row and might have been deleted just after above <until...> was evaluated.
                                    try
                                        # Row must be SELECTED (even if was) to enable the Remove button!
                                        repeat until selected of selectedRow
                                            select selectedRow
                                            delay 0.01
                                        end repeat
                                                                               
                                                                                -- Click button "Remove"
                                        set uiScript to "click UI Element 1 of sheet 1                of window 1 of application process \"Safari\""
                                                                                my doWithTimeout(uiScript, timeoutSeconds)

                                                                                end try
                                end repeat
                               
                                if prospectiveJunkRows = (count of (rows of myTable)) then
                                    log "Failed deleting: " & rowSite
                                end if
                                -- END DELETE COOCKIE
                            end if
                        on error errorMessage number errorNumber from f to t partial result p
                            --log "*** ERROR! " & errorMessage
                            error errorMessage number errorNumber from f to t partial result p
                        end try
                       
                        # Here instead of just after DELETE because it might also change outside of our control!
                        set prospectiveJunkRows to count of (rows of myTable)
                       
                        if debugLog then
                            log "-------- BOTTOM"
                            log "Cookie: '" & rowSite & "'"
                            log "Domain: '" & domainName & "'"
                            log "rowIndex: " & rowIndex as text
                            log "prospectiveJunkRows: " & prospectiveJunkRows as text
                            log "count of rows: " & (count of (rows of myTable)) as text
                        end if
                    end repeat #while prospectiveJunkRows > rowIndex   
                    -- END PROCESS ALL Lines MATCHING
                end repeat #with theJunkSpecifier in junkList
                -- ENND SEARCH
               
                                -- Click button "Done"
                set uiScript to "click UI Element 3 of sheet 1 of window 1 of application process \"Safari\""
    my doWithTimeout(uiScript, timeoutSeconds)

               
               
                # Don't like this style.
                --keystroke "w" using command down
               
                # This is hard coded but works
                click button 1 of (buttons where role description = "close button")
               
                # WARNING: this line will FAIL upon removal of the () around <butons where...>
                # because the <where ...> clause will be wrongly applied the implied
                # <of windowPreferences> target instead of its <buttons> collection!   
                --click button 1 of (buttons where role description = "close button")
            end tell #windowPreferences
        end tell
    end tell
   
   
end main

on textTokens(textToParse, parseDelimiters)
    set listTokens to missing value
    set asTID to AppleScript's text item delimiters
   
    try
        set AppleScript's text item delimiters to parseDelimiters
        set listTokens to text items of textToParse
    end try
   
    set AppleScript's text item delimiters to asTID
    return listTokens
end textTokens

on min(n1, n2)
    if n1 ≤ n2 then
        return n1
    end if
   
    return n2
end min

on max(n1, n2)
    if n1 ≥ n2 then
        return n1
    end if
   
    return n2
end max

on run {}
    main()
end run

on doWithTimeout(uiScript, timeoutSeconds)
    set endDate to (current date) + timeoutSeconds
    repeat
        try
            run script "tell application \"System Events\"
" & uiScript & "
end tell"
            exit repeat
        on error errorMessage
            if ((current date) > endDate) then
                error "Can not " & uiScript
            end if
        end try
    end repeat
end doWithTimeout

Last edited by KniazidisR (2019-03-09 03:53:00 am)


macOS Mojave -- version 10.14.4
Safari -- version 12.1

Offline

 

#7 2019-03-10 04:13:01 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4869

Re: Safari CookieCleaner - full featured white/junk list based cleaner

"KniazidisR wrote:

A tried your solution, but it throw error and script failes. One bug found: if in cookies-window exists less than 7 sites, scroll area don't exists. Exists only static list without scroll. So, referring to scroll area in such case throws the next error


Hi KniazidisR.

Just to clear up any misunderstanding, the script in post #1 was written and posted by Ron_Pinkas, not by me.

My contributions in posts #2 and #5 are just different code for opening the Privacy pane in Safari's Preferences window. They don't themselves delete any cookies or caches.

I've not been able to reproduce the error you describe. If there are fewer than seven entries in the scroll area, the scroll area still exists; it just doesn't have a scroll bar. However, I did find a problem yesterday whereby if there were no entries at all, the script would wait forever for the text saying there were no cookies to be replaced with the cookie list! I've now modified the code in post #5 above to get round this.

I also found that in Safari 11.1.2 on my El Capitan machine, the Privacy pane has a different number of UI elements from that in Safari 12.0.3 on my Mojave one. This caused an infinite repeat when I ran the script on the El Capitan machine. I've now made allowances for this too in post #5.

The code has been tested and works in English, French, and Greek environments on my Mojave system and in an English environment on my El Capitan machine.

As I said above, my code doesn't go as far as deleting any cookies or caches. In fact I've never tried running Ron's deletion code. But reading through it yesterday, I noticed that it searches for and deletes certain Safari databases from the disk. In Mojave, the folders containing these databases — and all the container folders in the chain up to and including the "Safari" folder in the user's "Library" folder — are unsearchable by System Events. It just returns an empty list for each search. ('list folder' also returns an empty list and the "find" shell script complains that the operation isn't permitted. The Finder, though, has no problem returning the folders' contents!  hmm )  System Events can access the folders and the databases if it's given the exact paths to them, but it can't find them for itself. So these two lines are unlikely to work in Mojave:

Applescript:

set myFiles to (files of application "System Events"'s folder (databasesPath as text) where (name contains ("." & domainName)) or name contains ("_" & domainName))
set myFiles to myFiles & (folders of application "System Events"'s folder (indexedDBPath as text) where (name contains ("." & domainName)) or name contains ("_" & domainName))

I may look into this further later on.

Edit: References to "post #4" corrected to "post #5"!

Last edited by Nigel Garvey (2019-03-12 04:01:59 am)


NG

Offline

 

#8 2019-03-11 12:39:09 pm

KniazidisR
Member
Registered: 2019-03-03
Posts: 126

Re: Safari CookieCleaner - full featured white/junk list based cleaner

Nigel Garvey wrote:



I've not been able to reproduce the error you describe. If there are fewer than seven entries in the scroll area, the scroll area still exists; it just doesn't have a scroll bar. However, I did find a problem yesterday whereby if there were no entries at all, the script would wait forever for the text saying there were no cookies to be replaced with the cookie list! I've now modified the code in post #4 above to get round this.

I also found that in Safari 11.1.2 on my El Capitan machine, the Privacy pane has a different number of UI elements from that in Safari 12.0.3 on my Mojave one. This caused an infinite repeat when I ran the script on the El Capitan machine. I've now made allowances for this too in post #4.

The code has been tested and works in English, French, and Greek environments on my Mojave system and in an English environment on my El Capitan machine.



Нi, Nigel. Thanks for your reply post.

Exactly, as I can understand, problematic is code lines, which refers to scroll bar 1 (If there are fewer than seven entries in the scroll area, it doesn't have a scroll bar). So, referring to scroll bar 1 is referring to object, which doesn't exist in that case, and throws error. I have Greek Mac OS X EI Captain.

Your latest solution for databases deletion is interesting and I can try later and reply later, as I am busy for now.

P.S. Founded 1 another problem. This code snippet doesn't work stable and throws sometimes error:

tell application "System Events"
        tell process "Safari"
            set myMenu to menu "Safari" of menu bar item "Safari" of menu bar 1
            click button 1 of (buttons where role description = "close button")
        end tell
end tell


Need replace it with this snippet, which is tested and works stable:

tell application "System Events"
        tell process "Safari"
            set myMenu to menu "Safari" of menu bar item "Safari" of menu bar 1
            end tell
end tell
set uiScript to "click UI Element 2 of window 1 of application process \"Safari\""
my doWithTimeout(uiScript, timeoutSeconds)

Last edited by KniazidisR (2019-03-11 02:29:46 pm)


macOS Mojave -- version 10.14.4
Safari -- version 12.1

Offline

 

#9 2019-03-11 11:57:57 pm

KniazidisR
Member
Registered: 2019-03-03
Posts: 126

Re: Safari CookieCleaner - full featured white/junk list based cleaner

So, as I said earlier, a reference to a nonexistent scrollbar raises an error on my machine. The error is raised when there are less than seven entries in the site list window. This happens specifically in this line of code:

#Reset Scroll bar to TOP, incase Windos was alreasdy opened, and scrolled by User
                    set value of scroll bar 1 of scroll area 1 of sheet 1 to 0

As you can see from the comment, this line of code is intended to scroll the list of sites to the top position. But! When the list of sites is less than seven, he is already at the top. This fact can be and should be used.

Simply replacing the unstable line of code with the next one solves this problem with scrolling the window completely:

#Reset Scroll bar to TOP, incase Windos was alreasdy opened, and scrolled by User
                    if prospectiveJunkRows > 6 then set value of scroll bar 1 of scroll area 1 of sheet 1 to 0

Last edited by KniazidisR (2019-03-12 12:10:52 am)


macOS Mojave -- version 10.14.4
Safari -- version 12.1

Offline

 

#10 2019-03-12 04:43:06 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4869

Re: Safari CookieCleaner - full featured white/junk list based cleaner

KniazidisR wrote:

So, as I said earlier, a reference to a nonexistent scrollbar raises an error on my machine.


This explains why I couldn't reproduce the error. What you actually reported was:

Hi, Nigel. A tried your solution, but it throw error and script failes. One bug found: if in cookies-window exists less than 7 sites, scroll area don't exists. Exists only static list without scroll. So, referring to scroll area in such case throws the next error


1. The scroll area does exist, regardless of the number of cookie sites listed in it.
2. My code contains no mention of a scroll bar. As I said earlier, my scripts above are just alternative ways to open the cookie window — the one in post #5 doing so without relying on the UI being in English. I've only ever glanced cursorily at Ron's code for deleting the cookies and caches and have never tried running it. This is principally because I'd want to be absolutely sure it was safe before trying it on my own computer! This in turn would require extensive testing in a user other than the main one on the machine — and I've not yet found time to do this. The scroll bar problem should be easy to cure in the first instance, but there are two further mentions of "scroll bar" later in the code and I'd need to check their significance before offering a solution.


NG

Offline

 

#11 2019-03-12 02:48:07 pm

KniazidisR
Member
Registered: 2019-03-03
Posts: 126

Re: Safari CookieCleaner - full featured white/junk list based cleaner

Greetings to all.

One of my last responses about this script. It concerns how to improve the script significantly.

How to increase the speed of the script several times, and, at the same time, avoid the use of hard coding of headers like "Databases"?

In its original form, the script compares with white/black lists in the repetition cycle all the records of the site window without diferency, which makes its work slow. What can be done? The answer is taking into account what kind of data the sites contribute when they visit. So. There are 4 types of input data:

1) Cash
2) Cash, Cookies
3) Cash, Cookies, Local storage
4) Cash, Cookies, Local storage, Databases

90% or more of the sites contribute data type 1). But, just a minute. we are not interested in clearing Cash, but cleaning Cookies, Local storage, Databases! Type 1 sites do not need to be processed in a repetition loop for comparison with the “white” and “black” list. Their script should just leave. This would make the script 10-20 times faster.

Now, how to avoid hard coding of headings in buttons and windows, working in English Mac OS and helpless with non-English systems?

Above, I have already shown how to fix it with buttons. And what can be done with the header type "Database"? The answer is simple: do not use it - everything that does not fall under cases 1) and 2) is Local storage and Databases, which need to be cleaned. Checking whether the site leaves data of type 1) or 2) is simple, since it does not differ on computers with different languages and is always written only in English (since these 2 words are a pure English invention). The other 2 words (Local storage and Databases) can not be hard-coded into conditional sentences, since they change from localization to localization.

The solution is simple - use a conditional sentence: all that is not of type 1), 2) is of type 3), 4). And what is of type 1) and 2) you can define and can hardcord. Other way is comparing with template "Cash, Cookies, *, *"

You will not force the user to abandon its localization. It will simply send your script to the trash.

My wishes are better read the author of the script to redo it for getting it really shone.

Last edited by KniazidisR (2019-03-12 03:17:42 pm)


macOS Mojave -- version 10.14.4
Safari -- version 12.1

Offline

 

#12 2019-03-25 06:42:08 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4869

Re: Safari CookieCleaner - full featured white/junk list based cleaner

As KniazidisR has observed above, Ron's script relies on Safari's GUI being in English.

I myself have found that in Mojave, the folders containing the site databases on disk have restricted access and can't be discovered or searched with System Events, list folder, ls, find, or NSFileManager. They can be discovered and searched using the Finder, but in view of what happens with the other methods, this is probably an oversight. The upshot is that the script can't (or shouldn't) delete the database files itself. It can only click the "Remove" button in Safari's Preferences window and leave it to Safari to take care of things. The kinds of data stored are therefore irrelevant for the purposes of this script.

When removing domains manually (individually or in batches) from the "stored data" table, I find that the table's contents can (but don't always) vary unpredictably. Removed items may reappear a few seconds later and may or may not go away again a few seconds after that. Items which weren't selected may sometimes disappear from the table at the same time as those that were and may or may not come back again later. Items which were simply not there before may spontaneously appear several minutes into the editing process!  hmm  I think this must be due to Safari struggling to update its disk storage while at the same time updating its display, handling instructions for further removals, and competing with other processes for processor time. (This is particularly severe on my machine, which is running BOINC tasks in the background for most of the time.) The least unreliable approach seems to be to select all the domains I want to remove, click the "Remove" button just once, and then wait for several seconds before doing anything else with Safari. Unfortunately, the only way to get all the relevant domains selected at the same time is to select the entire table and then go through individually unselecting the rows I want to keep. This isn't fast, but at least the end result's fairly reliable.

The script presented here is as language-independent as I can make it (although it speaks in English!) and will probably only ever work with Safari 11 and Safari 12. It opens Safari (closing it first, if it was open, to ensure a clean slate with regard to the GUI and any open sites), then opens the "Manage Website Data…" sheet in Safari's Preference window, selects all the listed domains, and unselects any which are either in the white list or not in the black list and not a match for any of the wildcard patterns in the "blackPatterns" list. The three lists are contained in the script object set at the top of the main() handler and should be edited as required. Similarly, there's a property which determines whether the script actually clicks the "Remove" button or leaves the unwanted domains in place, selected for inspection. And there's another property which, if set to 'true', blacks every domain not explicitly in the white list.

NB. My black and white lists aren't the same as Ron's, so you should check what's in them before trying the script yourself!

Applescript:

(* Open the "Manage Website Data" sheet in Safari's Preferences window, select blacklisted stored-data domains, and, if set to do so, delete them.

This has only been tested in Engish with Safari 11.1.2 in El Capitan and in English, French, and Greek with Safari 12.0.3 in Mojave.
Assumptions are kept to a minimum, but it's been necessary to assume that:
   • The first item with an associated keystroke in Safari's "Safari" menu is "Preferences…" in the local language.
   • The "Privacy" button is the seventh in the Preferences window toolbar.
   • The "Privacy" pane is the only one with a certain number of UI elements in group 1 of group 1. (The actual number depends on the Safari version.)
   • The first *named* button in the "Privacy" group is "Manage Website Data…" in the local language.
   • The seventh item with an associated keystroke in Safari's "Edit" menu is "Select All" in the local language.
   • The first button in the drop-down sheet is the "Remove" button and the last is "Done".
*)


use AppleScript version "2.5" -- Mac OS 10.11 (El Capitan) or later (for Safari 11.1.2 or Safari 12.0.3).
use scripting additions

main()

on main()
   -- Various customising properties for convenience. Edit as required.
   script scriptObject
       -- Whether or not the script itself should click the "Remove" button.
       property deleting : true
       -- Whether or not to blacklist every domain not in the white list.
       property blackingAllButWhiteList : false
       -- Explicit white list.
       property whiteList : {"amazon.co.uk", "asteroidsathome.net", "berkely.edu", "climateprediction.net", "cpdn.org", "dropbox.com", "dropboxstatic.com", "einsteinathome.org", "google.co.uk", "google.com", "googlevideo.com", "latenightsw.com", "macscripter.net", "rpi.edu", "uwm.edu", "wikipedia.org", "worldcommunitygrid.org"}
       -- Explicit blacklist.
       property blackList : {"000webhostapp.com", "2mdn.net", "adroll.com", "ads-twitter.com", "adsafeprotected.com", "adsafeprotexted.com", "adsymptotic.com", "ampproject.org", "appdynamics.com", "assets-queue-it.net", "audiencemanager.de", "bing.com", "bizographics.com", "chartbeat.com", "consensu.org", "contentsquare.net", "cookielaw.org", "decibelinsight.net", "demdex.net", "doubleclick.net", "dwin1.com", "edigitalsurvey.com", "ensighten.com", "flashtalking.com", "flexiblehosting.net", "fontawesome.com", "hotjar.com", "ggpht.com", "go-mpulse.net", "gravatar.com", "gstatic.com", "hlserve.com", "icons8.com", "inq.com", "jquery.com", "krxd.net", "logout.cz", "moatads.com", "moengage.com", "monetate.net", "mouseflow.com", "mundayweb.com", "newrelic.com", "optimizely.com", "optmnstr.com", "outbrain.com", "permutive.com", "pinterest.com", "placeholder.com", "postcodeanywhere.co.uk", "qualtrics.com", "rawgit.com", "reevoo.com", "richrelevance.com", "salecycle.com", "scorecardresearch.com", "seenthis.se", "serving-sys.com", "sharethis.com", "smct.co", "sophus3.com", "statcounter.com", "taboola.com", "tripadvisor.co.uk", "trustpilot.com", "tvsquared.com", "twimg.com", "twitter.com", "typography.com", "uniqodo.com", "unpkg.com", "webtrends.com", "windows.net", "wpengine.com", "xg4ken.com", "youvisit.com"}
       -- Wildcard patterns matching unwanted domains. Only checked against domains not in the explicit lists. Each pattern must begin and/or end with "*", but not contain it anywhere else.
       property blackPatterns : {"addthis*", "*amazon*", "*avatar*", "brightcove.*", "*cdn.*", "criteo.*", "*cloud*", "facebook*", "*.fastly.net", "*fonts*", "github*", "*google*", "*instagram*", "*.io", "*media*", "quant*", "*.services", "*stats*", "stripe.*", "*track*", "wiki*"}
       
       -- Used by the manageWebsiteData() handler. Don't edit!
       property rowDescriptions : missing value
   end script
   
   set SafariWasOpen to (application id "com.apple.Safari" is running)
   -- If Safari's running, close it to get rid of any open sites or invisible "closed" windows and to update its stored-data list.
   if (SafariWasOpen) then closeSafari()
   -- Open the stored-data sheet in the "Privacy" pane of Safari's preferences.
   openWebsiteDataSheet()
   -- Select blacklisted domains in the data and remove them if set to do so.
   manageWebsiteData(scriptObject)
   (*
   -- Close Safari if it wasn't open before. (DISABLED TO ALLOW SAFARI TIME TO COMPLETE THE DISK WORK.)
   if (not SafariWasOpen) then closeSafari()
   *)

end main

(* Close Safari to get rid of any open sites or invisible "closed" windows. *)
on closeSafari()
   tell application id "com.apple.systemevents"
       tell (first application process whose bundle identifier is "com.apple.Safari")
           set frontmost to true
           -- If a drop-down sheet is open in the Preferences window, dismiss it.
           tell sheet 1 of window 1
               if (it exists) then
                   click last button
                   repeat while (it exists)
                       delay 0.5
                   end repeat
               end if
           end tell
           -- Click "Quit Safari" in the "Safari" menu and wait for the Safari process to disappear. This works much faster here than a scripted 'quit' command to the application!
           -- (NB. menu item -2. menu item -1 of the "Safari" menu is "Quit and Keep Windows", which appears instead of "Quit Safari" when the option key's held down.)
           click menu item -2 of menu 1 of second menu bar item of menu bar 1
           repeat while (it exists)
               delay 0.5
           end repeat
       end tell
   end tell
end closeSafari

(* Open the stored-data sheet in the "Privacy" pane of Safari's preferences. *)
on openWebsiteDataSheet()
   -- (Re)open Safari.
   tell application id "com.apple.Safari"
       activate
       set windowCount to (count windows) -- Safari itself only counts its document windows
       set SafariVersion to version
   end tell
   
   -- The number of UI elements in the Privacy pane depends on the Safari version.
   considering numeric strings
       if ((SafariVersion < "11") or (SafariVersion is not less than "12.2")) then
           tell application id "com.apple.Safari" to display dialog ("This script's not known to work with Safari " & SafariVersion & "!") buttons {"Stop"} default button 1 cancel button 1 with icon stop
       else if (SafariVersion begins with "11") then
           set PrivacyElementCount to 9 -- Tested with Safari 11.1.2.
       else if (SafariVersion begins with "12.0") then
           set PrivacyElementCount to 7 -- Tested with Safari 12.0.3
       else -- if (SafariVersion < "12.2") then
           set PrivacyElementCount to 6 -- Tested with Safari 12.1
       end if
   end considering
   
   tell application id "com.apple.systemevents"
       tell (first process whose bundle identifier is "com.apple.Safari")
           set frontmost to true
           
           -- Click the first menu item in the "Safari" menu which has a keyboard shortcut. This is hopefully "Preferences…" in the local language.
           set SafariMenu to menu 1 of menu bar item 2 of menu bar 1
           click (first menu item of SafariMenu where class of value of attribute "AXMenuItemCmdChar" is not missing value)
           
           -- Delay until the number of Safari windows is more than the number of its document windows.
           repeat until ((count windows) > windowCount)
               delay 0.1
           end repeat
           -- Use a 'tell' statement with an index reference to the window. (The window's name may change when the "Privacy" button's clicked.)
           tell window 1
               -- Cllck the seventh button in the window's toolbar, delay until there are PrivacyElementCount UI elements in group 1 of group 1 and two of them are buttons, then click the first named button in that group.
               click button 7 of toolbar 1
               tell group 1 of group 1
                   repeat until (((count UI elements) is PrivacyElementCount) and ((count buttons) is 2))
                       delay 0.1
                   end repeat
                   click button (first text of (get name of buttons))
               end tell
               
               -- Wait for the drop-down sheet to appear.
               repeat until (sheet 1 exists)
                   delay 0.1
               end repeat
               -- Then wait for the "Loading Website Data…" message to disappear. This may take some time, depending on processor activity.)
               repeat while ((value of static text 1 of sheet 1 ends with "…") or (value of static text -1 of sheet 1 ends with "…"))
                   delay 0.5
               end repeat
           end tell
       end tell
   end tell
end openWebsiteDataSheet

(* Select blacklisted domains in the stored-data sheet and remove them if set to do so. *)
on manageWebsiteData(scriptObject)
   -- Get a reference to the table displaying the stored-data domains and count the listed domains.
   tell application id "com.apple.systemevents"
       set appProcessSafari to first application process whose bundle identifier is "com.apple.Safari"
       set domainTable to table 1 of scroll area 1 of sheet 1 of window 1 of appProcessSafari
       set rowCount to (count rows of domainTable)
   end tell
   
   -- If the table's not empty:
   if (rowCount > 0) then
       tell application id "com.apple.systemevents"
           -- Switch the focus to the table.
           set focused of domainTable to true
           -- Select the table's entire contents by clicking the seventh menu item with a keyboard shortcut (hopefully "Select All") in Safari's "Edit" menu.
           set EditMenu to menu 1 of menu bar item 4 of menu bar 1 of appProcessSafari
           click (seventh menu item of EditMenu where class of value of attribute "AXMenuItemCmdChar" is not missing value)
           -- Get the description of UI element 1 of every row in the table.
           set scriptObject's rowDescriptions to description of UI element 1 of rows of domainTable
       end tell
       
       -- Except for "Local documents on your computer", each description consists of a domain name followed by a space and the kind(s) of data stored.
       -- We'll use this first space to parse the domain names.
       set astid to AppleScript's text item delimiters
       set AppleScript's text item delimiters to space
       set patternCount to (count scriptObject's blackPatterns)
       -- Work through the descriptions, unselecting the rows corresponding to non-blacklisted domains. This may also take a while!
       if (rowCount > 7) then say "Please wait for the next announcement."
       repeat with i from 1 to rowCount
           set thisDescription to item i of scriptObject's rowDescriptions
           set thisDomain to text item 1 of thisDescription
           if (scriptObject's blackingAllButWhiteList) then
               -- Blacklisting everything not in the white list. Unselect the row if this domain's in the white list.
               if (thisDomain is in scriptObject's whiteList) then tell application id "com.apple.systemevents" to set selected of row i of domainTable to false
           else if (thisDomain is in scriptObject's blackList) then
               -- Heeding the blacklisting and this domain's explicitly blacklisted. Leave the row selected.
           else if (thisDomain is in scriptObject's whiteList) then
               -- Heeding the blacklisting and this domain's explicitly whitelisted. Unselect the row.
               tell application id "com.apple.systemevents" to set selected of row i of domainTable to false
           else
               -- Heeding the blacklisting and no explicit match either way. Test the domain against the wildcard patterns and unselect the row if no matches.
               repeat with j from 1 to patternCount
                   set thisPattern to item j of scriptObject's blackPatterns
                   if (thisPattern begins with "*") then
                       if (thisPattern ends with "*") then
                           set zapping to (thisDomain contains text 2 thru -2 of thisPattern)
                       else
                           set zapping to (thisDomain ends with text 2 thru -1 of thisPattern)
                       end if
                   else -- if (thisDomain ends with "*") then
                       set zapping to (thisDomain begins with text 1 thru -2 of thisPattern)
                   end if
                   if (zapping) then exit repeat
               end repeat
               if (not zapping) then tell application id "com.apple.systemevents" to set selected of row i of domainTable to false
           end if
       end repeat
       set AppleScript's text item delimiters to astid
   end if
   
   -- When done, either click the "Remove" button or not and alert the user.
   tell application id "com.apple.systemevents"
       set RemoveButton to button 1 of sheet 1 of window 1 of appProcessSafari
       set RemoveButtonEnabled to RemoveButton's enabled
   end tell
   if (RemoveButtonEnabled) then
       if (scriptObject's deleting) then
           tell application id "com.apple.systemevents"
               set focused of RemoveButton to true
               click RemoveButton
               repeat while ((count rows of domainTable) is rowCount)
                   delay 0.1
               end repeat
               -- Click "Done" too (if required).
               (* tell window 1 of appProcessSafari
                   set focused of button -1 of sheet 1 to true
                   click button -1 of sheet 1
                   repeat while (sheet 1 exists)
                       delay 0.1
                   end repeat
               end tell *)

           end tell
           say "Blacklisted domains deleted."
       else
           say "Blacklisted domains selected."
       end if
   else
       say "No blacklisted domains found."
   end if
end manageWebsiteData

Edit: openWebsiteDataSheet() handler modified to work with Safari 12.1 too. It also displays a message and stops the script if the Safari version isn't in the range 11.0 to 12.1.x.

Last edited by Nigel Garvey (2019-04-05 02:51:27 am)


NG

Offline

 

#13 2019-03-26 08:34:17 am

KniazidisR
Member
Registered: 2019-03-03
Posts: 126

Re: Safari CookieCleaner - full featured white/junk list based cleaner

Hello, Nigel.

Tested your script. It seems you did it. The script is clear, flexible and instructive. And faster! Thank you very much for the excellent article.

I just need to attack your script to the Quit event of my Safari 11.1.2.

Just one question. Is there a mechanical error ( due to negligence) or is the command specifically included in the comments?

(*
   -- Close Safari if it wasn't open before. (DISABLED TO ALLOW SAFARI TIME TO COMPLETE THE DISK WORK.)
   if (not SafariWasOpen) then closeSafari()
   *)

Last edited by KniazidisR (2019-03-26 09:12:19 am)


macOS Mojave -- version 10.14.4
Safari -- version 12.1

Offline

 

#14 2019-03-26 05:48:57 pm

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4869

Re: Safari CookieCleaner - full featured white/junk list based cleaner

Hi KniazidisR.

Thanks for the feedback. I'm glad the script works for you too.

I deliberately left the final closing of Safari commented out as I found that letting Safari sit for a while after the script was run was the best way to ensure that the deletions were completed internally. It also makes it easier for people trying out the script to check that their blacklisted items have been removed and to see if there are any more domains they want to add to their blacklists. But the closing code's there to be reinstated if people find it works for them, so if you want to give it a try, that's fine. Just remove the (* and *) lines — or comment them out.


NG

Offline

 

#15 2019-04-05 02:56:39 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4869

Re: Safari CookieCleaner - full featured white/junk list based cleaner

I've modified the script in post #12 to work with Safari 12.1 (which came with the Mojave 10.14.4 update) too.


NG

Offline

 
  • Index
  •  » Code Exchange
  •  » Safari CookieCleaner - full featured white/junk list based cleaner

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)