Home | History | Annotate | Download | only in applescript
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #import "chrome/browser/ui/cocoa/applescript/tab_applescript.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/files/file_path.h"
      9 #include "base/logging.h"
     10 #import "base/mac/scoped_nsobject.h"
     11 #include "base/strings/sys_string_conversions.h"
     12 #include "chrome/browser/printing/print_view_manager.h"
     13 #include "chrome/browser/sessions/session_id.h"
     14 #include "chrome/browser/sessions/session_tab_helper.h"
     15 #include "chrome/browser/ui/cocoa/applescript/apple_event_util.h"
     16 #include "chrome/browser/ui/cocoa/applescript/error_applescript.h"
     17 #include "chrome/common/url_constants.h"
     18 #include "content/public/browser/navigation_controller.h"
     19 #include "content/public/browser/navigation_entry.h"
     20 #include "content/public/browser/render_frame_host.h"
     21 #include "content/public/browser/render_view_host.h"
     22 #include "content/public/browser/save_page_type.h"
     23 #include "content/public/browser/web_contents.h"
     24 #include "content/public/browser/web_contents_delegate.h"
     25 #include "url/gurl.h"
     26 
     27 using content::NavigationController;
     28 using content::NavigationEntry;
     29 using content::OpenURLParams;
     30 using content::RenderFrameHost;
     31 using content::RenderViewHost;
     32 using content::Referrer;
     33 using content::WebContents;
     34 
     35 namespace {
     36 
     37 void ResumeAppleEventAndSendReply(NSAppleEventManagerSuspensionID suspension_id,
     38                                   const base::Value* result_value) {
     39   NSAppleEventDescriptor* result_descriptor =
     40       chrome::mac::ValueToAppleEventDescriptor(result_value);
     41 
     42   NSAppleEventManager* manager = [NSAppleEventManager sharedAppleEventManager];
     43   NSAppleEventDescriptor* reply_event =
     44       [manager replyAppleEventForSuspensionID:suspension_id];
     45   [reply_event setParamDescriptor:result_descriptor
     46                        forKeyword:keyDirectObject];
     47   [manager resumeWithSuspensionID:suspension_id];
     48 }
     49 
     50 }  // namespace
     51 
     52 @interface TabAppleScript()
     53 @property (nonatomic, copy) NSString* tempURL;
     54 @end
     55 
     56 @implementation TabAppleScript
     57 
     58 @synthesize tempURL = tempURL_;
     59 
     60 - (id)init {
     61   if ((self = [super init])) {
     62     SessionID session;
     63     SessionID::id_type futureSessionIDOfTab = session.id() + 1;
     64     // Holds the SessionID that the new tab is going to get.
     65     base::scoped_nsobject<NSNumber> numID(
     66         [[NSNumber alloc] initWithInt:futureSessionIDOfTab]);
     67     [self setUniqueID:numID];
     68   }
     69   return self;
     70 }
     71 
     72 - (void)dealloc {
     73   [tempURL_ release];
     74   [super dealloc];
     75 }
     76 
     77 - (id)initWithWebContents:(content::WebContents*)webContents {
     78   if (!webContents) {
     79     [self release];
     80     return nil;
     81   }
     82 
     83   if ((self = [super init])) {
     84     // It is safe to be weak; if a tab goes away (e.g. the user closes a tab)
     85     // the AppleScript runtime calls tabs in AppleScriptWindow and this
     86     // particular tab is never returned.
     87     webContents_ = webContents;
     88     SessionTabHelper* session_tab_helper =
     89         SessionTabHelper::FromWebContents(webContents);
     90     base::scoped_nsobject<NSNumber> numID(
     91         [[NSNumber alloc] initWithInt:session_tab_helper->session_id().id()]);
     92     [self setUniqueID:numID];
     93   }
     94   return self;
     95 }
     96 
     97 - (void)setWebContents:(content::WebContents*)webContents {
     98   DCHECK(webContents);
     99   // It is safe to be weak; if a tab goes away (e.g. the user closes a tab)
    100   // the AppleScript runtime calls tabs in AppleScriptWindow and this
    101   // particular tab is never returned.
    102   webContents_ = webContents;
    103   SessionTabHelper* session_tab_helper =
    104       SessionTabHelper::FromWebContents(webContents);
    105   base::scoped_nsobject<NSNumber> numID(
    106       [[NSNumber alloc] initWithInt:session_tab_helper->session_id().id()]);
    107   [self setUniqueID:numID];
    108 
    109   if ([self tempURL])
    110     [self setURL:[self tempURL]];
    111 }
    112 
    113 - (NSString*)URL {
    114   if (!webContents_) {
    115     return nil;
    116   }
    117 
    118   NavigationEntry* entry = webContents_->GetController().GetActiveEntry();
    119   if (!entry) {
    120     return nil;
    121   }
    122   const GURL& url = entry->GetVirtualURL();
    123   return base::SysUTF8ToNSString(url.spec());
    124 }
    125 
    126 - (void)setURL:(NSString*)aURL {
    127   // If a scripter sets a URL before the node is added save it at a temporary
    128   // location.
    129   if (!webContents_) {
    130     [self setTempURL:aURL];
    131     return;
    132   }
    133 
    134   GURL url(base::SysNSStringToUTF8(aURL));
    135   // check for valid url.
    136   if (!url.is_empty() && !url.is_valid()) {
    137     AppleScript::SetError(AppleScript::errInvalidURL);
    138     return;
    139   }
    140 
    141   NavigationEntry* entry = webContents_->GetController().GetActiveEntry();
    142   if (!entry)
    143     return;
    144 
    145   const GURL& previousURL = entry->GetVirtualURL();
    146   webContents_->OpenURL(OpenURLParams(
    147       url,
    148       content::Referrer(previousURL, blink::WebReferrerPolicyDefault),
    149       CURRENT_TAB,
    150       content::PAGE_TRANSITION_TYPED,
    151       false));
    152 }
    153 
    154 - (NSString*)title {
    155   NavigationEntry* entry = webContents_->GetController().GetActiveEntry();
    156   if (!entry)
    157     return nil;
    158 
    159   base::string16 title = entry ? entry->GetTitle() : base::string16();
    160   return base::SysUTF16ToNSString(title);
    161 }
    162 
    163 - (NSNumber*)loading {
    164   BOOL loadingValue = webContents_->IsLoading() ? YES : NO;
    165   return [NSNumber numberWithBool:loadingValue];
    166 }
    167 
    168 - (void)handlesUndoScriptCommand:(NSScriptCommand*)command {
    169   webContents_->Undo();
    170 }
    171 
    172 - (void)handlesRedoScriptCommand:(NSScriptCommand*)command {
    173   webContents_->Redo();
    174 }
    175 
    176 - (void)handlesCutScriptCommand:(NSScriptCommand*)command {
    177   webContents_->Cut();
    178 }
    179 
    180 - (void)handlesCopyScriptCommand:(NSScriptCommand*)command {
    181   webContents_->Copy();
    182 }
    183 
    184 - (void)handlesPasteScriptCommand:(NSScriptCommand*)command {
    185   webContents_->Paste();
    186 }
    187 
    188 - (void)handlesSelectAllScriptCommand:(NSScriptCommand*)command {
    189   webContents_->SelectAll();
    190 }
    191 
    192 - (void)handlesGoBackScriptCommand:(NSScriptCommand*)command {
    193   NavigationController& navigationController = webContents_->GetController();
    194   if (navigationController.CanGoBack())
    195     navigationController.GoBack();
    196 }
    197 
    198 - (void)handlesGoForwardScriptCommand:(NSScriptCommand*)command {
    199   NavigationController& navigationController = webContents_->GetController();
    200   if (navigationController.CanGoForward())
    201     navigationController.GoForward();
    202 }
    203 
    204 - (void)handlesReloadScriptCommand:(NSScriptCommand*)command {
    205   NavigationController& navigationController = webContents_->GetController();
    206   const bool checkForRepost = true;
    207   navigationController.Reload(checkForRepost);
    208 }
    209 
    210 - (void)handlesStopScriptCommand:(NSScriptCommand*)command {
    211   RenderViewHost* view = webContents_->GetRenderViewHost();
    212   if (!view) {
    213     // We tolerate Stop being called even before a view has been created.
    214     // So just log a warning instead of a NOTREACHED().
    215     DLOG(WARNING) << "Stop: no view for handle ";
    216     return;
    217   }
    218 
    219   view->Stop();
    220 }
    221 
    222 - (void)handlesPrintScriptCommand:(NSScriptCommand*)command {
    223   bool initiated =
    224       printing::PrintViewManager::FromWebContents(webContents_)->PrintNow();
    225   if (!initiated) {
    226     AppleScript::SetError(AppleScript::errInitiatePrinting);
    227   }
    228 }
    229 
    230 - (void)handlesSaveScriptCommand:(NSScriptCommand*)command {
    231   NSDictionary* dictionary = [command evaluatedArguments];
    232 
    233   NSURL* fileURL = [dictionary objectForKey:@"File"];
    234   // Scripter has not specifed the location at which to save, so we prompt for
    235   // it.
    236   if (!fileURL) {
    237     webContents_->OnSavePage();
    238     return;
    239   }
    240 
    241   base::FilePath mainFile(base::SysNSStringToUTF8([fileURL path]));
    242   // We create a directory path at the folder within which the file exists.
    243   // Eg.    if main_file = '/Users/Foo/Documents/Google.html'
    244   // then directory_path = '/Users/Foo/Documents/Google_files/'.
    245   base::FilePath directoryPath = mainFile.RemoveExtension();
    246   directoryPath = directoryPath.InsertBeforeExtension(std::string("_files/"));
    247 
    248   NSString* saveType = [dictionary objectForKey:@"FileType"];
    249 
    250   content::SavePageType savePageType = content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML;
    251   if (saveType) {
    252     if ([saveType isEqualToString:@"only html"]) {
    253       savePageType = content::SAVE_PAGE_TYPE_AS_ONLY_HTML;
    254     } else if ([saveType isEqualToString:@"complete html"]) {
    255       savePageType = content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML;
    256     } else {
    257       AppleScript::SetError(AppleScript::errInvalidSaveType);
    258       return;
    259     }
    260   }
    261 
    262   webContents_->SavePage(mainFile, directoryPath, savePageType);
    263 }
    264 
    265 - (void)handlesCloseScriptCommand:(NSScriptCommand*)command {
    266   webContents_->GetDelegate()->CloseContents(webContents_);
    267 }
    268 
    269 - (void)handlesViewSourceScriptCommand:(NSScriptCommand*)command {
    270   NavigationEntry* entry =
    271       webContents_->GetController().GetLastCommittedEntry();
    272   if (entry) {
    273     webContents_->OpenURL(
    274         OpenURLParams(GURL(content::kViewSourceScheme + std::string(":") +
    275                            entry->GetURL().spec()),
    276                       Referrer(),
    277                       NEW_FOREGROUND_TAB,
    278                       content::PAGE_TRANSITION_LINK,
    279                       false));
    280   }
    281 }
    282 
    283 - (id)handlesExecuteJavascriptScriptCommand:(NSScriptCommand*)command {
    284   content::RenderFrameHost* frame = webContents_->GetMainFrame();
    285   if (!frame) {
    286     NOTREACHED();
    287     return nil;
    288   }
    289 
    290   NSAppleEventManager* manager = [NSAppleEventManager sharedAppleEventManager];
    291   NSAppleEventManagerSuspensionID suspensionID =
    292       [manager suspendCurrentAppleEvent];
    293   content::RenderFrameHost::JavaScriptResultCallback callback =
    294       base::Bind(&ResumeAppleEventAndSendReply, suspensionID);
    295 
    296   base::string16 script = base::SysNSStringToUTF16(
    297       [[command evaluatedArguments] objectForKey:@"javascript"]);
    298   frame->ExecuteJavaScript(script, callback);
    299 
    300   return nil;
    301 }
    302 
    303 @end
    304