1 /* 2 * Copyright (C) 2007 Ryan Leavengood <leavengood@gmail.com> 3 * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de> 4 * 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "BrowserApp.h" 30 31 #include <AboutWindow.h> 32 #include <Alert.h> 33 #include <Autolock.h> 34 #include <Catalog.h> 35 #include <Directory.h> 36 #include <Entry.h> 37 #include <FindDirectory.h> 38 #include <Locale.h> 39 #include <Path.h> 40 #include <Screen.h> 41 #include <UrlContext.h> 42 #include <debugger.h> 43 44 #include <stdio.h> 45 46 #include "BrowserWindow.h" 47 #include "BrowsingHistory.h" 48 #include "DownloadWindow.h" 49 #include "SettingsMessage.h" 50 #include "SettingsWindow.h" 51 #include "ConsoleWindow.h" 52 #include "CookieWindow.h" 53 #include "NetworkCookieJar.h" 54 #include "WebKitInfo.h" 55 #include "WebPage.h" 56 #include "WebSettings.h" 57 #include "WebView.h" 58 #include "WebViewConstants.h" 59 60 61 #undef B_TRANSLATION_CONTEXT 62 #define B_TRANSLATION_CONTEXT "WebPositive" 63 64 const char* kApplicationSignature = "application/x-vnd.Haiku-WebPositive"; 65 const char* kApplicationName = B_TRANSLATE_SYSTEM_NAME("WebPositive"); 66 static const uint32 PRELOAD_BROWSING_HISTORY = 'plbh'; 67 68 #define ENABLE_NATIVE_COOKIES 1 69 70 71 BrowserApp::BrowserApp() 72 : 73 BApplication(kApplicationSignature), 74 fWindowCount(0), 75 fLastWindowFrame(50, 50, 950, 750), 76 fLaunchRefsMessage(0), 77 fInitialized(false), 78 fSettings(NULL), 79 fCookies(NULL), 80 fSession(NULL), 81 fContext(NULL), 82 fDownloadWindow(NULL), 83 fSettingsWindow(NULL), 84 fConsoleWindow(NULL), 85 fCookieWindow(NULL) 86 { 87 #ifdef __i386__ 88 // First let's check SSE2 is available 89 cpuid_info info; 90 get_cpuid(&info, 1, 0); 91 92 if ((info.eax_1.features & (1 << 26)) == 0) { 93 BAlert alert(B_TRANSLATE("No SSE2 support"), B_TRANSLATE("Your CPU is " 94 "too old and does not support the SSE2 extensions, without which " 95 "WebPositive cannot run. We recommend installing NetSurf instead."), 96 B_TRANSLATE("Darn!")); 97 alert.Go(); 98 exit(-1); 99 } 100 #endif 101 102 #if ENABLE_NATIVE_COOKIES 103 BString cookieStorePath = kApplicationName; 104 cookieStorePath << "/Cookies"; 105 fCookies = new SettingsMessage(B_USER_SETTINGS_DIRECTORY, 106 cookieStorePath.String()); 107 fContext = new BPrivate::Network::BUrlContext(); 108 if (fCookies->InitCheck() == B_OK) { 109 BMessage cookieArchive = fCookies->GetValue("cookies", cookieArchive); 110 fContext->SetCookieJar( 111 BPrivate::Network::BNetworkCookieJar(&cookieArchive)); 112 } 113 #endif 114 115 BString sessionStorePath = kApplicationName; 116 sessionStorePath << "/Session"; 117 fSession = new SettingsMessage(B_USER_SETTINGS_DIRECTORY, 118 sessionStorePath.String()); 119 } 120 121 122 BrowserApp::~BrowserApp() 123 { 124 delete fLaunchRefsMessage; 125 delete fSettings; 126 delete fCookies; 127 delete fSession; 128 delete fContext; 129 } 130 131 132 void 133 BrowserApp::AboutRequested() 134 { 135 BAboutWindow* window = new BAboutWindow(kApplicationName, 136 kApplicationSignature); 137 138 // create the about window 139 140 const char* authors[] = { 141 "Andrea Anzani", 142 "Stephan Aßmus", 143 "Alexandre Deckner", 144 "Adrien Destugues", 145 "Rajagopalan Gangadharan", 146 "Rene Gollent", 147 "Ryan Leavengood", 148 "Michael Lotz", 149 "Maxime Simon", 150 NULL 151 }; 152 153 BString aboutText(""); 154 aboutText << "HaikuWebKit " << WebKitInfo::HaikuWebKitVersion(); 155 aboutText << "\nWebKit " << WebKitInfo::WebKitVersion(); 156 157 window->AddCopyright(2007, "Haiku, Inc."); 158 window->AddAuthors(authors); 159 window->AddExtraInfo(aboutText.String()); 160 161 window->Show(); 162 } 163 164 165 void 166 BrowserApp::ArgvReceived(int32 argc, char** argv) 167 { 168 BMessage message(B_REFS_RECEIVED); 169 for (int i = 1; i < argc; i++) { 170 if (strcmp("-f", argv[i]) == 0 171 || strcmp("--fullscreen", argv[i]) == 0) { 172 message.AddBool("fullscreen", true); 173 continue; 174 } 175 const char* url = argv[i]; 176 BEntry entry(argv[i], true); 177 BPath path; 178 if (entry.Exists() && entry.GetPath(&path) == B_OK) 179 url = path.Path(); 180 message.AddString("url", url); 181 } 182 // Upon program launch, it will buffer a copy of the message, since 183 // ArgReceived() is called before ReadyToRun(). 184 RefsReceived(&message); 185 } 186 187 188 void 189 BrowserApp::ReadyToRun() 190 { 191 // Since we will essentially run the GUI... 192 set_thread_priority(Thread(), B_DISPLAY_PRIORITY); 193 194 BWebPage::InitializeOnce(); 195 BWebPage::SetCacheModel(B_WEBKIT_CACHE_MODEL_WEB_BROWSER); 196 197 BPath path; 198 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK 199 && path.Append(kApplicationName) == B_OK 200 && create_directory(path.Path(), 0777) == B_OK) { 201 202 BWebSettings::SetPersistentStoragePath(path.Path()); 203 } 204 205 BString mainSettingsPath(kApplicationName); 206 mainSettingsPath << "/Application"; 207 fSettings = new SettingsMessage(B_USER_SETTINGS_DIRECTORY, 208 mainSettingsPath.String()); 209 210 fLastWindowFrame = fSettings->GetValue("window frame", fLastWindowFrame); 211 BRect defaultDownloadWindowFrame(-10, -10, 365, 265); 212 BRect downloadWindowFrame = fSettings->GetValue("downloads window frame", 213 defaultDownloadWindowFrame); 214 BRect settingsWindowFrame = fSettings->GetValue("settings window frame", 215 BRect()); 216 BRect consoleWindowFrame = fSettings->GetValue("console window frame", 217 BRect(50, 50, 400, 300)); 218 BRect cookieWindowFrame = fSettings->GetValue("cookie window frame", 219 BRect(50, 50, 400, 300)); 220 bool showDownloads = fSettings->GetValue("show downloads", false); 221 222 fDownloadWindow = new DownloadWindow(downloadWindowFrame, showDownloads, 223 fSettings); 224 if (downloadWindowFrame == defaultDownloadWindowFrame) { 225 // Initially put download window in lower right of screen. 226 BRect screenFrame = BScreen().Frame(); 227 BMessage decoratorSettings; 228 fDownloadWindow->GetDecoratorSettings(&decoratorSettings); 229 float borderWidth = 0; 230 if (decoratorSettings.FindFloat("border width", &borderWidth) != B_OK) 231 borderWidth = 5; 232 fDownloadWindow->MoveTo(screenFrame.Width() 233 - fDownloadWindow->Frame().Width() - borderWidth, 234 screenFrame.Height() - fDownloadWindow->Frame().Height() 235 - borderWidth); 236 } 237 fSettingsWindow = new SettingsWindow(settingsWindowFrame, fSettings); 238 239 BWebPage::SetDownloadListener(BMessenger(fDownloadWindow)); 240 241 fConsoleWindow = new ConsoleWindow(consoleWindowFrame); 242 fCookieWindow = new CookieWindow(cookieWindowFrame, fContext->GetCookieJar()); 243 244 fInitialized = true; 245 246 int32 pagesCreated = 0; 247 bool fullscreen = false; 248 if (fLaunchRefsMessage) { 249 _RefsReceived(fLaunchRefsMessage, &pagesCreated, &fullscreen); 250 delete fLaunchRefsMessage; 251 fLaunchRefsMessage = NULL; 252 } 253 254 // If no refs led to a new open page, open new session if set 255 if (fSession->InitCheck() == B_OK && pagesCreated == 0) { 256 const char* kSettingsKeyStartUpPolicy = "start up policy"; 257 uint32 fStartUpPolicy = fSettings->GetValue(kSettingsKeyStartUpPolicy, 258 (uint32)ResumePriorSession); 259 if (fStartUpPolicy == StartNewSession) { 260 PostMessage(NEW_WINDOW); 261 } else { 262 // otherwise, restore previous session 263 BMessage archivedWindow; 264 for (int i = 0; fSession->FindMessage("window", i, &archivedWindow) 265 == B_OK; i++) { 266 BRect frame = archivedWindow.FindRect("window frame"); 267 BString url; 268 archivedWindow.FindString("tab", 0, &url); 269 BrowserWindow* window = new(std::nothrow) BrowserWindow(frame, 270 fSettings, url, fContext); 271 272 if (window != NULL) { 273 window->Show(); 274 pagesCreated++; 275 276 for (int j = 1; archivedWindow.FindString("tab", j, &url) 277 == B_OK; j++) { 278 printf("Create %d:%d\n", i, j); 279 _CreateNewTab(window, url, false); 280 pagesCreated++; 281 } 282 } 283 } 284 } 285 } 286 287 // If previous session did not contain any window, create a new empty one. 288 if (pagesCreated == 0) 289 _CreateNewWindow("", fullscreen); 290 291 PostMessage(PRELOAD_BROWSING_HISTORY); 292 } 293 294 295 void 296 BrowserApp::MessageReceived(BMessage* message) 297 { 298 switch (message->what) { 299 case PRELOAD_BROWSING_HISTORY: 300 // Accessing the default instance will load the history from disk. 301 BrowsingHistory::DefaultInstance(); 302 break; 303 case B_SILENT_RELAUNCH: 304 _CreateNewPage(""); 305 break; 306 case NEW_WINDOW: { 307 BString url; 308 if (message->FindString("url", &url) != B_OK) 309 break; 310 _CreateNewWindow(url); 311 break; 312 } 313 case NEW_TAB: { 314 BrowserWindow* window; 315 if (message->FindPointer("window", 316 reinterpret_cast<void**>(&window)) != B_OK) 317 break; 318 BString url; 319 message->FindString("url", &url); 320 bool select = false; 321 message->FindBool("select", &select); 322 _CreateNewTab(window, url, select); 323 break; 324 } 325 case WINDOW_OPENED: 326 fWindowCount++; 327 fDownloadWindow->SetMinimizeOnClose(false); 328 break; 329 case WINDOW_CLOSED: 330 fWindowCount--; 331 message->FindRect("window frame", &fLastWindowFrame); 332 if (fWindowCount <= 0) { 333 BMessage* message = new BMessage(B_QUIT_REQUESTED); 334 message->AddMessage("window", DetachCurrentMessage()); 335 PostMessage(message); 336 } 337 break; 338 339 case SHOW_DOWNLOAD_WINDOW: 340 _ShowWindow(message, fDownloadWindow); 341 break; 342 case SHOW_SETTINGS_WINDOW: 343 _ShowWindow(message, fSettingsWindow); 344 break; 345 case SHOW_CONSOLE_WINDOW: 346 _ShowWindow(message, fConsoleWindow); 347 break; 348 case SHOW_COOKIE_WINDOW: 349 _ShowWindow(message, fCookieWindow); 350 break; 351 case ADD_CONSOLE_MESSAGE: 352 fConsoleWindow->PostMessage(message); 353 break; 354 355 default: 356 BApplication::MessageReceived(message); 357 break; 358 } 359 } 360 361 362 void 363 BrowserApp::RefsReceived(BMessage* message) 364 { 365 if (!fInitialized) { 366 delete fLaunchRefsMessage; 367 fLaunchRefsMessage = new BMessage(*message); 368 return; 369 } 370 371 _RefsReceived(message); 372 } 373 374 375 bool 376 BrowserApp::QuitRequested() 377 { 378 if (fDownloadWindow->DownloadsInProgress()) { 379 BAlert* alert = new BAlert(B_TRANSLATE("Downloads in progress"), 380 B_TRANSLATE("There are still downloads in progress, do you really " 381 "want to quit WebPositive now?"), B_TRANSLATE("Quit"), 382 B_TRANSLATE("Continue downloads")); 383 int32 choice = alert->Go(); 384 if (choice == 1) { 385 if (fWindowCount == 0) { 386 if (fDownloadWindow->Lock()) { 387 fDownloadWindow->SetWorkspaces(1 << current_workspace()); 388 if (fDownloadWindow->IsHidden()) 389 fDownloadWindow->Show(); 390 else 391 fDownloadWindow->Activate(); 392 fDownloadWindow->SetMinimizeOnClose(true); 393 fDownloadWindow->Unlock(); 394 return false; 395 } 396 } else 397 return false; 398 } 399 } 400 401 fSession->MakeEmpty(); 402 403 /* See if we got here because the last window is already closed. 404 * In that case we only need to save that one, which is already archived */ 405 BMessage* message = CurrentMessage(); 406 BMessage windowMessage; 407 408 status_t ret = message->FindMessage("window", &windowMessage); 409 if (ret == B_OK) { 410 fSession->AddMessage("window", &windowMessage); 411 } else { 412 for (int i = 0; BWindow* window = WindowAt(i); i++) { 413 BrowserWindow* webWindow = dynamic_cast<BrowserWindow*>(window); 414 if (!webWindow) 415 continue; 416 if (!webWindow->Lock()) 417 continue; 418 419 BMessage windowArchive; 420 webWindow->Archive(&windowArchive, true); 421 fSession->AddMessage("window", &windowArchive); 422 423 if (webWindow->QuitRequested()) { 424 fLastWindowFrame = webWindow->WindowFrame(); 425 webWindow->Quit(); 426 i--; 427 } else { 428 webWindow->Unlock(); 429 return false; 430 } 431 } 432 } 433 434 BWebPage::ShutdownOnce(); 435 436 fSettings->SetValue("window frame", fLastWindowFrame); 437 if (fDownloadWindow->Lock()) { 438 fSettings->SetValue("downloads window frame", fDownloadWindow->Frame()); 439 fSettings->SetValue("show downloads", !fDownloadWindow->IsHidden()); 440 fDownloadWindow->Unlock(); 441 } 442 if (fSettingsWindow->Lock()) { 443 fSettings->SetValue("settings window frame", fSettingsWindow->Frame()); 444 fSettingsWindow->Unlock(); 445 } 446 if (fConsoleWindow->Lock()) { 447 fSettings->SetValue("console window frame", fConsoleWindow->Frame()); 448 fConsoleWindow->Unlock(); 449 } 450 if (fCookieWindow->Lock()) { 451 fSettings->SetValue("cookie window frame", fCookieWindow->Frame()); 452 fCookieWindow->Unlock(); 453 } 454 455 BMessage cookieArchive; 456 BPrivate::Network::BNetworkCookieJar& cookieJar = fContext->GetCookieJar(); 457 cookieJar.PurgeForExit(); 458 if (cookieJar.Archive(&cookieArchive) == B_OK) 459 fCookies->SetValue("cookies", cookieArchive); 460 461 return true; 462 } 463 464 465 void 466 BrowserApp::_RefsReceived(BMessage* message, int32* _pagesCreated, 467 bool* _fullscreen) 468 { 469 int32 pagesCreated = 0; 470 471 BrowserWindow* window = NULL; 472 if (message->FindPointer("window", (void**)&window) != B_OK) 473 window = NULL; 474 475 bool fullscreen; 476 if (message->FindBool("fullscreen", &fullscreen) != B_OK) 477 fullscreen = false; 478 479 entry_ref ref; 480 for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) { 481 BEntry entry(&ref, true); 482 if (!entry.Exists()) 483 continue; 484 BPath path; 485 if (entry.GetPath(&path) != B_OK) 486 continue; 487 BUrl url(path); 488 window = _CreateNewPage(url.UrlString(), window, fullscreen, 489 pagesCreated == 0); 490 pagesCreated++; 491 } 492 493 BString url; 494 for (int32 i = 0; message->FindString("url", i, &url) == B_OK; i++) { 495 window = _CreateNewPage(url, window, fullscreen, pagesCreated == 0); 496 pagesCreated++; 497 } 498 499 if (_pagesCreated != NULL) 500 *_pagesCreated = pagesCreated; 501 if (_fullscreen != NULL) 502 *_fullscreen = fullscreen; 503 } 504 505 506 BrowserWindow* 507 BrowserApp::_CreateNewPage(const BString& url, BrowserWindow* webWindow, 508 bool fullscreen, bool useBlankTab) 509 { 510 // Let's first see if we must target a specific window... 511 if (webWindow && webWindow->Lock()) { 512 if (useBlankTab && webWindow->IsBlankTab()) { 513 if (url.Length() != 0) 514 webWindow->CurrentWebView()->LoadURL(url); 515 } else 516 webWindow->CreateNewTab(url, true); 517 webWindow->Activate(); 518 webWindow->CurrentWebView()->MakeFocus(true); 519 webWindow->Unlock(); 520 return webWindow; 521 } 522 523 // Otherwise, try to find one in the current workspace 524 uint32 workspace = 1 << current_workspace(); 525 526 bool loadedInWindowOnCurrentWorkspace = false; 527 for (int i = 0; BWindow* window = WindowAt(i); i++) { 528 webWindow = dynamic_cast<BrowserWindow*>(window); 529 if (!webWindow) 530 continue; 531 532 if (webWindow->Lock()) { 533 if (webWindow->Workspaces() & workspace) { 534 if (useBlankTab && webWindow->IsBlankTab()) { 535 if (url.Length() != 0) 536 webWindow->CurrentWebView()->LoadURL(url); 537 } else 538 webWindow->CreateNewTab(url, true); 539 webWindow->Activate(); 540 webWindow->CurrentWebView()->MakeFocus(true); 541 loadedInWindowOnCurrentWorkspace = true; 542 } 543 webWindow->Unlock(); 544 } 545 if (loadedInWindowOnCurrentWorkspace) 546 return webWindow; 547 } 548 549 // Finally, if no window is available, let's create one. 550 return _CreateNewWindow(url, fullscreen); 551 } 552 553 554 BrowserWindow* 555 BrowserApp::_CreateNewWindow(const BString& url, bool fullscreen) 556 { 557 // Offset the window frame unless this is the first window created in the 558 // session. 559 if (fWindowCount > 0) 560 fLastWindowFrame.OffsetBy(20, 20); 561 if (!BScreen().Frame().Contains(fLastWindowFrame)) 562 fLastWindowFrame.OffsetTo(50, 50); 563 564 BrowserWindow* window = new BrowserWindow(fLastWindowFrame, fSettings, 565 url, fContext); 566 if (fullscreen) 567 window->ToggleFullscreen(); 568 window->Show(); 569 return window; 570 } 571 572 573 void 574 BrowserApp::_CreateNewTab(BrowserWindow* window, const BString& url, 575 bool select) 576 { 577 if (!window->Lock()) 578 return; 579 window->CreateNewTab(url, select); 580 window->Unlock(); 581 } 582 583 584 void 585 BrowserApp::_ShowWindow(const BMessage* message, BWindow* window) 586 { 587 BAutolock _(window); 588 uint32 workspaces; 589 if (message->FindUInt32("workspaces", &workspaces) == B_OK) 590 window->SetWorkspaces(workspaces); 591 if (window->IsHidden()) 592 window->Show(); 593 else 594 window->Activate(); 595 } 596 597 598 // #pragma mark - 599 600 601 int 602 main(int, char**) 603 { 604 try { 605 new BrowserApp(); 606 be_app->Run(); 607 delete be_app; 608 } catch (...) { 609 debugger("Exception caught."); 610 } 611 612 return 0; 613 } 614 615