Merge pull request #3080 from atom/save-page-api
Implement webContents.savePage API.
This commit is contained in:
		
				commit
				
					
						0e94ccb72b
					
				
			
		
					 8 changed files with 210 additions and 0 deletions
				
			
		|  | @ -11,6 +11,7 @@ | |||
| #include "atom/browser/api/atom_api_download_item.h" | ||||
| #include "atom/browser/atom_browser_context.h" | ||||
| #include "atom/browser/api/atom_api_web_contents.h" | ||||
| #include "atom/browser/api/save_page_handler.h" | ||||
| #include "atom/common/native_mate_converters/callback.h" | ||||
| #include "atom/common/native_mate_converters/gurl_converter.h" | ||||
| #include "atom/common/native_mate_converters/file_path_converter.h" | ||||
|  | @ -237,6 +238,8 @@ Session::~Session() { | |||
| void Session::OnDownloadCreated(content::DownloadManager* manager, | ||||
|                                 content::DownloadItem* item) { | ||||
|   auto web_contents = item->GetWebContents(); | ||||
|   if (SavePageHandler::IsSavePageTypes(item->GetMimeType())) | ||||
|     return; | ||||
|   bool prevent_default = Emit( | ||||
|       "will-download", | ||||
|       DownloadItem::Create(isolate(), item), | ||||
|  |  | |||
|  | @ -162,6 +162,26 @@ struct Converter<net::HttpResponseHeaders*> { | |||
|   } | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Converter<content::SavePageType> { | ||||
|   static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val, | ||||
|                      content::SavePageType* out) { | ||||
|     std::string save_type; | ||||
|     if (!ConvertFromV8(isolate, val, &save_type)) | ||||
|       return false; | ||||
|     if (save_type == "HTMLOnly") { | ||||
|       *out = content::SAVE_PAGE_TYPE_AS_ONLY_HTML; | ||||
|     } else if (save_type == "HTMLComplete") { | ||||
|       *out = content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML; | ||||
|     } else if (save_type == "MHTML") { | ||||
|       *out = content::SAVE_PAGE_TYPE_AS_MHTML; | ||||
|     } else { | ||||
|       return false; | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| }  // namespace mate
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -665,6 +685,13 @@ void WebContents::InsertCSS(const std::string& css) { | |||
|   web_contents()->InsertCSS(css); | ||||
| } | ||||
| 
 | ||||
| bool WebContents::SavePage(const base::FilePath& full_file_path, | ||||
|                            const content::SavePageType& save_type, | ||||
|                            const SavePageHandler::SavePageCallback& callback) { | ||||
|   auto handler = new SavePageHandler(web_contents(), callback); | ||||
|   return handler->Handle(full_file_path, save_type); | ||||
| } | ||||
| 
 | ||||
| void WebContents::ExecuteJavaScript(const base::string16& code, | ||||
|                                     bool has_user_gesture) { | ||||
|   Send(new AtomViewMsg_ExecuteJavaScript(routing_id(), code, has_user_gesture)); | ||||
|  | @ -976,6 +1003,7 @@ mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder( | |||
|         .SetMethod("setUserAgent", &WebContents::SetUserAgent) | ||||
|         .SetMethod("getUserAgent", &WebContents::GetUserAgent) | ||||
|         .SetMethod("insertCSS", &WebContents::InsertCSS) | ||||
|         .SetMethod("savePage", &WebContents::SavePage) | ||||
|         .SetMethod("_executeJavaScript", &WebContents::ExecuteJavaScript) | ||||
|         .SetMethod("openDevTools", &WebContents::OpenDevTools) | ||||
|         .SetMethod("closeDevTools", &WebContents::CloseDevTools) | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| #include <vector> | ||||
| 
 | ||||
| #include "atom/browser/api/frame_subscriber.h" | ||||
| #include "atom/browser/api/save_page_handler.h" | ||||
| #include "atom/browser/api/trackable_object.h" | ||||
| #include "atom/browser/common_web_contents_delegate.h" | ||||
| #include "content/public/browser/web_contents_observer.h" | ||||
|  | @ -73,6 +74,9 @@ class WebContents : public mate::TrackableObject<WebContents>, | |||
|   void SetUserAgent(const std::string& user_agent); | ||||
|   std::string GetUserAgent(); | ||||
|   void InsertCSS(const std::string& css); | ||||
|   bool SavePage(const base::FilePath& full_file_path, | ||||
|                 const content::SavePageType& save_type, | ||||
|                 const SavePageHandler::SavePageCallback& callback); | ||||
|   void ExecuteJavaScript(const base::string16& code, | ||||
|                          bool has_user_gesture); | ||||
|   void OpenDevTools(mate::Arguments* args); | ||||
|  |  | |||
							
								
								
									
										78
									
								
								atom/browser/api/save_page_handler.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								atom/browser/api/save_page_handler.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | |||
| // Copyright (c) 2015 GitHub, Inc.
 | ||||
| // Use of this source code is governed by the MIT license that can be
 | ||||
| // found in the LICENSE file.
 | ||||
| 
 | ||||
| #include "atom/browser/api/save_page_handler.h" | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| #include "atom/browser/atom_browser_context.h" | ||||
| #include "base/callback.h" | ||||
| #include "base/files/file_path.h" | ||||
| #include "content/public/browser/web_contents.h" | ||||
| 
 | ||||
| namespace atom { | ||||
| 
 | ||||
| namespace api { | ||||
| 
 | ||||
| SavePageHandler::SavePageHandler(content::WebContents* web_contents, | ||||
|                                  const SavePageCallback& callback) | ||||
|     : web_contents_(web_contents), | ||||
|       callback_(callback) { | ||||
| } | ||||
| 
 | ||||
| SavePageHandler::~SavePageHandler() { | ||||
| } | ||||
| 
 | ||||
| void SavePageHandler::OnDownloadCreated(content::DownloadManager* manager, | ||||
|                                         content::DownloadItem* item) { | ||||
|   // OnDownloadCreated is invoked during WebContents::SavePage, so the |item|
 | ||||
|   // here is the one stated by WebContents::SavePage.
 | ||||
|   item->AddObserver(this); | ||||
| } | ||||
| 
 | ||||
| bool SavePageHandler::Handle(const base::FilePath& full_path, | ||||
|                              const content::SavePageType& save_type) { | ||||
|   auto download_manager = content::BrowserContext::GetDownloadManager( | ||||
|       web_contents_->GetBrowserContext()); | ||||
|   download_manager->AddObserver(this); | ||||
|   bool result = web_contents_->SavePage(full_path, | ||||
|                                         full_path.DirName(), | ||||
|                                         save_type); | ||||
|   download_manager->RemoveObserver(this); | ||||
|   // If initialization fails which means fail to create |DownloadItem|, we need
 | ||||
|   // to delete the |SavePageHandler| instance to avoid memory-leak.
 | ||||
|   if (!result) | ||||
|     delete this; | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| void SavePageHandler::OnDownloadUpdated(content::DownloadItem* item) { | ||||
|   if (item->IsDone()) { | ||||
|     v8::Isolate* isolate = v8::Isolate::GetCurrent(); | ||||
|     v8::Locker locker(isolate); | ||||
|     v8::HandleScope handle_scope(isolate); | ||||
|     if (item->GetState() == content::DownloadItem::COMPLETE) { | ||||
|       callback_.Run(v8::Null(isolate)); | ||||
|     } else { | ||||
|       v8::Local<v8::String> error_message = v8::String::NewFromUtf8( | ||||
|           isolate, "Fail to save page"); | ||||
|       callback_.Run(v8::Exception::Error(error_message)); | ||||
|     } | ||||
|     Destroy(item); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void SavePageHandler::Destroy(content::DownloadItem* item) { | ||||
|   item->RemoveObserver(this); | ||||
|   delete this; | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| bool SavePageHandler::IsSavePageTypes(const std::string& type) { | ||||
|   return type == "multipart/related" || type == "text/html"; | ||||
| } | ||||
| 
 | ||||
| }  // namespace api
 | ||||
| 
 | ||||
| }  // namespace atom
 | ||||
							
								
								
									
										60
									
								
								atom/browser/api/save_page_handler.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								atom/browser/api/save_page_handler.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | |||
| // Copyright (c) 2015 GitHub, Inc.
 | ||||
| // Use of this source code is governed by the MIT license that can be
 | ||||
| // found in the LICENSE file.
 | ||||
| 
 | ||||
| #ifndef ATOM_BROWSER_API_SAVE_PAGE_HANDLER_H_ | ||||
| #define ATOM_BROWSER_API_SAVE_PAGE_HANDLER_H_ | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| #include "content/public/browser/download_item.h" | ||||
| #include "content/public/browser/download_manager.h" | ||||
| #include "content/public/browser/save_page_type.h" | ||||
| #include "v8/include/v8.h" | ||||
| 
 | ||||
| namespace base { | ||||
| class FilePath; | ||||
| } | ||||
| 
 | ||||
| namespace content { | ||||
| class WebContents; | ||||
| } | ||||
| 
 | ||||
| namespace atom { | ||||
| 
 | ||||
| namespace api { | ||||
| 
 | ||||
| // A self-destroyed class for handling save page request.
 | ||||
| class SavePageHandler : public content::DownloadManager::Observer, | ||||
|                         public content::DownloadItem::Observer { | ||||
|  public: | ||||
|   using SavePageCallback = base::Callback<void(v8::Local<v8::Value>)>; | ||||
| 
 | ||||
|   SavePageHandler(content::WebContents* web_contents, | ||||
|                   const SavePageCallback& callback); | ||||
|   ~SavePageHandler(); | ||||
| 
 | ||||
|   bool Handle(const base::FilePath& full_path, | ||||
|               const content::SavePageType& save_type); | ||||
| 
 | ||||
|   static bool IsSavePageTypes(const std::string& type); | ||||
| 
 | ||||
|  private: | ||||
|   void Destroy(content::DownloadItem* item); | ||||
| 
 | ||||
|   // content::DownloadManager::Observer:
 | ||||
|   void OnDownloadCreated(content::DownloadManager* manager, | ||||
|                          content::DownloadItem* item) override; | ||||
| 
 | ||||
|   // content::DownloadItem::Observer:
 | ||||
|   void OnDownloadUpdated(content::DownloadItem* item) override; | ||||
| 
 | ||||
|   content::WebContents* web_contents_;  // weak
 | ||||
|   SavePageCallback callback_; | ||||
| }; | ||||
| 
 | ||||
| }  // namespace api
 | ||||
| 
 | ||||
| }  // namespace atom
 | ||||
| 
 | ||||
| #endif  // ATOM_BROWSER_API_SAVE_PAGE_HANDLER_H_
 | ||||
|  | @ -631,3 +631,26 @@ Get the `WebContents` of DevTools for this `WebContents`. | |||
| 
 | ||||
| **Note:** Users should never store this object because it may become `null` | ||||
| when the DevTools has been closed. | ||||
| 
 | ||||
| ### `webContents.savePage(fullPath, saveType, callback)` | ||||
| 
 | ||||
| * `fullPath` String - The full file path. | ||||
| * `saveType` String - Specify the save type. | ||||
|   * `HTMLOnly` - Save only the HTML of the page. | ||||
|   * `HTMLComplete` - Save complete-html page. | ||||
|   * `MHTML` - Save complete-html page as MHTML. | ||||
| * `callback` Function - `function(error) {}`. | ||||
|   * `error` Error | ||||
| 
 | ||||
| Returns true if the process of saving page has been initiated successfully. | ||||
| 
 | ||||
| ```javascript | ||||
| win.loadUrl('https://github.com'); | ||||
| 
 | ||||
| win.webContents.on('did-finish-load', function() { | ||||
|   win.webContents.savePage('/tmp/test.html', 'HTMLComplete', function(error) { | ||||
|     if (!error) | ||||
|       console.log("Save page successfully"); | ||||
|   }); | ||||
| }); | ||||
| ``` | ||||
|  |  | |||
|  | @ -108,6 +108,8 @@ | |||
|       'atom/browser/api/trackable_object.h', | ||||
|       'atom/browser/api/frame_subscriber.cc', | ||||
|       'atom/browser/api/frame_subscriber.h', | ||||
|       'atom/browser/api/save_page_handler.cc', | ||||
|       'atom/browser/api/save_page_handler.h', | ||||
|       'atom/browser/auto_updater.cc', | ||||
|       'atom/browser/auto_updater.h', | ||||
|       'atom/browser/auto_updater_delegate.h', | ||||
|  |  | |||
|  | @ -301,3 +301,15 @@ describe 'browser-window module', -> | |||
|         assert.notEqual data.length, 0 | ||||
|         w.webContents.endFrameSubscription() | ||||
|         done() | ||||
| 
 | ||||
|   describe 'save page', -> | ||||
|     savePagePath = path.join fixtures, 'save_page.html' | ||||
|     it 'should save page', (done) -> | ||||
|       w.webContents.on 'did-finish-load', -> | ||||
|         w.webContents.savePage savePagePath, 'HTMLComplete', (error) -> | ||||
|           assert.equal error, null | ||||
|           assert fs.existsSync savePagePath | ||||
|           fs.unlinkSync savePagePath | ||||
|           done() | ||||
| 
 | ||||
|       w.loadUrl "file://#{fixtures}/api/blank.html" | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Cheng Zhao
				Cheng Zhao