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